1 Star 0 Fork 3

AndyWang/Bitcoin arbitrager

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
arbitrager_haobtc.py 38.96 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
# 既有Haobtc的maker、taker赚手续费功能,也有普通的差价套利功能
import markets
import config
import math
import random
import time
import sys
import smtplib
from concurrent.futures import ThreadPoolExecutor
from operator import attrgetter
from lib.bmobHelper import BmobHelper
exchanges = []
init_btc = 0.0 # 程序运行前,比特币数量,在程序运行过程中,数量一般保持不变
total_cny = 0.0 # 当前的人民币数量和比特币数量
total_btc = 0.0
last_cny = 0.0 # 每轮操作前,人民币数量,每轮操作后都可能会变
last_btc = 0.0 # 上一轮操作结束后的比特币数量
def init_exchanges():
exchange_names = config.exchanges
global exchanges # 哪里需要使用全局变量,哪里就声明一下
for exchange_name in exchange_names:
try:
exec('import markets.' + exchange_name.lower())
exchange = eval('markets.' + exchange_name.lower() + '.' + exchange_name + '()')
exchanges.append(exchange)
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
print("%s: Init exchange %s." % (datetime, exchange_name))
except Exception as e:
print("%s exchange name is invalid, please check config.py" % exchange_name)
print(e)
# decimal表示浮点数值,number表示保留小数位数;number只能为1,2,3或者4
def retainDigiNum(decimal, number): # 保留N位小数,不四舍五入;最好别用round函数,有精度误差的问题,会产生特别长的浮点数
if number == 1:
return int(decimal * 10) / 10
if number == 2:
return int(decimal * 100) / 100
if number == 3:
return int(decimal * 1000) / 1000
if number == 4:
return int(decimal * 10000) / 10000
if number == 5:
return int(decimal * 100000) / 100000
if number == 6:
return int(decimal * 1000000) / 1000000
def cancelAll(): # 不撤销好比特的订单
global exchanges
for exchange in exchanges:
if exchange.__class__.__name__ == 'Haobtc':
continue
exchange.cancel_all()
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
print("%s: Cancel all orders except Haobtc!" % datetime)
def updateCurrentDepth(exclude=[]): # 参数为当前操作不更新的市场列表
global exchanges
try:
threadpool = ThreadPoolExecutor(max_workers=10)
for exchange in exchanges:
if exchange.__class__.__name__ in exclude:
continue
threadpool.submit(exchange.update_depth)
threadpool.shutdown(wait=True)
except Exception as e:
# 若深度更新失败,将所有交易所深度信息置为0
for exchange in exchanges:
exchange.bid = 0.0
exchange.bid_volume = 0.0
exchange.ask = 0.0
exchange.ask_volume = 0.0
print('UpdateCurrentDepth Failed!' + str(e))
def updateCurrentAsset(exclude=[]):
global exchanges
try:
threadpool = ThreadPoolExecutor(max_workers=10)
for exchange in exchanges:
if exchange.__class__.__name__ in exclude:
continue
threadpool.submit(exchange.update_accountInfo)
threadpool.shutdown(wait=True)
except Exception as e:
# 若资产更新失败,将所有交易所的资产信息都置为0
for exchange in exchanges:
exchange.btc_free = 0.0
exchange.btc_frozen = 0.0
exchange.cny_free = 0.0
exchange.cny_frozen = 0.0
print('UpdateCurrentAsset Failed!' + str(e))
global total_cny, total_btc
total_btc = 0.0
total_cny = 0.0
for exchange in exchanges:
total_cny += exchange.cny_free
total_cny += exchange.cny_frozen
total_btc += exchange.btc_free
total_btc += exchange.btc_frozen
def getProfit(): # 计算当前总资产更上一周期的差值,即利润
global total_cny, last_cny, total_btc, last_btc
bid = 0
for exchange in exchanges:
if (exchange.ask == 0) or (exchange.bid == 0):
continue
bid = exchange.bid
break
updateCurrentAsset()
if isNomal() and (bid != 0): # 交易所资产正常并且参考价格不为零,才做利润统计
return (total_cny - last_cny) + (total_btc - last_btc) * bid
else:
return 0
def balanceBtc(exclude=[]): # 平衡一轮操作前后的BTC数量,保持BTC量前后一致
global total_btc, total_cny, init_btc, last_btc, last_cny, exchanges
updateCurrentAsset(exclude)
# 获取当前比特币价格
bid = 0
for exchange in exchanges:
if (exchange.ask == 0) or (exchange.bid == 0):
continue
bid = exchange.bid
break
if isNomal() and (abs((total_btc-last_btc)*bid+total_cny-last_cny) < init_btc * 20 * 2):
# 交易所资产更新都成功即为正常
# 并且当前与上一轮的总资产差值的绝对值小于阈值,此举是为了避免好比特的资产api更新异常而设
diff = retainDigiNum(total_btc - init_btc, 4)
temp = abs(diff)
updateCurrentDepth()
if diff <= -0.001: # 假如不平衡量小于0.001,交易所都不能下单,故不处理
record = BmobHelper()
balance_exchanges = sorted(exchanges, key=attrgetter('ask'))
for exchange in balance_exchanges:
if exchange.__class__.__name__ == 'Haobtc': # 不在Haobtc主动下单
continue
if exchange.ask == 0:
continue
if exchange.__class__.__name__ == 'Okcoin':
if temp < config.MinAmountOnce:
continue # 如果需要平衡的量小于0.01,并且当前交易所为Okcoin,跳过不做
else:
temp = retainDigiNum(temp, 3) # 如果小数位数大于3位,只保留3位
if exchange.cny_free >= temp * (exchange.ask + config.SlidePrice):
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
if exchange.buy(exchange.ask + config.SlidePrice, temp):
record.record_op(datetime, "enbalanceBTC", exchange.__class__.__name__, None,
exchange.ask + config.SlidePrice, temp, None)
temp = 0
if (exchange.cny_free < temp * (exchange.ask + config.SlidePrice)) and \
(retainDigiNum(exchange.cny_free / (exchange.ask + config.SlidePrice), 3) >= 0.01):
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
if exchange.buy(exchange.ask + config.SlidePrice,
retainDigiNum(exchange.cny_free / (exchange.ask + config.SlidePrice), 3)):
record.record_op(datetime, "enbalanceBTC", exchange.__class__.__name__, None,
exchange.ask + config.SlidePrice,
retainDigiNum(exchange.cny_free / (exchange.ask + config.SlidePrice), 3), None)
temp -= retainDigiNum(exchange.cny_free / (exchange.ask + config.SlidePrice), 3)
if temp < 0.01:
break
if temp >= 0.001:
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
print("%s: Exchanges(except Haobtc) has not enough RMB to balance. Should buy %f btc."
% (datetime, temp))
if 0.001 <= diff:
record = BmobHelper()
balance_exchanges = sorted(exchanges, key=attrgetter('bid'), reverse=True)
for exchange in balance_exchanges:
if exchange.__class__.__name__ == 'Haobtc':
continue
if exchange.bid == 0:
continue
if exchange.__class__.__name__ == 'Okcoin':
if temp < config.MinAmountOnce:
continue # 如果需要平衡的量小于0.01,并且当前交易所为Okcoin,跳过不做
else:
temp = retainDigiNum(temp, 3) # 如果小数位数大于3位,只保留3位
if exchange.btc_free >= temp:
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
if exchange.sell(exchange.bid - config.SlidePrice, temp):
record.record_op(datetime, "enbalanceBTC", None, exchange.__class__.__name__,
exchange.bid - config.SlidePrice, temp, None)
temp = 0
if (exchange.btc_free < temp) and (exchange.btc_free >= 0.01):
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
if exchange.sell(exchange.bid - config.SlidePrice, retainDigiNum(exchange.btc_free, 3)):
record.record_op(datetime, "enbalanceBTC", None, exchange.__class__.__name__,
exchange.bid - config.SlidePrice, retainDigiNum(exchange.btc_free, 3), None)
temp -= retainDigiNum(exchange.btc_free, 3)
if temp < 0.01:
break
if temp >= 0.001:
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
print("%s: Exchanges has not enough BTC to balance. Should sell %f btc."
% (datetime, temp))
else:
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
print("%s: Exchanges asset fail to update." % datetime)
def isNomal(): # 判断交易所资产更新是否正常
flag = True
for exchange in exchanges:
if (exchange.cny_free == 0) and (exchange.cny_frozen == 0) and (exchange.btc_free == 0) and \
(exchange.btc_frozen == 0):
flag = False
break
return flag
def isDepthNomal(): # 判断除好比特外的交易所深度是否正常
flag = True
for exchange in exchanges:
if exchange.__class__.__name__ == 'Haobtc':
continue
if (exchange.ask == 0) or (exchange.bid == 0):
flag = False
break
return flag
def handleHaobtc(): # 处理Haobtc的成交情况
global exchanges
# 作为参照时,ask, bid都不能为0;如果为零,跳过
ask_min = 0
ask_max = 0
bid_min = 0
bid_max = 0
HaobtcPendingBidMaxVolume = 0
HaobtcPendingAskMaxVolume = 0
print("----------Haobtc----------")
balanceBtc()
updateCurrentAsset(["Haobtc"]) # 因为只是获取市场参考价格和深度,所以不更新Haobtc资产和深度
updateCurrentDepth(["Haobtc"])
if isDepthNomal(): # 除好比特以外的市场深度正常时,才继续下去;否则,不对好比特套利进行操作
# 其他交易所的ask价即为平衡时的买入价,所以按ask价从低到高排列
# 依次遍历,如果交易所的可用人民币大于1000元,则为ask最优价;取最后一个为次优价;若没有最优价,次优价也为最优价
IsGetOptimal = False # 是否得到最优标志
for exchange in sorted(exchanges, key=attrgetter('ask'), reverse=False):
if exchange.__class__.__name__ == 'Haobtc':
continue
if (exchange.ask == 0) or (exchange.bid == 0):
continue
if (exchange.cny_free > config.MinAmountOnce * exchange.ask) and (not IsGetOptimal):
ask_min = exchange.ask
HaobtcPendingAskMaxVolume = retainDigiNum(exchange.cny_free / exchange.ask, 4)
IsGetOptimal = True
ask_max = exchange.ask
if not IsGetOptimal: # 如果没有取得最优价,则使用次优价,这里指的是较高的ask
ask_min = ask_max
# 其他交易所的bid价即为平衡时的卖出价,所以按bid价从高到低排列
# 依次遍历,如果交易所的可用比特币资产大于1000元,则为bid最优价;取最后一个为次优价;若没有最优价,次优价也为最优价
IsGetOptimal = False
for exchange in sorted(exchanges, key=attrgetter('bid'), reverse=True):
if exchange.__class__.__name__ == 'Haobtc':
continue
if (exchange.ask == 0) or (exchange.bid == 0):
continue
if (exchange.btc_free > config.MinAmountOnce) and (not IsGetOptimal):
bid_max = exchange.bid
HaobtcPendingBidMaxVolume = retainDigiNum(exchange.btc_free, 4)
IsGetOptimal = True
bid_min = exchange.bid
if not IsGetOptimal: # 如果没有取得最优价,则使用次优价,这里指的是较低的bid
bid_max = bid_min
amountHaobtcBidPending = 0 # 统计Haobtc买单上的比特币累积数量
amountHaobtcAskPending = 0 # 统计Haobtc卖单上的比特币累积数量
for exchange in exchanges:
if exchange.__class__.__name__ == 'Haobtc':
exchange.update_accountInfo()
exchange.update_depth()
# 撤销不在下单范围的订单;撤销可能产生亏损的单;撤销在下单范围内的,但不是最优情况的订单
orders = exchange.get_orders()
if orders:
for order in orders:
if order['status'] != 'All deal':
if order['type'] == 'BUY':
if (bid_max != 0) and \
(float(order['price']) * (1 - config.makerRate * config.ORDERWARNINGRATE) >= bid_max):
# 买单没有利润,撤单
exchange.cancel_order(order['order_id'])
continue
if float(order['price']) < exchange.bid - config.OrderRetainScope + 1:
# 买单不在挂单范围内,撤单
exchange.cancel_order(order['order_id'])
continue
if float(order['price']) >= exchange.bid - config.OrderRetainScope + 1:
# 买单在保留范围内,但非最优
if float(order['price']) <= (exchange.ask-1)-config.OrderValidScope: # 在有效范围外面
# 临界值使用次优价来计算,减少撤单概率
if float(order['price']) <= (exchange.ask - 1) - config.OrderValidScope \
< int(bid_min / (1 - config.makerRate * config.ORDERWARNINGRATE)):
# 如果订单价格 <= 当前买一价 - 有效范围 < 临界值,撤单
exchange.cancel_order(order['order_id'])
continue
elif float(order['price']) < int(
bid_min / (1 - config.makerRate * config.ORDERWARNINGRATE)) \
<= (exchange.ask - 1) - config.OrderValidScope:
# 如果订单价格 < 临界值向下取整 <= 当前买一价 - 有效范围,撤单
exchange.cancel_order(order['order_id'])
continue
else: # 在有效范围里面
# 使用模拟退火算法来算当前撤单的概率
if float(order['price']) \
< (exchange.ask-1) \
< int(bid_min / (1 - config.makerRate * config.ORDERWARNINGRATE)):
if (1 - math.exp((float(order['price']) - (exchange.ask-1)
) / config.OrderValidScope / 2)) > random.random():
exchange.cancel_order(order['order_id'])
continue
if float(float(order['price'])) \
< int(bid_min / (1 - config.makerRate * config.ORDERWARNINGRATE)) \
<= (exchange.ask-1):
if (1 - math.exp((float(order['price']) - int(
bid_min / (1 - config.makerRate * config.ORDERWARNINGRATE))
) / config.OrderValidScope / 2)) > random.random():
exchange.cancel_order(order['order_id'])
continue
# 统计好比特当前已下买单的比特币数量。PS:只统计订单成交时的成本价在两个交易所之间的情况,
# 即Ok和火币差价大的情况
if bid_min < float(order['price']) * (1 - config.makerRate * config.ORDERWARNINGRATE) < bid_max:
amountHaobtcBidPending += float(order['remainsize'])
if amountHaobtcBidPending > HaobtcPendingBidMaxVolume:
exchange.cancel_order(order['order_id'])
amountHaobtcBidPending -= float(order['remainsize'])
continue
elif order['type'] == 'SELL':
if (ask_min != 0) and \
(float(order['price']) * (1 + config.makerRate * config.ORDERWARNINGRATE) <= ask_min):
# 卖单没有利润,撤单
exchange.cancel_order(order['order_id'])
continue
if float(order['price']) > exchange.ask + config.OrderRetainScope - 1:
# 卖单不在挂单范围内,撤单
exchange.cancel_order(order['order_id'])
continue
if float(order['price']) <= exchange.ask + config.OrderRetainScope - 1: # 卖单在保留范围内,但非最优
if float(order['price']) >= (exchange.bid+1)+config.OrderValidScope: # 在有效范围外面
# 临界值用次优价来算,减少撤单概率
if math.ceil(ask_max / (1 + config.makerRate * config.ORDERWARNINGRATE)) \
< (exchange.bid + 1) + config.OrderValidScope <= float(order['price']):
# 临界值 < 当前卖一价+有效范围 <= 订单价格,撤单
exchange.cancel_order(order['order_id'])
continue
elif (exchange.bid + 1) + config.OrderValidScope \
<= math.ceil(ask_max / (1 + config.makerRate * config.ORDERWARNINGRATE)) \
< float(order['price']):
# 当前卖一价+有效范围 <= 临界值向上取整 < 订单价格,撤单
exchange.cancel_order(order['order_id'])
continue
else: # 在有效范围里面
# 使用模拟退火算法来算当前撤单的概率
if math.ceil(ask_max / (1 + config.makerRate * config.ORDERWARNINGRATE)) \
< exchange.bid+1 \
< float(order['price']):
if (1-math.exp((exchange.bid+1-float(
order['price']))/config.OrderValidScope/2)) > random.random():
exchange.cancel_order(order['order_id'])
continue
if exchange.bid+1 \
<= math.ceil(ask_max / (1 + config.makerRate * config.ORDERWARNINGRATE)) \
< float(order['price']):
if (1-math.exp((math.ceil(ask_max/(
1+config.makerRate*config.ORDERWARNINGRATE)) - float(order['price'])
)/config.OrderValidScope/2)) > random.random():
exchange.cancel_order(order['order_id'])
continue
# 统计好比特当前已下卖单的比特币数量。PS:只统计订单成交时的成本价在两个交易所之间的情况,
# 即Ok和火币差价大的情况
if ask_min < float(order['price']) * (1 + config.makerRate * config.ORDERWARNINGRATE) < ask_max:
amountHaobtcAskPending += float(order['remainsize'])
if amountHaobtcAskPending > HaobtcPendingAskMaxVolume:
exchange.cancel_order(order['order_id'])
amountHaobtcAskPending -= float(order['remainsize'])
continue
# 如果Haobtc更新深度失败,即bid和ask为零,则不进行下单操作
if (exchange.bid == 0) or (exchange.ask == 0):
break
# 用对盘计算临界值,跟最优价和次优价对比,以主动寻求机会
# Haobtc下买单时的正常情况
if (exchange.cny_free > config.MinAmountOnce * exchange.ask) and (bid_min != 0) \
and ((exchange.ask-1) * (1 - config.makerRate * config.ORDERWARNINGRATE) < bid_min):
exchange.buy(int(exchange.ask-1), retainDigiNum(exchange.cny_free / (exchange.ask-1), 4))
# Haobtc下卖单时的正常情况
if (exchange.btc_free > config.MinAmountOnce) and (ask_max != 0) \
and ((exchange.bid+1) * (1 + config.makerRate * config.ORDERWARNINGRATE) > ask_max):
exchange.sell(int(exchange.bid+1), retainDigiNum(exchange.btc_free, 4))
# 其他市场差价过大时,Haobtc下买单存在最优价的情况
if (exchange.cny_free > config.MinAmountOnce * exchange.ask) \
and (amountHaobtcBidPending < HaobtcPendingBidMaxVolume - config.MinAmountOnce) \
and (bid_max != 0) \
and (bid_min <= (exchange.ask-1) * (1 - config.makerRate * config.ORDERWARNINGRATE) < bid_max):
# 下在买一上的数量
amount_pend_in_firstprice = min(HaobtcPendingBidMaxVolume,
retainDigiNum(exchange.cny_free / (exchange.ask-1), 4),
retainDigiNum(HaobtcPendingBidMaxVolume - amountHaobtcBidPending,
4))
# 剩余的数量,下在次优价格对应的临界值上
amount_pend_in_next_price = exchange.cny_free / (exchange.ask-1) - amount_pend_in_firstprice
# 下买单
exchange.buy(int(exchange.ask-1), amount_pend_in_firstprice)
if amount_pend_in_next_price > config.MinAmountOnce:
exchange.buy(int(bid_min / (1 - config.makerRate * config.ORDERWARNINGRATE)),
retainDigiNum(amount_pend_in_next_price, 4))
# 其他市场最优价格不能产生利润时
# 下买单,没有利润,下在临界值向下取整
if (exchange.cny_free > config.MinAmountOnce * exchange.ask) \
and (amountHaobtcBidPending < HaobtcPendingBidMaxVolume - config.MinAmountOnce) \
and (bid_max != 0) \
and (bid_max < (exchange.ask-1) * (1 - config.makerRate * config.ORDERWARNINGRATE)):
if int(bid_max / (1 - config.makerRate * config.ORDERWARNINGRATE)) > exchange.bid - config.OrderRetainScope:
exchange.buy(int(bid_max / (1 - config.makerRate * config.ORDERWARNINGRATE)),
min(HaobtcPendingBidMaxVolume,
# 下全部空余资金
retainDigiNum(
exchange.cny_free / int(bid_max / (1 - config.makerRate * config.ORDERWARNINGRATE)),
4),
retainDigiNum(HaobtcPendingBidMaxVolume - amountHaobtcBidPending, 4)))
# 其他市场差价过大时,Haobtc下卖单存在最优价的情况
if (exchange.btc_free > config.MinAmountOnce) \
and (amountHaobtcAskPending < HaobtcPendingAskMaxVolume - config.MinAmountOnce) \
and (ask_max != 0) \
and (ask_min < (exchange.bid+1) * (1 + config.makerRate * config.ORDERWARNINGRATE) <= ask_max):
# 下在卖一上的数量
amount_pend_in_firstprice = min(HaobtcPendingAskMaxVolume,
retainDigiNum(exchange.btc_free, 4),
retainDigiNum(HaobtcPendingAskMaxVolume - amountHaobtcAskPending,
4))
# 下在次优价格对应的临界值上的数量
amount_pend_in_next_price = exchange.btc_free - amount_pend_in_firstprice
# 下卖单
exchange.sell(int(exchange.bid+1), retainDigiNum(amount_pend_in_firstprice, 4))
if amount_pend_in_next_price > config.MinAmountOnce:
exchange.sell(math.ceil(ask_max / (1 + config.makerRate * config.ORDERWARNINGRATE)),
retainDigiNum(amount_pend_in_next_price, 4))
# 其他市场最优价格不能产生利润时
# 下卖单,没有利润,下在临界值向上取整
if (exchange.btc_free > config.MinAmountOnce) \
and (amountHaobtcAskPending < HaobtcPendingAskMaxVolume - config.MinAmountOnce) \
and (ask_max != 0) \
and ((exchange.bid+1) * (1 + config.makerRate * config.ORDERWARNINGRATE) < ask_min):
if math.ceil(ask_min / (1 + config.makerRate * config.ORDERWARNINGRATE))\
< exchange.ask + config.OrderRetainScope:
exchange.sell(math.ceil(ask_min / (1 + config.makerRate * config.ORDERWARNINGRATE)),
min(HaobtcPendingAskMaxVolume,
# 下全部空余资金
retainDigiNum(exchange.btc_free, 4),
retainDigiNum(HaobtcPendingAskMaxVolume - amountHaobtcAskPending, 4)))
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
print("%s: Pending Haobtc complete." % datetime)
break
else:
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
print("%s: Pending Haobtc failed! Other exchanges' depth error!" % datetime)
def handleOthers(): # 处理普通的市场套利,也包括考虑了手续费的Haobtc
global exchanges, init_btc, total_btc, total_cny, last_cny, last_btc
# 标记利润来源,0表示来源于被动策略,1表示来源于主动策略;初始为0
flag = 0
record = BmobHelper()
print("----------Others----------")
if config.OpenPriorityStrategy:
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
updateCurrentAsset(["Haobtc"])
if not isNomal():
# 如果交易所资产更新失败,或者handleOthers跟handleHaobtc之中haobtc有成交,或者上一轮平衡币数出错,则操作之前,平衡一次
# balanceBtc() # 考虑不用平衡
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
updateCurrentAsset(["Haobtc"])
print("%s: Asset is updated! Current total_cny: %f, total_btc: %f; Init_btc(changeless): %f"
% (datetime, total_cny, total_btc, init_btc))
else:
print("%s: Asset is updated! Current total_cny: %f, total_btc: %f; Init_btc(changeless): %f"
% (datetime, total_cny, total_btc, init_btc))
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
updateCurrentDepth(["Haobtc"])
print("%s: Depth is updated!" % datetime)
for exchange in exchanges:
print("%8s, bid: %7.2f, ask: %7.2f; btc_free: %10.6f, btc_frozen: %10.6f, cny_free: %9.4f, cny_frozen: %9.4f" %
(exchange.__class__.__name__, exchange.bid, exchange.ask, exchange.btc_free, exchange.btc_frozen,
exchange.cny_free, exchange.cny_frozen))
buy_exchanges = sorted(exchanges, key=attrgetter('ask'), reverse=False)
sell_exchanges = sorted(exchanges, key=attrgetter('bid'), reverse=True)
for buy_exchange in buy_exchanges:
for sell_exchange in sell_exchanges:
if buy_exchange == sell_exchange: # 排除相同的交易所
continue
if (buy_exchange.__class__.__name__ == 'Haobtc') or (sell_exchange.__class__.__name__ == 'Haobtc'):
continue
if buy_exchange.bid == 0 or sell_exchange.bid == 0: # 排除交易所深度更新不成功;只要更新不成功,四个参数都为0,取其中任一个判断
continue
if sell_exchange.bid * (1 - sell_exchange.fee / 100) - buy_exchange.ask * \
(1 + buy_exchange.fee / 100) < config.MinDiff: # 排除价差不够
continue # 思考:能不能简化步骤,将分析和买卖合并
maxVolume = max(config.MinAmountOnce, min(retainDigiNum(buy_exchange.ask_volume, 3),
retainDigiNum(sell_exchange.bid_volume, 3))) # 最大可交易量
price = retainDigiNum((sell_exchange.bid + buy_exchange.ask) / 2, 2)
if (buy_exchange.cny_free < config.MinAmountOnce * price) or \
(sell_exchange.btc_free < config.MinAmountOnce): # 排除可用资金不够
continue
volume = min(config.MaxAmountOnce, retainDigiNum(buy_exchange.cny_free / price, 3),
retainDigiNum(sell_exchange.btc_free, 3), maxVolume) # 实际下单币数取交易所可交易数,最大可交易数,最大交易数限制,四者最小值
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time()))) # 记录买卖操作的时间
threadpool = ThreadPoolExecutor(max_workers=2)
buy_future = threadpool.submit(buy_exchange.buy, price, volume)
sell_future = threadpool.submit(sell_exchange.sell, price, volume)
try:
buy_result = buy_future.result(timeout=10)
sell_result = sell_future.result(timeout=10)
except Exception as e:
buy_result = False
sell_result = False
print(e)
record.record_op(datetime, "trade", buy_exchange.__class__.__name__, sell_exchange.__class__.__name__,
price, volume, "Price diff: %f" % (sell_exchange.bid - buy_exchange.ask))
if buy_exchange.cancel_order(buy_result):
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
record.record_op(datetime, "error", None, None, 0, 0,
"%s dot not completely deal" % buy_exchange.__class__.__name__)
if sell_exchange.cancel_order(sell_result):
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
record.record_op(datetime, "error", None, None, 0, 0,
"%s dot not completely deal" % sell_exchange.__class__.__name__)
balanceBtc()
flag = 1
break
if flag == 1:
break
if flag == 0:
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
print("%s: No opportunity to arbitrage." % datetime)
print('----------Profit----------')
if int(time.time()) % config.PeriodRecordProfit < config.TiggerTime:
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
profit = retainDigiNum(getProfit(), 6) # 此操作会更新资产信息
if profit != 0: # 如果利润值为0,上一次的比特币数量和人民币数量不变化;利润值不为零,才用当前的数量取代上次的
# 计算利润时利润值为零的情况:交易所资产更新失败;交易所深度更新失败;利润值真的为零
last_cny = total_cny # 操作结束后,将当前的人民币(和比特币)数量赋值给last_cny,作为下一轮操作的利润统计参考值
last_btc = total_btc
# 获取当前市场价格
price = 0
for exchange in exchanges:
if (exchange.ask == 0) or (exchange.bid == 0):
continue
price = exchange.bid
break
if isNomal() and (price != 0) and (abs(profit) < total_btc * 20 * 3): # 预估最大差价为20,误差系数为3
# flag记录利润来源,0表示好比特,1表示其他市场
record.record_op(datetime, "profit", None, None, flag, retainDigiNum(profit, 6),
"total_cny: %.6f, total_btc: %.6f, price: %.2f" % (total_cny, total_btc, price))
print("%s: Current total_cny: %f, total_btc: %f; profit: %f" % (datetime, total_cny, total_btc, profit))
print("****************************************")
def balanceFund():
if (int(time.time() + 28800) % 86400 % int(24 / config.TIMES_BALANCEFUND * 3600) < config.TiggerTime) \
and isNomal():
# 撤销非好比特市场的所有订单,因为行情大波动时,实际滑点大于预估滑点,导致订单长时间地挂在不能成交的位置
cancelAll()
# 用来平衡市场的比特币和人民币价值,使之保持平衡
global total_btc, total_cny, init_btc, last_btc, last_cny, exchanges
ask = 0
for exchange in exchanges:
if (exchange.ask == 0) or (exchange.bid == 0):
continue
ask = exchange.ask
break
referPrice = ask # 参考价格
referBTC = (referPrice*total_btc + total_cny) / 2 / referPrice # 比特币参考持仓
fundBalance = referBTC - init_btc # 需要平衡的币数,根据当前资产来算的
fundDiff = fundBalance * referPrice # 需要平衡的比特币数量的人民币市值
# 资产差超过了资金触发阈值,并且参考价格不为零,才做资金平衡操作
if abs(fundDiff) > config.ThresholdPrice * total_btc and (referPrice != 0):
if fundBalance > 0: # 比特币跌了,留一定的缓冲区更低的时候买
referBTC -= (config.ThresholdPrice * total_btc)/referPrice
else: # 比特币涨了,留一定的缓冲区更高的时候卖
referBTC += (config.ThresholdPrice * total_btc)/referPrice
init_btc = retainDigiNum(referBTC, 4) # 用当前的持仓参考值覆盖之前的初始比特币数量
balanceBtc()
# 记录操作
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
record = BmobHelper()
# 操作比特币数量记录在amount字段,正为买入比特币,负为卖出比特币;折合人民币记录在price字段
record.record_op(datetime, "fundBalance", None, None, retainDigiNum(fundDiff, 4),
retainDigiNum(fundBalance, 4), None)
def sendEmail():
# 邮件用来报告错误信息
subject = 'Error report!'
datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
msg = "Arbitrager exit! Please restart."
message = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s\r\n" % \
(config.EMAIL_HOST_USER, ", ".join(config.EMAIL_RECEIVER), subject, msg)
try:
smtpserver = smtplib.SMTP(config.EMAIL_HOST)
smtpserver.set_debuglevel(0)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.login(config.EMAIL_HOST_USER, config.EMAIL_HOST_PASSWORD)
smtpserver.sendmail(config.EMAIL_HOST_USER, config.EMAIL_RECEIVER, message)
smtpserver.quit()
smtpserver.close()
print("%s: Send email successfully!" % datetime)
except Exception as e:
print(e)
print("%s: Send email failed!" % datetime)
def onTick():
global total_cny
handleHaobtc()
handleOthers()
def main():
global init_btc, total_btc, last_cny, total_cny, last_btc
init_exchanges()
updateCurrentAsset()
if isNomal():
last_cny = total_cny # 记录程序运行前,人民币数量,给第一次onTick使用,每次onTick执行完后,last_cny都重新赋值
last_btc = total_btc
init_btc = total_btc # 记录程序运行前,比特币的数量,此数量一直不变
else:
print("Update asset failed! Init program failed! Please find the problems and restart.")
sys.exit()
cancelAll()
while True:
onTick()
balanceFund()
time.sleep(config.TickInterval)
if __name__ == '__main__':
main()
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Python
1
https://gitee.com/andywangqdu/Bitcoin-arbitrager.git
[email protected]:andywangqdu/Bitcoin-arbitrager.git
andywangqdu
Bitcoin-arbitrager
Bitcoin arbitrager
master

搜索帮助