本文利用ccxt实现简单的一个网格交易策略,只要价格一直在某一范围内波动就会有盈利产生。此外,在价格超出网格后增加了重新下单的功能,并在此基础上添加了可用余额的监控,从而在盈利足够多的情况下自动增加交易量。
用到的包
import os
import ccxt
import time
import pymysql
import logging
日志管理
tm = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
log_name = tm + '_huobi.log'
logging.basicConfig(filename=log_name, level=logging.INFO,format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
数据库
事先在mysql里建好名字为‘huobi’的数据库(create database huobi default charset utf8;)
# 连接database
db = pymysql.connect(
host='127.0.0.1',
user ='这里输用户名',
password ='这里输密码',
database ='huobi',)
# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()
#在数据库中创建新的订单信息表
cursor.execute("DROP TABLE IF EXISTS order_info")
sql = """CREATE TABLE order_info (
order_id VARCHAR(100) NOT NULL,
side VARCHAR(10),
price FLOAT,
amount FLOAT,
related_id VARCHAR(100))"""
cursor.execute(sql)
关联火币账户
# huobi
apikey = ‘这里输火币账户的apikey’
secretkey = ‘这里输火币账户的secretkey’
huobi=ccxt.huobipro({
'apiKey':apikey,
'secret':secretkey,
})
第一笔买单
以ETH/USDT为例,先下第一笔买单
order_symbol='ETH/USDT'
order_type='limit'
order_side='buy'
order_amount=0.04
ETH_Last=huobi.fetch_ticker(order_symbol)['last']
logging.info('ETH 最新价格:'+str(ETH_Last))
order_price=ETH_Last-0.3
take_order=huobi.create_order(order_symbol,order_type,order_side,order_amount,order_price)
logging.info(take_order)
takeorder_id=take_order['id']
logging.info(takeorder_id)
order_status=huobi.fetch_order(takeorder_id,order_symbol)
logging.info(order_status)
takeorder_side=order_status['side']
logging.info(takeorder_side)
takeorder_price=order_status['price']
logging.info(takeorder_price)
sql = f"""INSERT INTO order_info(order_id,side, price, amount, related_id) VALUES ('{takeorder_id}', '{takeorder_side}', {takeorder_price}, {order_amount}, '{takeorder_id}')"""
cursor.execute(sql)
db.commit()
设置基础余额
这一变量需根据实际账户余额设置,之后会不断根据可用余额和这个基础余额进行比较,从而决定是否增加交易量。
base_usdt=500
开始网格交易
这里的策略是每当价格下降2usdt,则买入0.04个eth。当价格上涨2usdt时,就卖出。实际可根据投入的资金自行决定并设置价格下降和上涨的范围。但由于要给火币平台一定的交易手续费,所以要提前减去这部分的成本,使得每笔的收益为正。
while True:
#价格低的优先查找(先买单后卖单)
sql = 'SELECT * FROM order_info ORDER BY price'
# 执行SQL语句
cursor.execute(sql)
# 获取所有记录列表
results = cursor.fetchall()
for index,row in enumerate(results):
order_id = row[0]
side = row[1]
price = row[2]
amount = row[3]
related_id = row[4]
#防止服务器请求异常
while True:
try:
order_status = huobi.fetch_order_status(order_id, order_symbol)
last_price = huobi.fetch_ticker(order_symbol)['last']
balance = huobi.fetch_balance()
#可用余额的监控与交易量的调整
if balance['USDT']['free']>=1.1*base_usdt:
base_usdt=1.1*base_usdt
order_amount=round(1.1*order_amount,3)
break
except:
logging.warning('请求异常,1秒后重试')
time.sleep(1)
continue
if side=='buy':
#当买单成交时
if order_status == 'closed':
logging.info('买单成交!')
# 删除已成交的db记录
sql = f"DELETE FROM order_info WHERE order_id='{order_id}'"
cursor.execute(sql)
db.commit()
#在止盈处下卖单
sell_side='sell'
sell_price=price+2
take_sell_order = huobi.create_order(order_symbol, order_type, sell_side, 0.998*amount, sell_price)
takeorder_id=take_sell_order['id']
sql = f"""INSERT INTO order_info(order_id,side, price, amount, related_id) VALUES ('{takeorder_id}', '{sell_side}', {sell_price}, {0.999*amount}, '{takeorder_id}')"""
related_id=takeorder_id
cursor.execute(sql)
db.commit()
logging.info(f"在止盈处下卖单成功:\n'{takeorder_id}', '{sell_side}', {sell_price}, {0.998*amount}, '{takeorder_id}'")
#在低一档的价格下买单
buy_side='buy'
buy_price=price-2
available_usdt = balance['USDT']['free']
if available_usdt < buy_price*order_amount:
logging.info('可用余额不足,无法下新的买单')
continue
take_buy_order = huobi.create_order(order_symbol, order_type, buy_side, order_amount, buy_price)
takeorder_id = take_buy_order['id']
sql = f"""INSERT INTO order_info(order_id,side, price, amount, related_id) VALUES ('{takeorder_id}', '{buy_side}', {buy_price}, {order_amount}, '{related_id}')"""
cursor.execute(sql)
db.commit()
logging.info(f"在低一档的价格下买单成功:\n'{takeorder_id}', '{buy_side}', {buy_price}, {order_amount}, '{related_id}")
# 当前价格远大于所挂买单时,撤销原有的买单并重新根据当前价格下新的买单
elif order_status=='open' and len(results)==1:
if last_price-price>=4:
logging.info('当前价格远大于所挂买单,撤销原有的买单!')
# 删除未成交的买单及db记录
sql = "SELECT * FROM order_info where side='buy'"
cursor.execute(sql)
total = cursor.fetchall()
try:
for row in total:
huobi.cancel_order(row[0], order_symbol)
except:
logging.warning('无法撤销已成交的买单')
continue
sql = f"DELETE FROM order_info WHERE side='buy'"
cursor.execute(sql)
db.commit()
logging.info('成功撤销所有买单!')
# 在略低于当前价格的地方下买单
buy_side = 'buy'
buy_price = last_price - 0.3
take_buy_order = huobi.create_order(order_symbol, order_type, buy_side, order_amount, buy_price)
takeorder_id = take_buy_order['id']
sql = f"""INSERT INTO order_info(order_id,side, price, amount, related_id) VALUES ('{takeorder_id}', '{buy_side}', {buy_price}, {order_amount}, '{takeorder_id}')"""
cursor.execute(sql)
db.commit()
logging.info(f"在略低于当前价格的地方下买单成功:\n'{takeorder_id}', '{buy_side}', {buy_price}, {order_amount}, '{takeorder_id}'")
elif side=='sell':
#当卖单成交时
if order_status=='closed':
logging.info('卖单成交!')
# 删除已成交的db记录
sql = f"DELETE FROM order_info WHERE order_id='{order_id}'"
cursor.execute(sql)
db.commit()
# 删除未成交的买单及db记录
sql = "SELECT * FROM order_info where side='buy'"
cursor.execute(sql)
total = cursor.fetchall()
try:
for row in total:
huobi.cancel_order(row[0],order_symbol)
except:
logging.warning('无法撤销已成交的买单')
continue
sql = f"DELETE FROM order_info WHERE side='buy'"
cursor.execute(sql)
db.commit()
logging.info('成功撤销所有买单!')
#在回调的地方下买单
buy_side='buy'
buy_price=last_price-2
take_buy_order = huobi.create_order(order_symbol, order_type, buy_side, order_amount, buy_price)
takeorder_id = take_buy_order['id']
sql = f"""INSERT INTO order_info(order_id,side, price, amount, related_id) VALUES ('{takeorder_id}', '{buy_side}', {buy_price}, {order_amount}, '{takeorder_id}')"""
cursor.execute(sql)
db.commit()
logging.info(f"在回调的地方下买单成功:\n'{takeorder_id}', '{buy_side}', {buy_price}, {order_amount}, '{takeorder_id}'")
#若价格低的卖单还未成交,则价格高的不可能成交
else:
time.sleep(1)
break
time.sleep(1)
运行结果
下图为最新的部分log,目前已运行了一周的时间,暂无异常发生。(之前是在okex平台已成功运行了一个多月,但现在okex无法提币,所以本文以火币为例)
更新
2020/11/25
以上是十月份的时候写的一篇博客,后面发现火币手续费要比okex贵一点,所以网格区间最好大于或等于2.5,封装了一下代码并已上传至github,但还没有测试过:)
2020/12/17
测试后修复了一处bug,目前可以在服务器上成功运行。开启脚本命令如下:
(python3 run.py > error_huobi.txt 2>&1 &)
2021/1/7
由于最近eth价格已超过1000,所以更新了网格区间,建议大于或等于10是安全的。
2021/2/6
由于最近eth价格增长速度过快,屡破新高,增加了动态调整网格区间的功能,设为价格的百分之一。