Files
strategies/币安永续多币种对冲策略做空或做多山寨币指数4月10日改进Bug需要更新.md
2023-07-14 18:39:48 +08:00

285 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> 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),最近一周山寨币相对于比特币是上涨的,因此亏损,如果你看多山寨币,可以在参数中设置做空比特币做多山寨币:
**策略默认是做多比特币做空山寨币,你也可以反过来(假如你认为山寨币处于底部),决定权在自己**
![IMG](https://www.fmz.com/upload/asset/24281c6de45544ca2b7.png)
## 策略逻辑
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代表保证金已用占比,不宜过高。
## 策略参数
![IMG](https://www.fmz.com/upload/asset/2c9e5e0e4c30f9eaada.png)
- 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)
``` 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