大家好!我是币圈玖哥,匆匆忙忙进场,匆匆忙忙进厂…提醒本文不构成任何投资建议,只是作为学习分享,本期给大家带来的策略是:期限套利,套取永续合约的资金费率,由于风险偏低,适合新手使用!
永续合约是一种在加密货币交易市场内特殊的期货合约,并不存在到期交割日,为了维持永续合约与现货价格之间不要脱钩,所以引入了「资金费率」的机制,让没有交割日的永续合约与现货间的价差能够借此收敛在一起,不脱钩太多。
资金费率的设计是为了让期货价格不要无限偏离现货价格太远,当市场中做多情绪浓厚时,做多方需要付资金费率给做空方,依此让使期货价格偏离现货价格的做多方有一个持仓的成本,反之若做空情绪浓厚则由做空方支付资金费率给做多方。
资金费率并不是交易者付给交易所的费用,而是在永续合约上做多以及做空两方对手方需要互相给付的持仓费用。而在加密货币市场这个特殊的机制背景下,产生出了新的套利机会:
永续合约的基础费率为每八小时 0.01% ,按照市场情况会在 -0.75% ~ 0.75% 之间变化。
以 BTC 来说,如果使用10000 USDT进行期现套利,假设当 BTC 为 10000 USDT 时,可以进行以下操作:
为了提高整体的资金利用率,我们能妥善的在合约市场中使用杠杆,如果使用 2 倍杠杆,则一样是使用上述的资金进行套利,现货市场可以买入 6666.6 USDT 的 BTC(0.666 BTC),在合约市场做空 0.666 BTC,因为使用 2 倍杠杆,此时保证金仅需要 3333.3 USDT。 若当前的费率为 0.05% 且 BTC 价格为 10000 USDT,那么将可以收到 0.666 * 10000 * 0.05% = 3.333 USDT 的资金费用,较没有使用杠杆多获得 33% 的资金费率。 在 2 倍槓杆的情况下,年化提升到 36.46%;在 3 倍杠杆的情况下,年化提升到 41.0625% 根据上方的步骤,就能在任何一个同时提供现货以及永续合约交易市场的交易所进行套利。
期现套利的原理非常简单,许多交易者也会直接手动进行期现套利,但市场变化多端,手动进行套利将藏着以下风险:
我们使用CCXT框架,不熟悉CCXT可以看看我之前的文章,这里不再赘述。
导入包
import json
import operator
import os
from tqdm import tqdm
import requests
import pandas as pd
# 代理proxy
os.environ["http_proxy"] = "http://127.0.0.1:21882"
os.environ["https_proxy"] = "http://127.0.0.1:21882"
# 连接交易所
exchange_id = "okx"
exchange_class = getattr(ccxt, exchange_id)
exchange = exchange_class({
'apiKey': 'your api Key',
'secret': 'your secret',
'password': 'your password',
'enableRateLimit': True
})
coin_rate = {}
# 获取一个币种的资金费率
print(exchange.fetchFundingRate("BTC/USDT:USDT"))
# 这里没有ccxt提供的函数,我对照api手写了一个函数
# 获取币种的代码
def fetchSwapMarket():
url = f"https://www.okex.com/api/v5/market/tickers?instType=SWAP"
res = requests.get(url)
json_res = json.loads(res.content)
list_ = []
if json_res["code"] == "0":
for i in json_res["data"]:
list_.append(i["instId"])
return list_
def findMaxfee():
swap_coin = fetchSwapMarket()
print("正在寻找目标币种,请等待!")
# 总进度
total = len(swap_coin)
with tqdm(total=total) as pbar:
pbar.set_description('Processing:')
for i, coin in enumerate(swap_coin):
coin_rate[coin] = exchange.fetchFundingRate(coin)
pbar.update(1)
我们可以看到下一个时间XCH-USDT-SWAP的负资金费率最高
核心:这两笔交易行情相关、方向相反、数量相当、盈亏相抵
MidClass中间类,封装了交易币种的相关信息
Strategy.py
class MidClass():
# 初始化
def __init__(self, ThisExchange, symbol):
self.Exchange = ThisExchange
self.Symbol = symbol
market = ThisExchange.markets[self.Symbol]
self.MinLeverage = market['limits']['leverage']['min']
self.MaxLeverage = market['limits']['leverage']['max']
self.Price_Precision = market['precision']['price']
self.AmountPrecision = len(str(market['precision']['amount']).split(".")[-1])
self.PricePrecision = len(str(market['precision']['price']).split(".")[-1])
self.TakerFee = ThisExchange.markets[symbol]["taker"]
self.MakerFee = ThisExchange.markets[symbol]["maker"]
self.LimitAmount = market['limits']['amount']['min']
# 获得交易对行情信息
def GetTicker(self):
self.High = '___'
self.Low = '___'
self.Buy = '___'
self.Sell = '___'
self.Last = '___'
try:
self.Ticker = self.Exchange.fetchTicker(self.Symbol)
self.Time = self.Ticker["timestamp"]
self.datetime = self.Ticker["datetime"]
self.High = self.Ticker['high']
self.Low = self.Ticker['low']
self.Buy = self.Ticker['bid']
self.Sell = self.Ticker['ask']
self.Last = self.Ticker['last']
return True # 只要有一个成功就返回True
except:
return False # 如果全都获取不了返回False
@staticmethod
def IsSpot(Symbol):
if len(Symbol.split(':')) == 1:
return True
else:
return False
# 获得账户对于该交易对信息 只显示交易过的币种
def GetAccount(self):
self.Account = '___'
self.Balance = '___'
self.FrozenBalance = '___'
self.Stocks = '___'
self.FrozenStocks = '___'
if MidClass.IsSpot(self.Symbol):
self.SymbolStocksName = self.Symbol.split('/')[0]
self.SymbolBalanceName = self.Symbol.split('/')[1]
else:
self.SymbolStocksName = self.Symbol.split(":")[0].split("/")[0]
self.SymbolBalanceName = self.Symbol.split(":")[-1]
try:
self.Account = self.Exchange.fetchBalance()
self.Balance = self.Account[self.SymbolBalanceName]['free']
self.FrozenBalance = self.Account[self.SymbolBalanceName]['used']
self.Stocks = self.Account[self.SymbolStocksName]['free']
self.FrozenStocks = self.Account[self.SymbolStocksName]['used']
return True
except Exception as e:
print("账户没有交易过这个币种!")
return False
# 确认是否获取到账户和交易对信息
def RefreshData(self):
if not self.GetAccount():
return 'false get account'
if not self.GetTicker():
return 'false get ticker'
return 'refresh data finish!'
# 创建订单
def CreateOrder(self, OrderType, Price, Amount):
if round(Amount, self.AmountPrecision) < self.LimitAmount:
raise RuntimeError("下单数量小于最小下单量,请加大保证金,或者提高杠杆!")
if OrderType == 'buy':
# 执行买单杠杆交易
params = {
"tdMode": "isolated",
}
OrderId = self.Exchange.create_order(self.Symbol, type="limit", side="buy",
amount=round(Amount, self.AmountPrecision),
price=round(Price, self.PricePrecision), params=params)["id"]
elif OrderType == 'sell':
# 执行卖单杠杆交易
params = {
"tdMode": "isolated",
}
OrderId = self.Exchange.create_order(self.Symbol, type="limit", side="sell",
amount=round(Amount, self.AmountPrecision),
price=round(Price, self.PricePrecision), params=params)["id"]
elif OrderType == 'short':
# 执行开空合约
params = {
"tdMode": "isolated",
"posSide": "short"
}
OrderId = self.Exchange.create_order(self.Symbol, type="limit", side="sell",
amount=round(Amount, self.AmountPrecision),
price=round(Price, self.PricePrecision), params=params)["id"]
elif OrderType == 'long':
# 执行开多合约
params = {
"tdMode": "isolated",
"posSide": "long"
}
OrderId = self.Exchange.create_order(self.Symbol, type="limit", side="buy",
amount=round(Amount, self.AmountPrecision),
price=round(Price, self.PricePrecision), params=params)["id"]
else:
pass
self.GetAccount()
return OrderId
# 获取订单状态
def GetOrder(self, Idd):
self.OrderId = '___'
self.OrderPrice = '___'
self.OrderNum = '___'
self.OrderDealNum = '___'
self.OrderAvgPrice = '___'
self.OrderStatus = '___'
try:
self.Order = self.Exchange.fetchOrder(Idd, self.Symbol)
self.OrderId = self.Order['id']
self.OrderPrice = self.Order['price']
self.OrderNum = self.Order['amount']
self.OrderDealNum = self.Order['filled']
self.OrderAvgPrice = self.Order['average']
self.OrderStatus = self.Order['status']
return True
except:
return False
# 取消订单
def CancelOrder(self, Idd):
self.CancelResult = '___'
try:
self.CancelResult = self.Exchange.cancelOrder(Idd, self.Symbol)
return True
except:
return False
# 获取k线数据
def GetRecords(self, Timeframe='1m'):
self.Records = '___'
try:
self.Records = self.Exchange.fetchOHLCV(self.Symbol, Timeframe)
return True
except:
return False
# 设置杠杆mgnMode:isolated/cross
def SetLeverage(self, leverage, params=None):
if MidClass.IsSpot(self.Symbol):
if params is None:
params = {"mgnMode": "isolated"}
else:
if params is None:
params = {"mgnMode": "isolated", "posSide": "long"}
try:
if leverage <= self.MaxLeverage and leverage >= self.MinLeverage:
self.LeverageInfo = self.Exchange.set_leverage(leverage, self.Symbol, params=params)
return True
except RuntimeError as e:
print(e, "请注意杠杆倍数,不能超过交易所设定倍数!")
return False
Untils.py
def ShowInfo(MyMid):
print("交易所时间:",MyMid.Time)
print("交易所时间:",MyMid.datetime)
print(MyMid.Symbol, '最新价:', MyMid.Last)
print('该币种可用额度为:', round(MyMid.Stocks, 2), MyMid.SymbolStocksName)
print('该币种冻结额度为:', round(MyMid.FrozenStocks, 2), MyMid.SymbolStocksName)
print('账户可用额度为:', round(MyMid.Balance, 2), 'USDT')
print('账户冻结额度为:', round(MyMid.FrozenBalance, 2), 'USDT')
print('该币种taker手续费', MyMid.TakerFee)
print('该币种Maker手续费', MyMid.MakerFee)
print('-'*40)
运行入口
import os
import ccxt
from MMQT.MidClass import MidClass
from MMQT.Untils import *
# 代理proxy
os.environ["http_proxy"] = "http://127.0.0.1:21882"
os.environ["https_proxy"] = "http://127.0.0.1:21882"
# 连接交易所
exchange_id = "okx"
exchange_class = getattr(ccxt, exchange_id)
exchange = exchange_class({
'apiKey': 'your apikey',
'secret': 'your secret',
'password': 'password',
'enableRateLimit': True
})
if __name__ == '__main__':
exchange.load_markets()
# 中间模块实例化
MyMid1 = MidClass(exchange, symbol="XCH/USDT:USDT")
MyMid2 = MidClass(exchange, symbol="XCH/USDT")
# # 数据更新
MyMid1.RefreshData()
MyMid2.RefreshData()
# 显示相关数据
ShowInfo(MyMid1)
ShowInfo(MyMid2)
# 设置杠杆
leverage = 5
MyMid1.SetLeverage(leverage)
MyMid2.SetLeverage(leverage)
# 设置资金
totalMoney = MyMid1.Balance * leverage
# # 开多合约
price1 = (MyMid1.Buy+MyMid1.Sell)/2
Amount = totalMoney*0.4 / (price1*MyMid1.Price_Precision)
MyMid1.CreateOrder("long", price1, Amount)
# 卖空现货
price2 = (MyMid2.Buy + MyMid2.Sell) / 2
Amount_ = totalMoney * 0.4 / MyMid2.Last
MyMid2.CreateOrder("sell", price2, Amount_)
人在币圈混,哪有不湿鞋。最后再强调下这个策略潜在的风险:
上述代码,实现了选币,一键买入,卖出,增加杠杆的功能,但还有很多潜在的模块没有开发,切不可直接实盘操作,存在很大的风险纰漏。
本文只做学习交流,不构成任何投资建议。
如果你认为本文对你有帮助的话,可以给作者点个再走呗!