You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

434 lines
24 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from binance import BinanceSpotHttp, OrderStatus, OrderType, OrderSide
from utils import config
from utils import round_to
from utils import utility
class BinanceTrader(object):
def __init__(self):
"""
:param api_key:
:param secret:
:param trade_type: 交易的类型, only support future and spot.
"""
self.loop_counts = 0
self.config_error_counts = 0 #止损价配置不当,提示次数
self.sleep_time = config.sleep_time #循环间隔秒数
self.cancel_time = config.cancel_time #取消订单周期
self.http_client = BinanceSpotHttp(api_key=config.api_key, secret=config.api_secret, proxy_host=config.proxy_host, proxy_port=config.proxy_port)
self.buy_orders = [] # 本地买单列表.
self.sell_orders = [] # 本地卖单列表.
self.deficit_scroll_map = {} # 滚动止盈的清仓价
def get_avg_price_5min(self):
#获取5分钟内的平均价
ticker = self.http_client.get_avg_price_5min(config.symbol)
price = 0
if ticker:
price = float(ticker.get('price', 0))
return price
def get_latest_price(self):
#获取最新价格
ticker = self.http_client.get_latest_price(config.symbol)
price = 0
if ticker:
price = float(ticker.get('price', 0))
return price
def get_price_change_percent_24h(self):
#获取24h内的价格变化百分比数据
ticker = self.http_client.get_price_info_24h(config.symbol)
priceChangePercent = 0
if ticker:
priceChangePercent = float(ticker.get('priceChangePercent', 0))
return priceChangePercent
def get_bid_ask_price(self):
#最高买价 最低卖价
ticker = self.http_client.get_ticker(config.symbol)
bid_price = 0
ask_price = 0
if ticker:
bid_price = float(ticker.get('bidPrice', 0))
ask_price = float(ticker.get('askPrice', 0))
return bid_price, ask_price
def get_depth(self,limit):
ticker = self.http_client.get_order_book(config.symbol,limit=limit)
if ticker:
#high_price = float(ticker[0][2])
print(ticker)
return 123
def get_kline(self,interval):
ticker = self.http_client.get_kline(config.symbol,interval=interval,limit=1)
if ticker:
high_price = float(ticker[0][2])
low_price = float(ticker[0][3])
return low_price, high_price
# 获取倒数第二个k线数据
def get_kline_pre4(self,interval):
ticker = self.http_client.get_kline(config.symbol,interval=interval,limit=4)
if ticker:
high_price = float(ticker[3][2])
low_price = float(ticker[3][3])
return low_price, high_price
def create_buy_order(self,quantity,buy_price):
new_buy_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.BUY,
order_type=OrderType.LIMIT, quantity=quantity,
price=buy_price)
if new_buy_order:
# 买单维护到买单列表待处理
self.buy_orders.append(new_buy_order)
print(f"买单创建 价格: {buy_price}, 数量: {quantity} ")
else:
print(f"买单创建失败 价格: {buy_price}, 数量: {quantity} ")
def create_sell_order(self,quantity,sell_price):
new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL,
order_type=OrderType.LIMIT, quantity=quantity, price=sell_price)
if new_sell_order:
# 放到卖单列表待处理
self.sell_orders.append(new_sell_order)
print(f"卖单创建 价格: {sell_price}, 数量: {quantity} ")
else:
print(f"卖单创建失败 价格: {sell_price}, 数量: {quantity} ")
def create_sell_order_by_client_id(self,quantity,sell_price,client_order_id):
new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL,
order_type=OrderType.LIMIT, quantity=quantity, price=sell_price,client_order_id=client_order_id)
if new_sell_order is not None:
# 放到卖单列表待处理
self.sell_orders.append(new_sell_order)
print(f"卖单创建 价格: {sell_price}, 数量: {quantity} ")
return True
else:
print(f"卖单创建失败 价格: {sell_price}, 数量: {quantity} ")
return False
def create_sell_order_market(self,quantity):
new_sell_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.SELL,
order_type=OrderType.MARKET, quantity=quantity)
if new_sell_order:
# 市价订单必定成功
self.sell_orders.append(new_sell_order)
print(f"交易对:{config.symbol} 市价卖出数量: {quantity} ")
return True
else:
print(f"交易对:{config.symbol} 卖单创建失败 市价卖出数量: {quantity} ")
return False
def cancel_single_order(self,cancel_order):
order = self.http_client.cancel_order(config.symbol,cancel_order.get('clientOrderId'))
if order is None:
utility.alert(f"订单撤销失败 symbol: {config.symbol}, clientOrderId: {cancel_order.get('clientOrderId')} ")
return False
else:
print(f"订单撤销成功 symbol: {config.symbol}, clientOrderId: {cancel_order.get('clientOrderId')} ")
return True
def cancel_open_orders(self):
open_orders_list = self.http_client.get_open_orders(config.symbol)
for open_order in open_orders_list:
if open_order.get('status') == OrderStatus.NEW.value and open_order.get('side') == OrderSide.BUY.value:
#只取消本客户端创建的买单
if open_order.get('clientOrderId') and config.symbol in open_order.get('clientOrderId'):
self.cancel_single_order(open_order)
print(f"买单{open_order.get('clientOrderId')}已取消取消")
def sync_open_orders(self):
open_orders_list = self.http_client.get_open_orders(config.symbol)
for open_order in open_orders_list:
#只同步本客户端创建的订单
if open_order.get('clientOrderId') and config.symbol in open_order.get('clientOrderId'):
if open_order.get('status') == OrderStatus.NEW.value and open_order.get('side') == OrderSide.BUY.value:
self.buy_orders.append(open_order)
print(f"买单{open_order.get('clientOrderId')}同步到本地")
if open_order.get('status') == OrderStatus.NEW.value and open_order.get('side') == OrderSide.SELL.value:
self.sell_orders.append(open_order)
print(f"卖单{open_order.get('clientOrderId')}同步到本地")
#网格交易核心逻辑.
def grid_trader(self):
"""
#网格交易核心逻辑.
"""
quantity = round_to(float(config.quantity), float(config.min_qty)) ## quantity是config.quantity到config.min_qty的精度转换
profit_scroll = float(config.profit_scroll) ## 每次刷新止盈价的比例
deficit_scroll = float(config.deficit_scroll) ## 每次刷新止损价的比例
profit_intend = float(config.profit_intend) ## 每次准备刷新止盈价的比例,到了这个比例直接刷
gap_percent= float(config.gap_percent) ## 卖单提价比例
self.loop_counts+=1
print(f"{utility.cn_time()}{self.loop_counts}轮循环开始")
bid_price, ask_price = self.get_bid_ask_price()
print(f"{config.symbol}:最高买价bid_price: {bid_price}, 最低卖价ask_price: {ask_price}")
avg_price_5min = float(self.get_avg_price_5min())
#latest_price = self.get_latest_price()
#5分钟k线 15分钟k线
low_price_in_15m, high_price_in_15m = self.get_kline("15m")
print(f"{config.symbol}:15分钟最低价: {low_price_in_15m}, 15分钟最高价: {high_price_in_15m}")
low_price_in_4h, high_price_in_4h = self.get_kline("4h")
low_price_pre_in_4h, high_price_pre_in_4h = self.get_kline_pre4("2h")
print(f"low_price_in_4h: {low_price_in_4h}")
print(f"high_price_in_4h: {high_price_in_4h}")
print(f"low_price_pre_in_4h: {low_price_pre_in_4h}")
print(f"high_price_pre_in_4h: {high_price_pre_in_4h}")
# 取波峰波谷的中间值
peak_price = high_price_in_4h if high_price_in_4h <= high_price_pre_in_4h else high_price_pre_in_4h
bottom_price = low_price_in_4h if low_price_in_4h >= low_price_pre_in_4h else low_price_pre_in_4h
delta_price_in_4h = float(peak_price)-float(bottom_price)
price_stop_to_sell = float(bottom_price) + delta_price_in_4h * 0.1
price_stop_to_buy = float(peak_price) - delta_price_in_4h * 0.8
print(f"high_price_in_4h_4h: {peak_price}")
print(f"low_price_in_4h_4h: {bottom_price}")
print(f"价格上限: {price_stop_to_buy}")
print(f"价格下限: {price_stop_to_sell}")
# price_stop_to_sell = float(config.price_stop_to_sell)
# price_stop_to_buy = float(config.price_stop_to_buy)
self.buy_orders.sort(key=lambda x: float(x['price']), reverse=True) # 最高价到最低价.
self.sell_orders.sort(key=lambda x: float(x['price']), reverse=True) # 最高价到最低价.
buy_delete_orders = [] # 需要删除的买单
sell_delete_orders = [] # 需要删除的卖单
# 买单逻辑,检查买单列表成交的情况.一旦成功立即加价卖
print("-------------买单逻辑-----------------")
for buy_order in self.buy_orders:
check_order = self.http_client.get_order(buy_order.get('symbol', config.symbol),client_order_id=buy_order.get('clientOrderId'))
if check_order:
order_status = check_order.get('status')
order_price = check_order.get('price')
order_orig_qty = check_order.get('origQty')
order_client_id = buy_order.get('clientOrderId')
#取消状态的买单 不管
if order_status == OrderStatus.CANCELED.value:
buy_delete_orders.append(buy_order)
print(f"买单{order_client_id}状态为取消: {order_status}")
#成交状态买单,需要处理
elif order_status == OrderStatus.FILLED.value:
print(f"成交状态的买单: {order_client_id} 成交时间: {utility.cn_time()} 价格: {order_price} 数量: {order_orig_qty}")
# 买单成交后,提高价格,挂盈利卖单
sell_price = round_to(float(order_price) * (1 + float(config.gap_percent)), float(config.min_price))
if 0 < sell_price < ask_price:
# 防止价格小于最低卖价ask_price
sell_price = round_to(ask_price, float(config.min_price))
#如果卖价小于设定的持仓价格price_stop_to_sell将卖价提高至持仓价才交易
if 0 < sell_price < price_stop_to_sell:
print(f"卖价{sell_price} 小于持仓价{price_stop_to_sell} ,已调整为持仓价交易")
sell_price = price_stop_to_sell
#盈利卖单挂
if self.create_sell_order(quantity, sell_price):
print(f"{utility.cn_time()} :交易对:{config.symbol}, 买单 {order_client_id} 成交, 价格: {order_price}, 数量: {order_orig_qty}")
# 盈利卖单挂成功的话,删掉本买单
buy_delete_orders.append(buy_order)
if buy_order in self.buy_orders:
self.buy_orders.remove(buy_order)
#未成交的买单不处理
elif order_status == OrderStatus.NEW.value:
print(f"未成交的买单{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
# 部分成交的买单 提示下就行了
elif order_status == OrderStatus.PARTIALLY_FILLED.value:
print(f"部分成交的买单{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
#异常状态买单
else:
utility.alert(f"交易对:{config.symbol}, 时间: {utility.cn_time()}, 买单{order_client_id}失败{order_status}")
# 过期或者拒绝的订单删除掉.
for delete_order in buy_delete_orders:
if delete_order in self.buy_orders:
self.buy_orders.remove(delete_order)
# 卖单逻辑, 检查卖单成交情况
print("--------------卖单逻辑----------------")
for sell_order in self.sell_orders:
check_order = self.http_client.get_order(sell_order.get('symbol', config.symbol),client_order_id=sell_order.get('clientOrderId'))
if check_order:
order_status = check_order.get('status')
order_price = check_order.get('price')
order_orig_qty = check_order.get('origQty')
order_client_id = sell_order.get('clientOrderId')
#卖单状态为取消 不管
if order_status == OrderStatus.CANCELED.value:
sell_delete_orders.append(sell_order)
print(f"卖单{order_client_id}状态为取消: {order_status}")
#卖单状态为成交
elif order_status == OrderStatus.FILLED.value:
print(f"卖单{order_client_id}成交时间: {utility.cn_time()}, 价格: {order_price}, 数量: {order_orig_qty}")
#第一遍才发通知
if sell_order not in sell_delete_orders:
utility.alert(f"卖单成交: {utility.cn_time()} {order_client_id}, 交易对:{config.symbol}, 价格: {order_price}, 数量: {order_orig_qty}")
# 卖单成交,不再维护该卖单状态
sell_delete_orders.append(sell_order)
if sell_order in self.sell_orders:
self.sell_orders.remove(sell_order)
# 新挂的卖单 创建移动止盈策略
elif order_status == OrderStatus.NEW.value:
print(f"还没成交的卖单:{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
print(f"当前最低卖价: {ask_price}")
orig_buy_price = float(order_price) / (float(gap_percent) + 1)
print(f"预估本单的买入价格: {orig_buy_price}")
triger_price = float(order_price) * float(profit_intend)
print(f"移动止盈触发价格: {triger_price}")
if ask_price >= triger_price:
print("最低卖价大于移动止盈触发价格,先删除本卖单,再创建一个更高价的卖单")
if self.cancel_single_order(check_order):
# 删除成功 再创建一个更高价的卖单
new_order_price_profit = float(order_price) * float(profit_scroll)
new_order_price_deficit = float(ask_price) * float(deficit_scroll)
new_order_price_profit = round_to(new_order_price_profit, float(config.min_price))
new_order_price_deficit = round_to(new_order_price_deficit, float(config.min_price))
print(f"new_order_price_profit: {new_order_price_profit}")
print(f"new_order_price_deficit: {new_order_price_deficit}")
profit_client_order_id = self.http_client.get_client_order_id()
profit_sell_order = self.create_sell_order_by_client_id(order_orig_qty,
new_order_price_profit,
profit_client_order_id)
if profit_sell_order:
print("----------------移动止盈触发成功-------yyyyyyyyyyyyyyyyyyyyyyyyyyyy------")
print(
f"{utility.cn_time()} :卖单 {order_client_id}滚动止盈触发新卖单{profit_client_order_id},交易对:{config.symbol},新期望价格: {new_order_price_profit}, 数量: {order_orig_qty}")
# 移动止盈卖单挂成功的话,本地不再维护原卖单
sell_delete_orders.append(sell_order)
if sell_order in self.sell_orders:
self.sell_orders.remove(sell_order)
# 本地记录新卖单的止损价格
self.deficit_scroll_map[profit_client_order_id] = new_order_price_deficit
else:
print("----------------移动止盈触发失败------eeeeeeeeeeeeeeeeeeeeeeeeeeee-------")
print(
f"{utility.cn_time()} 移动止盈卖单创建失败 交易对:{config.symbol} 价格: {new_order_price_profit}, 数量: {order_orig_qty} ")
utility.alert(
f"{utility.cn_time()} 移动止盈卖单创建失败 交易对:{config.symbol} 价格: {new_order_price_profit}, 数量: {order_orig_qty} ")
else:
print("原买单删除失败")
# 当前卖单存在止损价,且最低卖价不大于止损价时候,止损处理掉当前卖单
# 其实所存的止损价是经过滚动止盈产生的,所以还是在盈利
# 当前卖价要大于买单价格才可以
elif order_client_id in self.deficit_scroll_map and ask_price <= self.deficit_scroll_map[order_client_id] and ask_price > orig_buy_price:
# 先删除本卖单,挂太高了,卖不出去
if self.cancel_open_orders(check_order):
# 市价卖出本订单待卖的币
self.create_sell_order_market(order_orig_qty)
print("----------移动止损成功--------------")
else:
print(f"止损卖单取消失败:{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
# 部分成交的卖单 提示下就行了
elif order_status == OrderStatus.PARTIALLY_FILLED.value:
print(f"部分成交的卖单{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
else:
utility.alert(f"挂卖单失败 交易对:{config.symbol}, 时间: {utility.cn_time()}, {order_status}")
#处理过期或者拒绝的订单,不在维护到本地
for delete_order in sell_delete_orders:
if delete_order in self.sell_orders:
self.sell_orders.remove(delete_order)
# 买单数量未满 并且卖单也没满时候 usdt没有积压 可以新建买单
if len(self.buy_orders) < int(config.max_orders) and len(self.sell_orders) < int(config.max_sell_orders):
print("买单数量未满,卖单也没积压,开始创建一个新买单:")
print("------------------------------")
if bid_price > 0:
buy_price = round_to(bid_price * (1 - float(config.gap_percent)), float(config.min_price))
##如果买价大于设定的抛仓价格price_stop_to_buy将买价降低至抛仓价才交易
if buy_price > price_stop_to_buy > 0:
buy_price = price_stop_to_buy
#动态价格保护如果买价不高于15分钟k线最高值说明价格在跌适合买
if buy_price <= high_price_in_15m :
# 如果价格不大于5分钟内的平均价 说明价格还可以,能继续买入
if buy_price <= avg_price_5min:
buy_price = round_to(buy_price,float(config.min_price))
print(f"开始创建买单:价格{buy_price} 数量 {quantity} ")
self.create_buy_order(quantity,buy_price)
else:
print(f"买价 {buy_price}大于平均价 {avg_price_5min} 再等等")
else:
print(f"买价 {buy_price}大于15分钟最高价 {high_price_in_15m} 高位勿买")
# 买单数量过多cancel_time 取消一次价格最离谱的买单
elif len(self.buy_orders) >= int(config.max_orders):
# cancel_time更新一次 主循环cancel_time/gcd_time次
cancel_period=self.cancel_time/self.sleep_time
print(f"当前买单数量达到{config.max_orders},将在{(cancel_period - self.loop_counts % cancel_period)*self.sleep_time}秒后删除一个价格最离谱的买单")
if self.loop_counts % cancel_period == 0:
print(f"开始删除价格最离谱的买单:")
print("------------------------------")
# 最离谱到最接近
self.buy_orders.sort(key=lambda x: abs(float(x['price'])-float(avg_price_5min)), reverse=True)
impossible_order=self.buy_orders[0]
if self.cancel_single_order(impossible_order):
if impossible_order in self.buy_orders:
self.buy_orders.remove(impossible_order)
buy_delete_orders.append(impossible_order)
print(f"买单列表已满 价格离谱的买单{impossible_order.get('clientOrderId')}删除 价格: {impossible_order.get('price')}, 数量: {impossible_order.get('origQty')} ")
else:
print(f"买单列表已满 价格离谱的买单{impossible_order.get('clientOrderId')}删除失败 价格: {impossible_order.get('price')}, 数量: {impossible_order.get('origQty')} ")
for fade_order in self.buy_orders:
if float(fade_order.get('price')) > high_price_in_15m or float(fade_order.get('price')) < low_price_in_15m:
if fade_order in self.buy_orders:
self.buy_orders.remove(fade_order)
buy_delete_orders.append(fade_order)
print(f"取消价格未在15m k线的买单{fade_order.get('clientOrderId')} 价格: {impossible_order.get('price')}, 数量: {impossible_order.get('origQty')} ")
# 没有卖单的时候 暂时先不创建卖单,防止利润计算错误
if len(self.sell_orders) <= 0:
print(f"当前没有卖单,请注意查看{config.symbol}余额")
print("------------------------------")
#卖单过多,暂不处理
elif len(self.sell_orders) > int(config.max_orders):
print("卖单过多:")
print("------------------------------")
print(f"交易对{config.symbol}卖单较多,大于个{config.max_orders} 请处理")
#集中处理 buy_delete_orders sell_delete_orders
for buy_order in buy_delete_orders:
if self.cancel_single_order(buy_order):
if buy_order in buy_delete_orders:
buy_delete_orders.remove(buy_order)
for sell_order in sell_delete_orders:
if self.cancel_single_order(sell_order):
if sell_order in sell_delete_orders:
sell_delete_orders.remove(sell_order)
print(f"{utility.cn_time()}{self.loop_counts}轮循环结束 休息{self.sleep_time}s")