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.

404 lines
22 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.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 = [] # 本地卖单列表.
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_15m(self):
ticker = self.http_client.get_kline(config.symbol,interval="15m",limit=1)
if ticker:
high_price = float(ticker[0][2])
low_price = float(ticker[0][3])
return low_price, high_price
def get_kline_5m(self):
ticker = self.http_client.get_kline(config.symbol,interval="5m",limit=1)
if ticker:
high_price = float(ticker[0][2])
low_price = float(ticker[0][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_buy_order_by_client_id(self,quantity,buy_price,client_order_id):
new_buy_order = self.http_client.place_order(symbol=config.symbol, order_side=OrderSide.BUY,
order_type=OrderType.LIMIT, quantity=quantity,
price=buy_price,client_order_id=client_order_id)
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:
# 放到卖单列表待处理
self.sell_orders.append(new_sell_order)
print(f"卖单创建 价格: {sell_price}, 数量: {quantity} ")
else:
print(f"卖单创建失败 价格: {sell_price}, 数量: {quantity} ")
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} ")
else:
print(f"交易对:{config.symbol} 卖单创建失败 市价卖出数量: {quantity} ")
def cancel_single_order(self,cancel_order):
order = self.http_client.cancel_order(config.symbol,cancel_order.get('clientOrderId'))
if order:
print(f"订单撤销 symbol: {config.symbol}, clientOrderId: {cancel_order.get('clientOrderId')} ")
else:
print(f"订单撤销失败 symbol: {config.symbol}, clientOrderId: {cancel_order.get('clientOrderId')} ")
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):
"""
#网格交易核心逻辑.
"""
price_stop_to_sell = float(config.price_stop_to_sell)
price_stop_to_buy = float(config.price_stop_to_buy)
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) ## 每次准备刷新止盈价的比例,到了这个比例直接刷
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_5m, high_price_in_5m = self.get_kline_5m()
# print(f"{config.symbol}:5分钟最低价: {low_price_in_5m}, 5分钟最高价: {high_price_in_5m}")
#循环提醒配置不当
if avg_price_5min > price_stop_to_buy or avg_price_5min < price_stop_to_sell:
print(f"交易对{config.symbol}:止损范围配置不当,恐难以成交,五分钟内平均价: {avg_price_5min}")
self.config_error_counts+=1
#15次循环提醒一次配置不当
if self.config_error_counts%15 == 0:
self.config_error_counts = 0
utility.alert(f"交易对{config.symbol}:止损范围配置不当,恐难以成交,五分钟内平均价: {avg_price_5min}")
else:
self.config_error_counts = 0
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 = [] # 需要删除的卖单
deficit_scroll_map = {} # 滚动止盈的清仓价
buy_order_price_map = {} # 买单的价格
buy_order_sell_map = {} # 买卖单对应关系
# 买单逻辑,检查买单列表成交的情况.一旦成功立即加价卖
# print("当前委托买单:")
# print(self.buy_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
#盈利卖单挂
profit_client_order_id = BinanceSpotHttp.get_client_order_id()
if self.create_sell_order_by_client_id(quantity, sell_price,profit_client_order_id):
print(f"{utility.cn_time()} :交易对:{config.symbol}, 买单 {order_client_id} 成交, 价格: {order_price}, 数量: {order_orig_qty}")
# 盈利卖单挂成功的话,删掉本买单
buy_delete_orders.append(buy_order)
self.buy_orders.remove(buy_order)
# 维护买卖单关系
buy_order_sell_map[order_client_id] = profit_client_order_id
#未成交的买单不处理
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:
self.buy_orders.remove(delete_order)
# 卖单逻辑, 检查卖单成交情况.不做措施
# print("当前委托卖单:")
# print(self.sell_orders)
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)
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}")
#预估
# 最低卖价大于原卖单定价的止盈比例时候,先删除本卖单,再创建一个更高价的卖单
if ask_price >= order_price * profit_intend:
# 先删除本卖单
if self.cancel_open_orders(check_order):
# 再创建一个更高价的卖单
new_order_price_profit = order_price * profit_scroll
new_order_price_deficit = ask_price * deficit_scroll
profit_client_order_id = BinanceSpotHttp.get_client_order_id()
profit_sell_order = self.self.create_sell_order_by_client_id(order_orig_qty,new_order_price_profit,profit_client_order_id)
if profit_sell_order:
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)
self.sell_orders.remove(sell_order)
# 本地记录新卖单的止损价格
deficit_scroll_map[profit_client_order_id] = new_order_price_deficit
else:
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(f"止盈卖单取消失败:{order_client_id} 价格: {order_price}, 数量: {order_orig_qty}")
# 当前卖单存在止损价,且最低卖价不大于止损价时候,止损处理掉当前卖单
# 其实所存的止损价是经过滚动止盈产生的,所以还是在盈利
elif deficit_scroll_map.has_key(order_client_id) and ask_price <= deficit_scroll_map[order_client_id]:
# 先删除本卖单,挂太高了,卖不出去
if self.cancel_open_orders(check_order):
# 市价卖出本订单待卖的币
self.create_sell_order_market(order_orig_qty)
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:
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:
print(f"开始创建买单:价格{buy_price} 数量 {quantity} ")
buy_client_order_id = BinanceSpotHttp.get_client_order_id()
if self.create_buy_order_by_client_id(quantity,buy_price,buy_client_order_id):
# 买入成功后记录买单的原价
buy_order_price_map[buy_client_order_id] = buy_price
else:
print(f"买单{buy_client_order_id}创建失败 数量 {quantity} 价格{buy_price}")
else:
print(f"买价 {buy_price}大于平均价 {avg_price_5min} 再等等")
else:
print(f"买价 {buy_price}大于15分钟最高价 {high_price_in_15m} 高位勿买")
# 买单数量过多2min取消一次价格最离谱的买单
elif len(self.buy_orders) >= int(config.max_orders):
# 主循环6次 2min更新一次
print(f"当前买单数量达到{config.max_orders},将在{(6 - self.loop_counts % 6)*20}秒后删除一个价格最离谱的买单")
if self.loop_counts % 6 == 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):
self.buy_orders.remove(impossible_order)
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:
self.buy_orders.remove(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} 请处理")
print(f"{utility.cn_time()} 第{self.loop_counts}轮循环结束 休息20s")