欢迎大家根据这篇报告,公开自己的策略,或者改进回测引擎
第二个策略有新的研究内容,一定参考:https://www.fmz.com/digest-topic/5364
此策略已出收费版,改进很多,可供币安做市商。加微信 wangweibing_ustb 联系。
币安期货多币种对冲策略研究
币安期货最近发起了“千团大战”活动(活动地址:https://www.binance-cn.com/cn/futures/activity/tournament )。FMZ量化平台官方也组织了团队,直接搜索“FMZ”就可以找到,目前已经有200多人,欢迎参与,参加后可加下方微信,回复“币安”拉微信群。
<img src=“https://www.fmz.com/upload/asset/1fbed0c3795dbecac04.jpg” />
参与活动的用户均可获取此币安期货策略(策略源码将在活动前放出,也可以直接根据此报告自己的代码)。本篇即为此策略的研究报告。**注意策略只供参考,可以在此基础上提出自己的思路进行优化,也欢迎分享。**报告可直接在FMZ网站的研究环境直接使用(点击右上角下载,在研究环境中上传)。
研究环境点击控制中心,再点击箭头位置即可进入,打开上传的.pynb后缀的文件,按shift+enter逐行运行即可。研究环境的使用帮助里有基础的使用教程。 <img src=“https://www.fmz.com/upload/asset/1b39347a88aa4cff916.jpg” />
1.策略缘由
币安现货上架了许多山寨币,虽然短期涨跌不定,如果用日线观察久一些,就会发现基本都跌了90%以上,有的甚至只有最高价零头的零头。可是现货并没有普遍的做空手段,除了不碰山寨币,没有特别的建议。最近两月币安期货上线了二十多个永续合约,其中大多数时主流币种,也有一些默默无闻。这给了我们做空这些山寨币组合的手段。利用山寨币对于BTC往往下跌以及山寨币的走势相关系数很高,可以设计出两种策略。
2.策略原理
第一个策略:策略将分散等值做空选定的一篮子山寨币,同时等仓位做多比特币对冲,降低风险和波动率。随着价格的波动,不断调整仓位保持空头价值恒定和多头仓位对等。本质上时做空山寨币-比特币价格指数。
第二个策略:将做空价格高于山寨币-比特币价格指数的币种,做多低于指数的币种,偏离越大,仓位越大。同时将未对冲的头寸用BTC对冲(也可不对冲)。
# 需要导入的库 import pandas as pd import requests import matplotlib.pyplot as plt import seaborn as sns import numpy as np %matplotlib inline
3.筛选需要的币种
币安永续合约当前上架币种,用API获取,不包含BTC共23个。但研究环境不能访问外网,这里直接给出列表。
#Info = requests.get('https://fapi.binance.com/fapi/v1/exchangeInfo') #symbols = [symbol_info['baseAsset'] for symbol_info in Info.json()['symbols']] symbols = ['ETH', 'BCH', 'XRP', 'EOS', 'LTC', 'TRX', 'ETC', 'LINK', 'XLM', 'ADA', 'XMR', 'DASH', 'ZEC', 'XTZ', 'BNB', 'ATOM', 'ONT', 'IOTA', 'BAT', 'VET', 'NEO', 'QTUM', 'IOST']
首先我们研究一下过去一年山寨币对比特币的价格走势,数据我已经提前下载好,并传到了论坛上,可以在研究环境中直接引用
price_btc = pd.read_csv('https://www.fmz.com/upload/asset/1ef1af8ec28a75a2dcb.csv', index_col = 0) price_btc.index = pd.to_datetime(price_btc.index,unit='ms') #索引位日期
price_btc.tail()
ETH BCH XRP EOS LTC TRX \ 0 2020-03-21 0.021434 0.035580 0.000026 0.000368 0.006196 0.000002 2020-03-22 0.021026 0.034852 0.000025 0.000367 0.006086 0.000002 2020-03-23 0.021010 0.034186 0.000024 0.000353 0.006021 0.000002 2020-03-24 0.020529 0.033712 0.000024 0.000347 0.006012 0.000002 2020-03-25 0.020727 0.033480 0.000024 0.000348 0.005996 0.000002 ETC LINK XLM ADA ... XTZ BNB \ 0 ... 2020-03-21 0.000810 0.000367 0.000006 0.000005 ... 0.000268 0.001956 2020-03-22 0.000789 0.000341 0.000006 0.000005 ... 0.000253 0.001918 2020-03-23 0.000768 0.000348 0.000006 0.000005 ... 0.000259 0.001883 2020-03-24 0.000751 0.000343 0.000006 0.000004 ... 0.000261 0.001842 2020-03-25 0.000786 0.000339 0.000006 0.000004 ... 0.000258 0.001857 ATOM ONT IOTA BAT VET NEO \ 0 2020-03-21 0.000351 0.000060 0.000022 0.000022 4.700000e-07 0.001007 2020-03-22 0.000330 0.000059 0.000022 0.000021 4.400000e-07 0.001022 2020-03-23 0.000326 0.000058 0.000022 0.000021 4.400000e-07 0.001032 2020-03-24 0.000318 0.000057 0.000021 0.000023 4.500000e-07 0.001022 2020-03-25 0.000321 0.000056 0.000021 0.000023 4.600000e-07 0.001020 QTUM IOST 0 2020-03-21 0.000199 5.600000e-07 2020-03-22 0.000197 5.000000e-07 2020-03-23 0.000191 5.100000e-07 2020-03-24 0.000189 4.900000e-07 2020-03-25 0.000187 4.900000e-07 [5 rows x 23 columns]
先把这些币种价格画出来看看趋势,数据要归一化。可以看到除了四个币种外,其余币种的价格走势基本一致,呈现出下降趋势。
price_btc_norm = price_btc/price_btc.fillna(method='bfill').iloc[0,] price_btc_norm.plot(figsize=(16,6),grid = True,legend=False);
<Figure size 1152x432 with 1 Axes>
将最后价格变化排一下序,就可以找到明显不同的几个币,分别是LINK,XTZ,BCH, ETH。说明它们往往能走出独立行情,做空它们风险较高,需要排除策略之外。 将剩余的币种再画一个相关系数的热力图,发现ETC,ATOM的走势也相对特殊,可以排除。
price_btc_norm.iloc[-1,].sort_values()[-5:]
ETH 0.600417 ETC 0.661616 BCH 1.141961 XTZ 2.512195 LINK 2.764495 Name: 2020-03-25 00:00:00, dtype: float64
trade_symbols = list(set(symbols)-set(['LINK','XTZ','BCH', 'ETH'])) #剩余的币种
plt.subplots(figsize=(12, 12)) # 设置画面大小 sns.heatmap(price_btc[trade_symbols].corr(), annot=True, vmax=1, square=True, cmap="Blues");
<Figure size 864x864 with 2 Axes>
最后剩余的币种一年平均下跌66%,显然有充足的做空空间。把这些币的趋势合成出山寨币价格指数,发现基本一路下跌,去年下半年较稳定,今年又开始一路下跌。本此研究筛选出’LINK’,‘XTZ’,‘BCH’, ‘ETH’, ‘ETC’,‘ATOM’,‘BNB’,‘EOS’,'LTC’不参与第一个策略的做空,具体的币种可以自己回测。
需要注意的是,现在的山寨币指数处于过去一年中低点,也许不是做空良机,反而可以做多,需要根据个人选择。
trade_symbols = list(set(symbols)-set(['LINK','XTZ','BCH', 'ETH', 'ETC','ATOM','BNB','EOS','LTC'])) #剩余的币种,具体减去哪些,可自己设定。 1-price_btc_norm[trade_symbols].iloc[-1,].mean()
0.6714306758250285
price_btc_norm[trade_symbols].mean(axis=1).plot(figsize=(16,6),grid = True,legend=False);
<Figure size 1152x432 with 1 Axes>
4.币安永续数据
同样的,币安永续的数据已经整理好了,你也可以在自己的notebook中直接引用,数据是2020年1月28到3月31日的1h行情K线,因为币安上线大部分永续合约的时间就这两个月,所以数据用于回测是够了。
price_usdt = pd.read_csv('https://www.fmz.com/upload/asset/20227de6c1d10cb9dd1.csv ', index_col = 0) price_usdt.index = pd.to_datetime(price_usdt.index)
price_usdt.tail()
BTC ETH BCH XRP EOS LTC TRX \ 0 2020-04-07 23:00:00 7192.90 164.55 252.51 0.1925 2.632 44.83 0.01329 2020-04-08 00:00:00 7153.32 164.42 251.62 0.1914 2.591 44.41 0.01321 2020-04-08 01:00:00 7160.02 163.83 250.55 0.1924 2.601 44.53 0.01321 2020-04-08 02:00:00 7262.37 168.74 264.67 0.1972 2.684 45.94 0.01352 2020-04-08 03:00:00 7378.88 172.81 275.81 0.2003 2.741 46.68 0.01371 ETC LINK XLM ... XTZ BNB ATOM ONT \ 0 ... 2020-04-07 23:00:00 5.475 2.729 0.04843 ... 1.960 14.725 2.346 0.4219 2020-04-08 00:00:00 5.415 2.719 0.04816 ... 1.928 14.588 2.321 0.4167 2020-04-08 01:00:00 5.428 2.758 0.04818 ... 1.943 14.592 2.320 0.4152 2020-04-08 02:00:00 5.586 2.808 0.04947 ... 1.995 14.902 2.378 0.4244 2020-04-08 03:00:00 5.720 2.838 0.05033 ... 2.052 15.339 2.419 0.4355 IOTA BAT VET NEO QTUM IOST 0 2020-04-07 23:00:00 0.1632 0.1611 0.003873 7.719 1.373 0.003394 2020-04-08 00:00:00 0.1628 0.1600 0.003822 7.658 1.366 0.003368 2020-04-08 01:00:00 0.1628 0.1633 0.003815 7.657 1.369 0.003373 2020-04-08 02:00:00 0.1662 0.1674 0.003873 7.800 1.400 0.003442 2020-04-08 03:00:00 0.1700 0.1702 0.003930 7.965 1.420 0.003484 [5 rows x 24 columns]
先归一化数据看一下整体走势,在3月份的大跌中,相对于2月初的价格,普遍腰斩,可见永续的风险也很高,这波大跌也是对策略的考验。
price_usdt_norm = price_usdt/price_usdt.fillna(method='bfill').iloc[0,] price_usdt_norm.plot(figsize=(16,6),grid = True,legend=False);
<Figure size 1152x432 with 1 Axes>
将我们要做空的币相对于比特币的指数价格画出来,策略原理就是做空这条曲线,收益也基本上是这条曲线反过来。
price_usdt_btc = price_usdt.divide(price_usdt['BTC'],axis=0) price_usdt_btc_norm = price_usdt_btc/price_usdt_btc.fillna(method='bfill').iloc[0,] price_usdt_btc_norm[trade_symbols].mean(axis=1).plot(figsize=(16,6),grid = True); #price_usdt_btc_norm.mean(axis=1).plot(figsize=(16,6),grid = True,legend=False);
<Figure size 1152x432 with 1 Axes>
5.回测引擎
由于FMZ本地回测并没有所有币种的数据,也不支持多币种回测,所以需要重新实现一个回测引擎,写的比较简单,但也基本够用。考虑到了手续费,但基本忽略了资金费率,没有考虑维持保证金的情况。记录了总权益、占用保证金、杠杆等历史。由于这次策略基本多空对等的,所以资金费率的影响不大。
回测并未考虑到滑价情况,可以自行加大手续费模拟,考虑到币安maker手续费低,即使是冷门币种的盘口差价也很小,实际下单中可以利用冰山委托的方式下单,影响应该不大。
创建交易所对象时,需要指定要交易的币种,Buy做多,Sell做空,由于永续的限制,同时多空会自动平仓, 当做空时币种数量为负。参数如下:
class Exchange: def __init__(self, trade_symbols, leverage=20, commission=0.00005, initial_balance=10000, log=False): self.initial_balance = initial_balance #初始的资产 self.commission = commission self.leverage = leverage self.trade_symbols = trade_symbols self.date = '' self.log = log self.df = pd.DataFrame(columns=['margin','total','leverage','realised_profit','unrealised_profit']) self.account = {'USDT':{'realised_profit':0, 'margin':0, 'unrealised_profit':0, 'total':initial_balance, 'leverage':0}} for symbol in trade_symbols: self.account[symbol] = {'amount':0, 'hold_price':0, 'value':0, 'price':0, 'realised_profit':0, 'margin':0, 'unrealised_profit':0} def Trade(self, symbol, direction, price, amount, msg=''): if self.date and self.log: print('%-20s%-5s%-5s%-10.8s%-8.6s %s'%(str(self.date), symbol, 'buy' if direction == 1 else 'sell', price, amount, msg)) cover_amount = 0 if direction*self.account[symbol]['amount'] >=0 else min(abs(self.account[symbol]['amount']), amount) open_amount = amount - cover_amount self.account['USDT']['realised_profit'] -= price*amount*self.commission #扣除手续费 if cover_amount > 0: #先平仓 self.account['USDT']['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount #利润 self.account['USDT']['margin'] -= cover_amount*self.account[symbol]['hold_price']/self.leverage #释放保证金 self.account[symbol]['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount self.account[symbol]['amount'] -= -direction*cover_amount self.account[symbol]['margin'] -= cover_amount*self.account[symbol]['hold_price']/self.leverage self.account[symbol]['hold_price'] = 0 if self.account[symbol]['amount'] == 0 else self.account[symbol]['hold_price'] if open_amount > 0: total_cost = self.account[symbol]['hold_price']*direction*self.account[symbol]['amount'] + price*open_amount total_amount = direction*self.account[symbol]['amount']+open_amount self.account['USDT']['margin'] += open_amount*price/self.leverage self.account[symbol]['hold_price'] = total_cost/total_amount self.account[symbol]['amount'] += direction*open_amount self.account[symbol]['margin'] += open_amount*price/self.leverage self.account[symbol]['unrealised_profit'] = (price - self.account[symbol]['hold_price'])*self.account[symbol]['amount'] self.account[symbol]['price'] = price self.account[symbol]['value'] = abs(self.account[symbol]['amount'])*price return True def Buy(self, symbol, price, amount, msg=''): self.Trade(symbol, 1, price, amount, msg) def Sell(self, symbol, price, amount, msg=''): self.Trade(symbol, -1, price, amount, msg) def Update(self, date, close_price): #对资产进行更新 self.date = date self.close = close_price self.account['USDT']['unrealised_profit'] = 0 for symbol in self.trade_symbols: if np.isnan(close_price[symbol]): continue self.account[symbol]['unrealised_profit'] = (close_price[symbol] - self.account[symbol]['hold_price'])*self.account[symbol]['amount'] self.account[symbol]['price'] = close_price[symbol] self.account[symbol]['value'] = abs(self.account[symbol]['amount'])*close_price[symbol] self.account['USDT']['unrealised_profit'] += self.account[symbol]['unrealised_profit'] if self.date.hour in [0,8,16]: pass self.account['USDT']['realised_profit'] += -self.account[symbol]['amount']*close_price[symbol]*0.01/100 self.account['USDT']['total'] = round(self.account['USDT']['realised_profit'] + self.initial_balance + self.account['USDT']['unrealised_profit'],6) self.account['USDT']['leverage'] = round(self.account['USDT']['margin']/self.account['USDT']['total'],4)*self.leverage self.df.loc[self.date] = [self.account['USDT']['margin'],self.account['USDT']['total'],self.account['USDT']['leverage'],self.account['USDT']['realised_profit'],self.account['USDT']['unrealised_profit']]
# 先测试一下回测引擎 e = Exchange(['BTC','XRP'],initial_balance=10000,commission=0,log=True) e.Buy('BTC',100, 5) e.Sell('XRP',10, 50) e.Sell('BTC',105,e.account['BTC']['amount']) e.Buy('XRP',9,-e.account['XRP']['amount']) round(e.account['USDT']['realised_profit'],4)
75.0
6.第一个策略代码
策略逻辑:
做空的价值trade_value,决定了仓位的大小。设置log=True将打印交易日志
# 需要与BTC对冲 trade_symbols = list(set(symbols)-set(['LINK','XTZ','BCH', 'ETH', 'ETC','ATOM','BNB','EOS','LTC'])) #剩余的币种 e = Exchange(trade_symbols+['BTC'],initial_balance=10000,commission=0.0005,log=False) trade_value = 2000 for row in price_usdt.iloc[:].iterrows(): e.Update(row[0], row[1]) empty_value = 0 for symbol in trade_symbols: price = row[1][symbol] if np.isnan(price): continue if e.account[symbol]['value'] - trade_value < -20 : e.Sell(symbol, price, round((trade_value-e.account[symbol]['value'])/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) if e.account[symbol]['value'] - trade_value > 20 : e.Buy(symbol, price, round((e.account[symbol]['value']-trade_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) empty_value += e.account[symbol]['value'] price = row[1]['BTC'] if e.account['BTC']['value'] - empty_value < -20: e.Buy('BTC', price, round((empty_value-e.account['BTC']['value'])/price,6),round(e.account['BTC']['realised_profit']+e.account['BTC']['unrealised_profit'],2)) if e.account['BTC']['value'] - empty_value > 20: e.Sell('BTC', price, round((e.account['BTC']['value']-empty_value)/price,6),round(e.account['BTC']['realised_profit']+e.account['BTC']['unrealised_profit'],2)) stragey_1 = e
最终各个币种的利润如下:
pd.DataFrame(stragey_1.account).T.apply(lambda x:round(x,3))
realised_profit margin unrealised_profit total leverage \ USDT 5576.449 2350.526 -536.669 15057.009 3.176 DASH 847.374 86.354 -272.925 NaN NaN IOST 926.829 88.409 -231.819 NaN NaN ONT 1302.690 85.751 -284.984 NaN NaN XMR 654.856 77.073 -458.549 NaN NaN XLM 643.571 81.016 -379.678 NaN NaN IOTA 1358.780 82.337 -353.258 NaN NaN QTUM 902.930 87.172 -256.569 NaN NaN BAT 1013.011 82.470 -350.592 NaN NaN XRP 682.601 83.889 -322.224 NaN NaN VET 1278.532 78.069 -438.614 NaN NaN NEO 1109.221 83.870 -322.590 NaN NaN ADA 982.309 81.397 -372.060 NaN NaN ZEC 1173.761 82.286 -354.278 NaN NaN TRX 706.935 82.172 -356.555 NaN NaN BTC -7708.165 1188.261 4234.779 NaN NaN amount hold_price value price USDT NaN NaN NaN NaN DASH -26.788 64.472 2000.000 74.660 IOST -574052.813 0.003 2000.000 0.003 ONT -4592.423 0.373 2000.000 0.436 XMR -34.471 44.717 2000.000 58.020 XLM -39737.731 0.041 2000.000 0.050 IOTA -11764.706 0.140 2000.000 0.170 QTUM -1408.451 1.238 2000.000 1.420 BAT -11750.881 0.140 2000.000 0.170 XRP -9985.022 0.168 2000.000 0.200 VET -508905.852 0.003 2000.000 0.004 NEO -251.099 6.680 2000.000 7.965 ADA -54510.766 0.030 2000.000 0.037 ZEC -52.618 31.277 2000.000 38.010 TRX -145878.920 0.011 2000.000 0.014 BTC 3.795 6262.883 28000.001 7378.880
下面两幅图分别是净值曲线和使用的杠杆。
净值曲线中黄色的是1倍杠杆做空山寨币指数的效果,可以看到策略基本放大了指数的波动,符合预期。最终两个月收益60%,最大回撤20%,最大使用杠杆约8倍,大部分时间都在6倍以下,还是比较安全的。最重要的是,完全对冲使得策略在3月12号大跌中损失不大。
当做空的币价上涨,合约价值增加,此时是减仓的,反之盈利是加仓。这使得总的合约价值维持恒定,即使暴涨暴跌也损失有限。
但风险前面也提到了,山寨币是很有可能走出独立的行情的,并且有可能从底部抬升不少。这取决与如何使用,如果你看好山寨币并认为已经到底部,可以方向操作,做多指数。或者你看好某几个币种,可以和它们对冲。
(stragey_1.df['total']/stragey_1.initial_balance).plot(figsize=(18,6),grid = True);#净值曲线 #(2-price_usdt_btc_norm[trade_symbols].mean(axis=1)).plot(figsize=(18,6),grid = True);
<Figure size 1296x432 with 1 Axes>
# 策略的杠杆 stragey_1.df['leverage'].plot(figsize=(18,6),grid = True);
<Figure size 1296x432 with 1 Axes>
当然由于山寨币对USDT的价格也是下跌的,极端的方案是不对冲,直接裸空,但波动很大,回撤很高
trade_symbols = list(set(symbols)-set(['LINK','XTZ','BCH', 'ETH', 'ETC','ATOM','BNB','EOS','LTC'])) #剩余的币种 e = Exchange(trade_symbols+['BTC'],initial_balance=10000,commission=0.0005,log=False) trade_value = 2000 for row in price_usdt.iloc[:].iterrows(): e.Update(row[0], row[1]) empty_value = 0 for symbol in trade_symbols: price = row[1][symbol] if np.isnan(price): continue if e.account[symbol]['value'] - trade_value < -20 : e.Sell(symbol, price, round((trade_value-e.account[symbol]['value'])/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) if e.account[symbol]['value'] - trade_value > 20 : pass #e.Buy(symbol, price, round((e.account[symbol]['value']-trade_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) empty_value += e.account[symbol]['value'] stragey_1b = e
(stragey_1b.df['total']/stragey_1.initial_balance).plot(figsize=(18,6),grid = True);#净值曲线 (2-price_usdt_btc_norm[trade_symbols].mean(axis=1)).plot(figsize=(18,6),grid = True);
<Figure size 1296x432 with 1 Axes>
7.第二个策略代码
策略逻辑:
同样由trade_value控制开仓大小。也可以修改diff/0.001的换算系数
trade_symbols = list(set(symbols)-set(['LINK','XTZ','BCH', 'ETH'])) #剩余的币种 price_usdt_btc_norm_mean = price_usdt_btc_norm[trade_symbols].mean(axis=1) e = Exchange(trade_symbols+['BTC'],initial_balance=10000,commission=0.0005,log=False) trade_value = 300 for row in price_usdt.iloc[:].iterrows(): e.Update(row[0], row[1]) empty_value = 0 for symbol in trade_symbols: price = row[1][symbol] if np.isnan(price): continue diff = price_usdt_btc_norm.loc[row[0],symbol] - price_usdt_btc_norm_mean[row[0]] aim_value = -trade_value*round(diff/0.01,0) now_value = e.account[symbol]['value']*np.sign(e.account[symbol]['amount']) empty_value += now_value if aim_value - now_value > 50: e.Buy(symbol, price, round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) if aim_value - now_value < -50: e.Sell(symbol, price, -round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) price = row[1]['BTC'] aim_value = -empty_value now_value = e.account['BTC']['value']*np.sign(e.account['BTC']['amount']) if aim_value - now_value > 50: e.Buy('BTC', price, round((aim_value - now_value)/price, 6),round(e.account['BTC']['realised_profit']+e.account['BTC']['unrealised_profit'],2)) if aim_value - now_value < -50: e.Sell('BTC', price, -round((aim_value - now_value)/price, 6),round(e.account['BTC']['realised_profit']+e.account['BTC']['unrealised_profit'],2)) stragey_2 = e
策略的收益相对第一个策略好上不少,近两个月有100%的收益,但还是有20%的回撤,并且最近一周由于行情波动较小,收益不明显。总体的杠杆也不多。这个策略值得尝试。根据偏离程度的不同,最多的开了7800多USDT。
注意到如果某个币走出了独立的行情,比如相对于指数上涨了几倍,将会在该币种上积累大量的做空仓位,同样的大幅下跌也会使得策略大量做多,可以限制最大开仓价值。
(stragey_2.df['total']/stragey_2.initial_balance).plot(figsize=(18,6),grid = True);
<Figure size 1296x432 with 1 Axes>
# 各币种汇总结果 pd.DataFrame(e.account).T.apply(lambda x:round(x,3))
realised_profit margin unrealised_profit total leverage \ USDT 11544.446 2240.648 -572.088 20961.347 2.142 LTC 208.435 71.269 -74.615 NaN NaN DASH -1611.443 15.000 -0.000 NaN NaN IOTA -731.880 137.227 255.450 NaN NaN QTUM 3695.002 15.000 8.472 NaN NaN EOS 515.414 15.000 -16.148 NaN NaN XRP 804.351 213.625 -527.496 NaN NaN VET 2029.654 119.771 39.893 NaN NaN NEO 1046.044 44.474 39.211 NaN NaN ADA 606.302 29.077 -26.916 NaN NaN BNB 895.348 71.510 -69.793 NaN NaN TRX 980.596 141.199 -218.180 NaN NaN IOST 3369.938 103.764 24.726 NaN NaN ETC -2929.940 245.720 485.595 NaN NaN ONT 1365.283 216.755 464.890 NaN NaN XMR 662.873 239.586 -608.286 NaN NaN XLM 319.303 238.507 -629.853 NaN NaN ATOM 1501.062 113.670 126.604 NaN NaN BAT 1572.965 45.429 -29.451 NaN NaN ZEC -1108.730 126.443 171.133 NaN NaN BTC 164.300 37.620 -0.000 NaN NaN amount hold_price value price USDT NaN NaN NaN NaN LTC -32.134 44.358 1500.000 46.680 DASH -4.018 74.660 300.000 74.660 IOTA 17647.059 0.156 3000.000 0.170 QTUM 217.234 1.381 308.472 1.420 EOS -115.340 2.601 316.148 2.741 XRP -23964.054 0.178 4800.000 0.200 VET 619674.671 0.004 2435.321 0.004 NEO 116.595 7.629 928.682 7.965 ADA -16583.748 0.035 608.458 0.037 BNB -97.790 14.625 1500.000 15.339 TRX -221893.491 0.013 3042.160 0.014 IOST 602755.454 0.003 2100.000 0.003 ETC 944.056 5.206 5400.000 5.720 ONT 11021.814 0.393 4800.000 0.436 XMR -93.071 51.484 5400.000 58.020 XLM -107291.874 0.044 5400.000 0.050 ATOM 992.146 2.291 2400.000 2.419 BAT -5511.329 0.165 938.028 0.170 ZEC 71.034 35.601 2700.000 38.010 BTC -0.102 7378.880 752.395 7378.880
e.df['leverage'].plot(figsize=(18,6),grid = True);
<Figure size 1296x432 with 1 Axes>
如果不对冲的结果如下,实际上差别不大。因为多空基本是平衡的。
trade_symbols = list(set(symbols)-set(['LINK','XTZ','BCH', 'ETH']))#剩余的币种 price_usdt_btc_norm_mean = price_usdt_btc_norm[trade_symbols].mean(axis=1) e = Exchange(trade_symbols,initial_balance=10000,commission=0.0005,log=False) trade_value = 300 for row in price_usdt.iloc[:].iterrows(): e.Update(row[0], row[1]) empty_value = 0 for symbol in trade_symbols: price = row[1][symbol] if np.isnan(price): continue diff = price_usdt_btc_norm.loc[row[0],symbol] - price_usdt_btc_norm_mean[row[0]] aim_value = -trade_value*round(diff/0.01,1) now_value = e.account[symbol]['value']*np.sign(e.account[symbol]['amount']) empty_value += now_value if aim_value - now_value > 20: e.Buy(symbol, price, round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) if aim_value - now_value < -20: e.Sell(symbol, price, -round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) stragey_2b = e
(stragey_2b.df['total']/stragey_2.initial_balance).plot(figsize=(18,6),grid = True); #(stragey_2.df['total']/stragey_2.initial_balance).plot(figsize=(18,6),grid = True); #可叠加在一起看看
<Figure size 1296x432 with 1 Axes>
如果参考USDT的价格回归,效果会差很多
trade_symbols = list(set(symbols)-set(['LINK','XTZ','BCH', 'ETH']))+['BTC'] #剩余的币种 price_usdt_norm_mean = price_usdt_norm[trade_symbols].mean(axis=1) e = Exchange(trade_symbols,initial_balance=10000,commission=0.0005,log=False) trade_value = 300 for row in price_usdt.iloc[:].iterrows(): e.Update(row[0], row[1]) empty_value = 0 for symbol in trade_symbols+['BTC']: price = row[1][symbol] if np.isnan(price): continue diff = price_usdt_norm.loc[row[0],symbol] - price_usdt_norm_mean[row[0]] aim_value = -trade_value*round(diff/0.01,1) now_value = e.account[symbol]['value']*np.sign(e.account[symbol]['amount']) empty_value += now_value if aim_value - now_value > 20: e.Buy(symbol, price, round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) if aim_value - now_value < -20: e.Sell(symbol, price, -round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) stragey_2c = e
(stragey_2c.df['total']/stragey_2.initial_balance).plot(figsize=(18,6),grid = True); (stragey_2b.df['total']/stragey_2.initial_balance).plot(figsize=(18,6),grid = True);
<Figure size 1296x432 with 1 Axes>
如果限制最大持仓价值,表现会差一些
trade_symbols = list(set(symbols)-set(['LINK','XTZ','BCH', 'ETH'])) #剩余的币种 price_usdt_btc_norm_mean = price_usdt_btc_norm[trade_symbols].mean(axis=1) e = Exchange(trade_symbols+['BTC'],initial_balance=10000,commission=0.0005,log=False) trade_value = 300 for row in price_usdt.iloc[:].iterrows(): e.Update(row[0], row[1]) empty_value = 0 for symbol in trade_symbols: price = row[1][symbol] if np.isnan(price): continue diff = price_usdt_btc_norm.loc[row[0],symbol] - price_usdt_btc_norm_mean[row[0]] aim_value = -trade_value*round(diff/0.01,1) now_value = e.account[symbol]['value']*np.sign(e.account[symbol]['amount']) empty_value += now_value if aim_value - now_value > 20 and abs(aim_value)<3000: e.Buy(symbol, price, round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) if aim_value - now_value < -20 and abs(aim_value)<3000: e.Sell(symbol, price, -round((aim_value - now_value)/price, 6),round(e.account[symbol]['realised_profit']+e.account[symbol]['unrealised_profit'],2)) price = row[1]['BTC'] aim_value = -empty_value now_value = e.account['BTC']['value']*np.sign(e.account['BTC']['amount']) if aim_value - now_value > 20: e.Buy('BTC', price, round((aim_value - now_value)/price, 6),round(e.account['BTC']['realised_profit']+e.account['BTC']['unrealised_profit'],2)) if aim_value - now_value < -20: e.Sell('BTC', price, -round((aim_value - now_value)/price, 6),round(e.account['BTC']['realised_profit']+e.account['BTC']['unrealised_profit'],2)) stragey_2d = e
(stragey_2d.df['total']/stragey_2.initial_balance).plot(figsize=(17,6),grid = True);
<Figure size 1224x432 with 1 Axes>
8.总结与风险
第一个策略利用了山寨币总体价值不如比特币的特点,如果你看多比特币,不妨将这个策略长期坚持用下去,由于多空对等,也基本不怕8h一次的资金费率。长期来看,胜率比较高。但也担心山寨币目前在底部,有可能走出一段上涨的行情,造成策略的亏损。
第二个策略运用了山寨币的价格回归特性,涨的比指数多,大概率要跌回来。但可能会在单币种累计过多仓位,如果某个币真的一飞冲天不回头,会产生较大亏损。
由于策略的启动时间不同,具体参数不同,使用这个策略的人比较多时影响应该也不是很大。
总之,没有完美的策略,只有正确看待策略的态度,它终究还取决与使用者对风险的理解和对未来的判断。
这两个策略将在比赛前放出FMZ发明者量化平台的源码供大家参考。再此之前,也可以自己写出来,欢迎大家分享。
小草 /upload/asset/20c6b6d8de91f682f97.png /upload/asset/208a70b018da8e37e57.png
小草 /upload/asset/20c6b6d8de91f682f97.png
小草 /upload/asset/1420b2081ecd122522d.csv /upload/asset/1d169ff6cb8e9c8165e.png
小草 /upload/asset/1420b2081ecd122522d.csv
excm 似乎没什么卵用,要是能判断牛熊为什么还要对冲
小草 策略根据用户反馈,修改了一些代码和策略说明,直接ctrl+A复制代码覆盖保存,重启机器人即可用上最新代码。主要更新了山寨币指数策略做多的逻辑。策略地址: https://www.fmz.com/strategy/195226 https://www.fmz.com/strategy/194825
小草 具体策略已经公开在广场
小草 /upload/asset/20227de6c1d10cb9dd1.csv 最新的小时K线
区块冰山 期待源码
skyfffire 厉害。。
dsaidasi 厉害