Name
币安永续多币种对冲策略做空或做多山寨币指数4月10日改进Bug需要更新
Author
小草
Strategy Description
- 一定要先看这篇研究 https://www.fmz.com/digest-topic/5294 。了解策略原理、风险、交易对如何筛选、如何设置参数、开仓和总资金的比例等等一系列问题。
- 上一篇研究报告需要下载并上传到自己的研究环境中。实际修改运行一遍。如果你已经看过这篇报告,最近又更新了最新一周的数据。
- 策略不能直接回测,需要在研究环境中回测。
- 策略代码以及默认参数仅供研究,实盘运行需要谨慎,根据自己的研究决定参数,自担风险。
- 策略不可能每天盈利,可看回测历史,1-2周的横盘和回撤都是正常的,并且有可能回测很大,需要正确对待
- 代码是公开的,可以自行修改,如果有任何问题,欢迎评论反馈,最好加入发明者币安交流群(研究报告中有加入方法),可获得更新通知
- 策略需要在全仓模式运行,不要设置双向持仓,策略只支持币安期货,创建机器人时用默认交易对和K线周期即可,策略没有用到K线
- 策略和其他策略以及手动操作是冲突的,需要注意
- 实盘运行需要海外托管者,测试阶段可在平台一键租用阿里云香港服务器,自己按月整租更便宜(配置最低的即可,部署教程: https://www.fmz.com/bbs-topic/2848 )
- 币安的期货和现货需要单独添加,币安期货为
Futures_Binance
策略将分散等值做空选定的一篮子山寨币,同时等仓位做多比特币对冲,降低风险和波动率。随着价格的波动,不断调整仓位保持空头价值恒定和多头仓位对等。本质上时做空山寨币-比特币价格指数。最近两个月的表现(3倍左右杠杆,数据更新到4.8),最近一周山寨币相对于比特币是上涨的,因此亏损,如果你看多山寨币,可以在参数中设置做空比特币做多山寨币:
策略默认是做多比特币做空山寨币,你也可以反过来(假如你认为山寨币处于底部),决定权在自己
1.更新行情和账户持仓 2.更新各个山寨币空头持仓价值,判断空头是否需要调整持仓 3.更新总空头的仓位,确定多头持仓,判断多头仓位是否要调整 4.下单,下单量由冰山委托决定,按照对手价成交(买入用卖一价)。下单后立即撤销(所以会看到很多撤销失败的单子400: {"code":-2011,"msg":"Unknown order sent."},正常现象) 5.再次循环
会判断Short_symbols,Long_symbols那个交易对多,多的每个币种开仓价值为Trade_value,少的每个币种合约价值为需要对冲价值的平均。
如果只空BTC,多TRX,DASH,ONT,QTUM,Trade_value为50,则TRX,DASH,ONT,QTUM均有多仓50,BTC持有空仓50*4。
如果只多BTC,空TRX,DASH,ONT,QTUM,Trade_value为50,则TRX,DASH,ONT,QTUM均有空仓50,BTC持有多仓50*4。
状态栏中的leverage代表保证金已用占比,不宜过高。
- Short_symbols:做空的币种,用“ , ”隔开
- Long_symbols:做多的币种,也可以留空,不对冲,直接裸空。
- Trade_value:单个币种做空持有价值。还要做多对冲,总价值=2*Trade_value*做空币种数。一般使用3-5倍杠杆,即总价值=3*账户余额。需要根据自己投入的总资金决定,可通过研究环境的回测看杠杆的大小
- Adjust_value: 合约价值(USDT计价)调整偏离值,过大调整较慢,太小手续费过高,根据Trade_value自行决定。不能低于20,否则会达不到最小成交
- Ice_value:冰山委托价值,同样不能低于20,实际下单选Adjust_value与Ice_value 中较小的那一个
当做空的币价上涨,合约价值增加,此时是减仓的,反之盈利是加仓。这使得总的合约价值维持恒定。山寨币是很有可能走出独立的行情的,目前从一年的周期看,山寨币可能处于底部,并且有可能从底部抬升不少。这取决与如何使用,如果你看好山寨币并认为已经到底部,可以方向操作,做多指数。或者你看好某几个币种(不一定是比特币),可以和它们对冲。
Strategy Arguments
Argument | Default | Description |
---|---|---|
Short_symbols | TRX,DASH,ONT,QTUM,BAT,IOST,ADA,ZEC,XMR,NEO,VET,XRP,IOTA,XLM | 做空的交易对 |
Long_symbols | BTC | 做多的交易对 |
Trade_value | 50 | 做空的单个合约价值 |
Adjust_value | 20 | 合约价值调整偏离值 |
Ice_value | 50 | 冰山委托订单的大小 |
Log_profit_interval | 20 | Log总权益间隔s |
Interval | 5 | 休眠时间s |
Source (javascript)
if(IsVirtual()){
throw '不能回测,回测参考 https://www.fmz.com/digest-topic/5294 '
}
if(exchange.GetName() != 'Futures_Binance'){
throw '只支持币安期货交易所,和现货交易所不同,需要单独添加,名称为Futures_Binance'
}
var short_symbols = Short_symbols.split(',')
var long_symbols = Long_symbols.split(',')
if(short_symbols.length == 1 && short_symbols[0] == ''){
short_symbols = []
}
if(long_symbols.length == 1 && long_symbols[0] == ''){
long_symbols = []
}
var symbols = []
for(var i=0; i<short_symbols.length; i++){
if(short_symbols[i]){
symbols.push(short_symbols[i])
}
}
for(var i=0; i<long_symbols.length; i++){
if(long_symbols[i]){
symbols.push(long_symbols[i])
}
}
var update_profit_time = 0
var assets = {}
var trade_info = {}
var exchange_info = HttpQuery('https://fapi.binance.com/fapi/v1/exchangeInfo')
if(!exchange_info){
throw '无法连接币安网络,需要海外托管者'
}
exchange_info = JSON.parse(exchange_info)
for (var i=0; i<exchange_info.symbols.length; i++){
if(symbols.indexOf(exchange_info.symbols[i].baseAsset) > -1){
assets[exchange_info.symbols[i].baseAsset] = {amount:0, hold_price:0, value:0, bid_price:0, ask_price:0, realised_profit:0, margin:0, unrealised_profit:0}
trade_info[exchange_info.symbols[i].baseAsset] = {minQty:parseFloat(exchange_info.symbols[i].filters[1].minQty),
priceSize:parseInt((Math.log10(1.1/parseFloat(exchange_info.symbols[i].filters[0].tickSize)))),
amountSize:parseInt((Math.log10(1.1/parseFloat(exchange_info.symbols[i].filters[1].stepSize))))
}
}
}
assets.USDT = {unrealised_profit:0, margin:0, margin_balance:0, total_balance:0, leverage:0}
function updateAccount(){
var account = exchange.GetAccount()
var pos = exchange.GetPosition()
if (account == null || pos == null ){
Log('update account time out')
return
}
assets.USDT.update_time = Date.now()
for(var i=0; i<symbols.length; i++){
assets[symbols[i]].margin = 0
assets[symbols[i]].unrealised_profit = 0
assets[symbols[i]].hold_price = 0
assets[symbols[i]].amount = 0
assets[symbols[i]].unrealised_profit = 0
}
for(var j=0; j<account.Info.positions.length; j++){
if(account.Info.positions[j].positionSide == 'BOTH'){
var pair = account.Info.positions[j].symbol
var coin = pair.slice(0,pair.length-4)
if(symbols.indexOf(coin) < 0){continue}
assets[coin].margin = parseFloat(account.Info.positions[j].initialMargin) + parseFloat(account.Info.positions[j].maintMargin)
assets[coin].unrealised_profit = parseFloat(account.Info.positions[j].unrealizedProfit)
}
}
assets.USDT.margin = _N(parseFloat(account.Info.totalInitialMargin) + parseFloat(account.Info.totalMaintMargin),2)
assets.USDT.margin_balance = _N(parseFloat(account.Info.totalMarginBalance),2)
assets.USDT.total_balance = _N(parseFloat(account.Info.totalWalletBalance),2)
assets.USDT.unrealised_profit = _N(parseFloat(account.Info.totalUnrealizedProfit),2)
assets.USDT.leverage = _N(assets.USDT.margin/assets.USDT.total_balance,2)
pos = JSON.parse(exchange.GetRawJSON())
if(pos.length > 0){
for(var k=0; k<pos.length; k++){
var pair = pos[k].symbol
var coin = pair.slice(0,pair.length-4)
if(symbols.indexOf(coin) < 0){continue}
assets[coin].hold_price = parseFloat(pos[k].entryPrice)
assets[coin].amount = parseFloat(pos[k].positionAmt)
assets[coin].unrealised_profit = parseFloat(pos[k].unRealizedProfit)
}
}
}
function updateTick(){
var ticker = HttpQuery('https://fapi.binance.com/fapi/v1/ticker/bookTicker')
if(ticker == null){
Log('get ticker time out')
return
}
ticker = JSON.parse(ticker)
for(var i=0; i<ticker.length; i++){
var pair = ticker[i].symbol
var coin = pair.slice(0,pair.length-4)
if(symbols.indexOf(coin) < 0){continue}
assets[coin].ask_price = parseFloat(ticker[i].askPrice)
assets[coin].bid_price = parseFloat(ticker[i].bidPrice)
assets[coin].ask_value = _N(assets[coin].amount*assets[coin].ask_price, 2)
assets[coin].bid_value = _N(assets[coin].amount*assets[coin].bid_price, 2)
}
}
function trade(symbol, dirction, value){
if(Date.now()-assets.USDT.update_time > 10*1000){
Log('更新账户延时,不交易')
return
}
var price = dirction == 'sell' ? assets[symbol].bid_price : assets[symbol].ask_price
var amount = _N(Math.min(value,Ice_value)/price, trade_info[symbol].amountSize)
if(amount < trade_info[symbol].minQty){
Log(symbol, '合约调整偏离价值或冰山委托订单设置过小,达不到最小成交, 至少需要: ', _N(trade_info[symbol].minQty*price,0))
return
}
exchange.IO("currency", symbol+'_'+'USDT')
exchange.SetContractType('swap')
exchange.SetDirection(dirction)
var f = dirction == 'buy' ? 'Buy' : 'Sell'
var id = exchange[f](price, amount, symbol)
if(id){
exchange.CancelOrder(id) //订单会立即撤销
}
}
function updateStatus(){
var table = {type: 'table', title: '交易对信息',
cols: ['币种', '数量', '持仓价格', '当前价格', '持仓价值', '保证金', '未实现盈亏'],
rows: []}
for (var i=0; i<symbols.length; i++){
var price = _N((assets[symbols[i]].ask_price + assets[symbols[i]].bid_price)/2, trade_info[symbols[i]].priceSize)
var value = _N((assets[symbols[i]].ask_value + assets[symbols[i]].bid_value)/2, 2)
var infoList = [symbols[i], assets[symbols[i]].amount, assets[symbols[i]].hold_price, price, value,_N(assets[symbols[i]].margin,3), _N(assets[symbols[i]].unrealised_profit,3)]
table.rows.push(infoList)
}
var logString = _D() + ' ' + JSON.stringify(assets.USDT) + '\n'
LogStatus(logString + '`' + JSON.stringify(table) + '`')
if(Date.now()-update_profit_time > Log_profit_interval*1000){
LogProfit(_N(assets.USDT.margin_balance,3))
update_profit_time = Date.now()
}
}
function onTick(){
var short_value = Trade_value
if(short_symbols.length<long_symbols.length){
short_value = _N(long_symbols.length*Trade_value/short_symbols.length,0)
}
var long_value = Trade_value
if(short_symbols.length>long_symbols.length){
long_value = _N(short_symbols.length*Trade_value/long_symbols.length,0)
}
var symbol = ''
for(var i=0; i<short_symbols.length; i++){
symbol = short_symbols[i]
if(assets[symbol].ask_price == 0){ continue }
if(assets[symbol].bid_value + short_value > Adjust_value){
trade(symbol, 'sell', assets[symbol].bid_value + short_value)
}
if(assets[symbol].ask_value + short_value < -Adjust_value){
trade(symbol, 'buy', -(assets[symbol].ask_value + short_value))
}
}
for(var i=0; i<long_symbols.length; i++){
symbol = long_symbols[i]
if(assets[symbol].ask_price == 0){ continue }
if(assets[symbol].bid_value - long_value > Adjust_value){
trade(symbol, 'sell', assets[symbol].bid_value-long_value)
}
if(assets[symbol].ask_value - long_value < -Adjust_value){
trade(symbol, 'buy', long_value-assets[symbol].ask_value)
}
}
}
function main() {
while(true){
updateAccount()
updateTick()
onTick()
updateStatus()
Sleep(Interval*1000)
}
}
Detail
https://www.fmz.com/strategy/194825
Last Modified
2020-08-04 14:22:07