3
tập trung vào
1444
Người theo dõi

Phân tích lợi nhuận từ chênh lệch giá giữa giá tương lai và giá giao ngay của hợp đồng dựa trên tiền tệ

Được tạo ra trong: 2021-09-16 14:57:39, cập nhật trên: 2024-12-04 21:13:08
comments   2
hits   3521

Phân tích lợi nhuận từ chênh lệch giá giữa giá tương lai và giá giao ngay của hợp đồng dựa trên tiền tệ

期现差价回归套利

上次介绍了永续合约资金费率套利(https://www.fmz.com/digest-topic/6381 ), 通过做空永续合约期货,做多等量现货,持续的获取资金费率收益,在大的负溢价情况下,还能赚钱溢价回归收益。在8、9月份的牛市中,年化一度可达100%,是一个比较受欢迎的策略。

数字货币低风险套利中,还有一类策略没有介绍——期现基差回归套利。具体原理:

1.存在期货合约和现货的差价。交割合约的特点是到交割日才按照现货价格结算,因此在离交割日很远时,交割合约的交割非常容易收到市场情绪的影响,如果市场表现不错,更多的人会看好未来的价格,因此出现正溢价,当出现大跌时,往往会有负溢价。但总的来说交割合约的交割不会偏离现货太多,并且总会在交割日回归。

2.存在币本位交割合约,如币安有当季和次季交割合约。币本位交割合约和USDT本位永续合约区别较大,币本位使用币结算,而币价又在不断的变化,接下来将详细介绍。

本文代码可以直接运行,但由于网络原因,爬取数据部分需要科学上网,最好使用自己的电脑运行,或者使用Google的colab。

币本位合约盈亏计算

根据币安文档,收益= 交易方向×成交数量×合约倍数×(1 / 开仓价格- 1 / 平仓价格) = 持仓价值×(1 / 开仓价格- 1 / 平仓价格),做空时持仓价值为负。假如持有100张BTCUSD交割合约,每张价值100USD,开仓价格为10000USD,那么随着价格盈亏的情况如下:

通过计算分析,币本位做空或者做多的收益都是非线性的,以币收益衡量,做空最多亏一个持仓币量,赚币数量无上限,做多最多赚一个持仓币量,亏损则无上限。看起来做空更划算,但做空的收益伴随着币价的降低,按USD算其实没这么高。如果计算USD收益,情况正好相反,做空最多赚一个持仓价值,亏损则是无限的。

同时交易币本位合约,需要原本就持有币,如果考虑USD计价,此时 账户总价值 = (账户币数 + 持仓价值 ×(1 / 开仓价格- 1 / 平仓价格))×现货价格。如果持仓价值 = - 账户币数 × 开仓价格,即一倍杠杆做空,账户总价值 = 账户币数 × 开仓价格 × 平仓时现货价格 / 平仓价格, 考虑到交割合约的价格最终回归于现货价格,最终 账户总价值 = 账户币数 × 开仓价格。也就是说账户价值将以开仓价格锁定,不在随当前价格而改变,而根据分析,即使价格无限上涨,也不会爆仓。这就是套保的原理。一倍杠杆做空相当于把你现货的币以期货的价格提前卖出。

如果期货账户的币是从现货市场上购买的,那么 账户总收益 = 账户币数 × (开仓价格 - 现货购买价格),只要开仓,我们的收益就已经固定,和当前价格无关。其中的差价就是套利的收益。这就是期现套利的原理。

当然大部分情况下我们不必等待到交割日,如果差价降低可以提前平仓,这时候的 账户总收益 = 现货价值 × (开仓期现比/平仓期现比 - 1),只要开仓时期货现货价格比大于平仓时,并且能够覆盖手续费,此时就有收益。

import requests
from datetime import date,datetime
import time
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
value = 100*100 #持仓价值
open_price = 10000 #开仓价格10000USD
long_profit_list = []
short_profit_list = []
long_usdt_profit_list = []
short_usdt_profit_list = []
close_range = range(1000,30000,10)
for p in close_range:
    profit = value*(1/open_price-1/p)
    long_profit_list.append(profit)
    long_usdt_profit_list.append(profit*p)
    short_profit_list.append(-profit)
    short_usdt_profit_list.append(-profit*p)
#币本位计价收益
plt.figure(figsize=(12, 7), dpi=80)
plt.plot(close_range,long_profit_list,label='long');
plt.plot(close_range,short_profit_list);
plt.plot(close_range,[1]*len(close_range),'r--');
plt.plot(close_range,[-1]*len(close_range),'g--');
plt.ylabel('profit');
plt.xlabel('close price');
plt.grid(True)
plt.annotate('Short profit', xy=(4500, 1.5), xytext=(7000, 2.5),arrowprops=dict(facecolor='black'));
plt.annotate('Long profit', xy=(4500, -1.5), xytext=(7000, -2.5),arrowprops=dict(facecolor='black'));             
plt.annotate('Open price', xy=(10000, 0), xytext=(13000, 2.5),arrowprops=dict(facecolor='black'));
#收益用USD计算
plt.figure(figsize=(12, 7), dpi=80)
plt.plot(close_range,long_usdt_profit_list,label='long');
plt.plot(close_range,short_usdt_profit_list);

plt.ylabel('profit');
plt.xlabel('close price');
plt.annotate('Short profit', xy=(5000, 5000), xytext=(7000, 10000),arrowprops=dict(facecolor='black'));
plt.annotate('Long profit', xy=(5000, -5000), xytext=(7000, -10000),arrowprops=dict(facecolor='black'));   
plt.grid(True)

期现套利的步骤

  • 1.实时监控期现溢价的变化,达到设定值后,现货买币并且立即转入期货做空,做空价值为现货数量 × 开仓价格。
  • 2.等待溢价回归,达到设定值后,期货平仓,划转到现货卖出,获得收益。

具体细节

  • 1.不同交割日期的溢价有不同意义,如当季溢价5%和次季溢价5%,肯定优先选择当季进行套利。需要根据交割日期计算相应的年化。
  • 2.手续费需要考虑,牵扯到现货买入卖出,期货的开仓平仓,总共交易4次。
  • 3.期货和现货的交易要同时进行,才能锁定溢价,为避免市场冲击,可以分次多笔减仓。
  • 4.当期货账户里有币,可以直接进行并发开仓套利,而不用等待划转。同理现货的币也不用完全转入期货,方便平仓并发。
  • 5.要监控所有交易对,哪个有机会,溢价高就操作哪个。
  • 6.平仓的选择很重要,可以按照阶梯平仓,0溢价或者负溢价平完。

历史溢价变化

以币安交易所交割数据数据为例,共存在 BTCUSDT,ETHUSDT,ADAUSDT,LINKUSDT,BCHUSDT,DOTUSDT,XRPUSDT,LTCUSDT,BNBUSDT 9个交易对可以用于套利交易。这里选取ETH具体分析交割合约与现货的溢价变化。

今年以来ETH从600U起步,最高涨到5月份的4000U,然后跌倒6月、7月的2000U一下,最近又重新回到3500U,行情大起。考察到期日为210625,210924,211231的三个交割合约,其中210625溢价长期维持在8%,如果10%开始套利,6%平仓,4个月大概有4次机会,年化在50%以上。210924溢价最高15%以上,现在已经回归,还有较长时间到期的211231,最高溢价5%。可见只要耐心等待,ETH上套利机会很多。

读者可以自行更改交易对,情况基本类似,总的来说,今年1-4月份是个很好的区间。

## 当前交易对
Info = requests.get('https://dapi.binance.com/dapi/v1/exchangeInfo')
symbols = [s['symbol'] for s in Info.json()['symbols']]
symbols_nq = list(filter(lambda x:x.split('_')[-1]=='211231', symbols)) #次季合约
symbols_q = list(filter(lambda x:x.split('_')[-1]=='210924', symbols))  #当季合约
symbols_s = [s.split('_')[0]+'T' for s in symbols_nq] # 现货交易对
','.join(symbols_s)
def GetKlines(symbol='BTCUSDT',start='2020-8-10',end='2021-8-10',period='1h',base='fapi',v = 'v1'):
    Klines = []
    start_time = int(time.mktime(datetime.strptime(start, "%Y-%m-%d").timetuple()))*1000 + 8*60*60*1000
    end_time = int(time.mktime(datetime.strptime(end, "%Y-%m-%d").timetuple()))*1000 + 8*60*60*1000
    intervel_map = {'m':60*1000,'h':60*60*1000,'d':24*60*60*1000}
    while start_time < end_time:
        mid_time = min(start_time+1000*int(period[:-1])*intervel_map[period[-1]],end_time)
        url = 'https://'+base+'.binance.com/'+base+'/'+v+'/klines?symbol=%s&interval=%s&startTime=%s&endTime=%s&limit=1000'%(symbol,period,start_time,mid_time)
        res = requests.get(url)
        res_list = res.json()
        if type(res_list) == list and len(res_list) > 0:
            start_time = res_list[-1][0]
            Klines += res_list
        elif type(res_list) == list:
            start_time = start_time+1000*int(period[:-1])*intervel_map[period[-1]]
        else:
            print(url)
    df = pd.DataFrame(Klines,columns=['time','open','high','low','close','amount','end_time','volume','count','buy_amount','buy_volume','null']).astype('float')
    df.index = pd.to_datetime(df.time,unit='ms')
    return df
symbol = 'ETH'
df_s = GetKlines(symbol=symbol+'USDT',start='2020-12-26',end='2021-9-15',period='1h',base='api',v='v3')
df_nq = GetKlines(symbol=symbol+'USD_211231',start='2021-6-26',end='2021-9-15',period='1h',base='dapi')
df_q = GetKlines(symbol=symbol+'USD_210924',start='2021-3-26',end='2021-9-15',period='1h',base='dapi')
df_lq = GetKlines(symbol=symbol+'USD_210625',start='2020-12-26',end='2021-6-24',period='1h',base='dapi') 
# 现货价格
df_s.close.dropna().plot(figsize=(16,6),grid=True);
# 上季合约溢价
(100*(df_lq.close-df_s.close)/df_s.close).dropna().plot(figsize=(16,6),grid=True);
# 当季合约的溢价
(100*(df_q.close-df_s.close)/df_s.close).dropna().plot(figsize=(16,6),grid=True);
# 次季合约溢价
(100*(df_nq.close-df_s.close)/df_s.close).dropna().plot(figsize=(16,6),grid=True);

当前交易机会

由于210924合约即将到期,这里主要考察还有3个月到期的211231,目前基本溢价在3%附近,最高的溢价是5%,可以说机会不是特别好,但210924到期后,将产生新的次季合约,距离交割时长6个月,会有更多的机会。

df_all = pd.DataFrame(index=pd.date_range(start='2021-6-26', end='2021-9-16', freq='1H'),columns=symbols_s)
for i in range(len(symbols_nq)):
    symbol_nq = symbols_nq[i]
    symbol_s = symbols_s[i]
    df_s = GetKlines(symbol=symbol_s,start='2021-6-26',end='2021-9-16',period='1h',base='api',v='v3')
    df_nq = GetKlines(symbol=symbol_nq,start='2021-6-26',end='2021-9-16',period='1h',base='dapi')
    df_all[symbol_s] = (100*(df_nq.close-df_s.close)/df_s.close).drop_duplicates()
df_all.dropna().plot(figsize=(16,10),grid=True);

总结

本文主要介绍了利用交割合约和现货之间差价的回归进行套利交易。这种套利方式是一种普遍的交易行为,有着很多优势:

  • 1.风险低,由于1倍杠杆做空不会爆仓,所以即使溢价扩大也没有风险,几乎是无风险套利。
  • 2.确定性高,交割合约的价格总会回归现货,套利完成后,不受当前价格涨跌的影响,最差的情况是拿到交割也会获取收益。
  • 3.操作原理简单,可以交易币种多,适合相对大一些的资金追求稳定的收益。
  • 4.收益不低,有时候会出现很深的负溢价,如果运气好掌握好节奏,利润非常可观。

主要风险:

  • 1.溢价如果长时间增长,会有较长时间的浮亏,此时平仓离场将会产生实际的亏损。
  • 2.交易所API故障,单腿交易。
  • 3.交易合约流动性差,且同类型策略太多,造成滑点过大,侵蚀利润。