Files
strategies/币安永续多币种对冲策略原版做多超跌做空超涨4月13日最新止损模块.md
T
2023-07-14 18:39:48 +08:00

410 lines
21 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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月13日最新止损模块
> Author
小草
> Strategy Description
此策略收费升级版已经可以使用,加微信 wangweibing_ustb 具体了解
## **重要内容!!**
- 一定要先看这篇研究 https://www.fmz.com/digest-topic/5294 。了解策略原理、风险、交易对如何筛选、如何设置参数、开仓和总资金的比例等等一系列问题。
- 上一篇研究报告需要下载并上传到自己的研究环境中。实际修改运行一遍。如果你已经看过这篇报告,最近又更新了最新一周的数据。
- 机器人停了很久再开,一定要重置数据,或者新建机器人
- **策略不能直接回测,需要在研究环境中回测**。
- 策略代码以及默认参数仅供研究,实盘运行需要谨慎,根据自己的研究决定参数,**自担风险**。
- **策略不可能每天盈利,可看回测历史,1-2周的横盘和回撤都是正常的,需要正确对待**
- 代码是公开的,可以自行修改,如果有任何问题,欢迎评论反馈,最好加入发明者币安交流群(研究报告中有加入方法),可获得更新通知
- **策略仅支持币安期货,需要在全仓模式运行,不要设置双向持仓!!,创建机器人时用默认交易对和K线周期即可,策略没有用到K线**
- **策略和其他策略以及手动操作是冲突的,需要注意**
- 实盘运行需要海外托管者,测试阶段可在平台一键租用阿里云香港服务器,自己按月整租更便宜(配置最低的即可,部署教程:https://www.fmz.com/bbs-topic/2848)
- 币安的期货和现货需要单独添加,币安期货为``Futures_Binance``
- 此策略重启不影响,但是新建机器人会重新记录历史数据。
- 策略根据用户反馈可能会有更新,直接ctrl+A复制代码覆盖保存(一般不会更新参数),重启机器人即可用上最新代码。
- 策略一开始并不会交易,第一次启动需要记录数据,需要等待行情的变化才会交易
## 4.16日更新内容
更改了止损的bug
修改了默认参数:
```
var Alpha = 0.001 //指数移动平均的Alpha参数,设置的越大,基准价格跟踪越敏感,最终持仓也会越低,降低了杠杆,但会降低收益,具体需要根据回测结果自己权衡
var Update_base_price_time_interval = 60 //多久更新一次基准价格, 单位秒,和Alpha参数相关,Alpha 设置的越小,这个间隔也可以设置的更小
```
## 4.13 更新内容
Stop_loss设置为0.8表示当资金达到低于初始资金的80%时,止损,清空所有仓位,停止策略。随着策略运行,Stop_loss可以设置大于1(重启生效),比如从1000赚到1500,Stop_loss设置为1.3,则回撤到1300元止损。不想止损可以把这个参数设置的很小。风险是大家都用这种止损会形成踩踏,加大亏损。初始资金在状态栏的init_balance字段,注意提现等操作会影响,别不小心止损了。如果还是怕黑天鹅事件,比如某个币归0等,可以手动提现出来。
Max_diff和Min_diff限制了偏离的程度,需要根据自己的trade_value,总资金和风险承受能力自己确定。
举个简单例子,假如一共交易20个币,其中一个币价值逐渐上涨到偏离0.4不再交易,其它币价格一直不变会,亏损trade_value的7倍。不断下跌到偏离到偏离-0.3,将亏损trade_value的6倍。
```
var Stop_loss = 0.8
var Max_diff = 0.4 //当偏差diff大于0.4时,不继续加空仓, 自行设置
var Min_diff = -0.3 //当diff小于-0.3时,不继续加多仓, 自行设置
```
## 4.10 更新内容
**把策略代码复制到本地策略,直接覆盖保存即可,重启机器人生效,还会保持原有的仓位**
币安期货做空超涨做多超跌策略重要优化notebook代码地址:https://www.fmz.com/bbs-topic/5364
原有策略山寨币指数 = mean(sum((山寨币价格/比特币价格)/(山寨币初始价格/比特币初始价格)))。最大的问题是最新价格和策略启动的初始价格对比,随着时间的增长,会越来越偏离,一个币可能持有很多的仓位,风险很高,最后将持有很多仓位,增加了风险和回撤。
最新的山寨币指数 = mean(sum((山寨币价格/比特币价格)/EMA(山寨币价格/比特币价格))),即是和均线的价格进行对比,能够跟踪最新的价格变化,更加灵活,回测发现降低了策略持仓,也减少了回撤。更加稳定。最重要的是原策略如果加入几个异常交易对,风险极高,很可能爆仓,现在却几乎不受影响。
为了无缝升级,其中两个参数写到了策略代码的前两行,按需更改。
Alpha = 0.04 指数移动平局的Alpha参数,设置的越大,基准价格跟踪越敏感,交易的越少,最终持仓也会越低,降低了杠杆,但会降低收益,降低最大回撤,可以加大成交量,具体需要根据回测结果自己权衡。
Update_base_price_time_interval = 30*60 多久更新一次基准价格, 单位秒,和Alpha参数相关,Alpha 设置的越小,这个间隔也可以设置的更小
如果你看了文章,想要交易全币种,这里是列表``ETH,BCH,XRP,EOS,LTC,TRX,ETC,LINK,XLM,ADA,XMR,DASH,ZEC,XTZ,BNB,ATOM,ONT,IOTA,BAT,VET,NEO,QTUM,IOST``
## 加入微信群参加币安千团大战获取更新
加下方微信号,回复“币安”即自动拉入群:
https://www.fmz.com![IMG](https://www.fmz.com/upload/asset/1fbed0c3795dbecac04.jpg)
## 策略原理
将做空价格高于山寨币-比特币价格指数的币种,做多低于指数的币种,偏离越大,仓位越大。(本策略没有把不对等的仓位用BTC对冲,也可以把BTC加入交易对)。最近两个月的表现(3倍左右杠杆,数据更新到4.8):
![IMG](https://www.fmz.com/upload/asset/1b2a6f297fcf590ad02.png)
## 策略逻辑
1.更新行情和账户持仓,第一次运行会记录初始价格(新加入的币种按照加入时间计算)
2.更新指数,指数是山寨币-比特币价格指数 = mean(sum((山寨币价格/比特币价格)/(山寨币初始价格/比特币初始价格)))
3.根据偏离指数判断做多做空,根据偏离大小判断仓位
4.下单,下单量由冰山委托决定,按照对手价成交(买入用卖一价)。**下单后立即撤销(所以会看到很多撤销失败的单子400: {"code":-2011,"msg":"Unknown order sent."},正常现象)**
5.再次循环
状态栏中的leverage代表保证金已用占比,需要保持较低水平以满足新的开仓
## 策略参数
![IMG](https://www.fmz.com/upload/asset/272b24a9c0018c96fb6.png)
- Trade_symbols:交易的币种,需要自己根据研究平台自己筛选,也可以加入BTC
- Trade_value:山寨币价格(BTC计价)每偏离指数1%持有价值,需要根据自己投入的总资金和风险偏好决定,建议设置为总资金的3-10%。可通过研究环境的回测看杠杆的大小,Trade_value可以小于Adjust_value,如Adjust_value的一半,这样相当于偏离指数2%的持有价值。
- Adjust_value: 合约价值(USDT计价)调整偏离值。 当 指数偏离 \* Trade_value - 当前持仓 > Adjust_value,即目标持仓和当前持仓的差超过此值,就会开始交易。过大调整较慢,太小交易频繁,不能低于10,否则会达不到最小成交,推荐设置为Trade_value的40%以上。
- Ice_value:冰山委托价值,同样不能低于10,实际下单选Adjust_value与Ice_value 中较小的那一个,如果资金较多可以设置的相对大一些这样调整的更快,建议不低于Adjust_value的20%,这样5次冰山就能完成成交,当然,Trade_value不大时,Ice_value可以设置的相对大一下,一两次就能调整到位。
- Interval:循环休眠时间,可以设置的小一些,如1s,但不能超过币安频率限制。
- Reset:重置历史数据,会将策略参考的初始价格重新设置为当前的价格,一般不用设置
## 策略风险
注意到如果某个币走出了独立的行情,比如相对于指数上涨了几倍,将会在该币种上积累大量的做空仓位,同样的大幅下跌也会使得策略大量做多。可限制开仓量或者止损不再交易。
> Strategy Arguments
|Argument|Default|Description|
|----|----|----|
|Trade_symbols|QTUM,DASH,ADA,BNB,XMR,ZEC,ATOM,IOTA,NEO,ONT,XRP,BAT,VET,EOS,ETC,LTC|交易的币种|
|Trade_value|20|每偏离指数1%持有价值|
|Adjust_value|20|合约价值调整偏离值|
|Ice_value|20|冰山委托订单的大小|
|Log_profit_interval|600|Log总权益间隔s|
|Interval|true|休眠时间s|
|Reset|false|重置历史数据|
> Source (javascript)
``` javascript
var Alpha = 0.001 //指数移动平均的Alpha参数,设置的越大,基准价格跟踪越敏感,最终持仓也会越低,降低了杠杆,但会降低收益,具体需要根据回测结果自己权衡
var Update_base_price_time_interval = 60 //多久更新一次基准价格, 单位秒,和Alpha参数相关,Alpha 设置的越小,这个间隔也可以设置的更小
//Stop_loss设置为0.8表示当资金达到低于初始资金的80%时,止损,清空所有仓位,停止策略。
//随着策略运行,Stop_loss可以设置大于1(重启生效),比如从1000赚到1500,Stop_loss设置为1.3,则回撤到1300元止损。不想止损可以把这个参数设置的很小。
//风险是大家都用这种止损会形成踩踏,加大亏损。
//初始资金在状态栏的init_balance字段,注意提现等操作会影响,别不小心止损了。
//如果还是怕黑天鹅事件,比如某个币归0等,可以手动提现出来。
var Stop_loss = 0.8
var Max_diff = 0.4 //当偏差diff大于0.4时,不继续加空仓, 自行设置
var Min_diff = -0.3 //当diff小于-0.3时,不继续加多仓, 自行设置
if(IsVirtual()){
throw '不能回测,回测参考 https://www.fmz.com/digest-topic/5294 '
}
if(exchange.GetName() != 'Futures_Binance'){
throw '只支持币安期货交易所,和现货交易所不同,需要单独添加,名称为Futures_Binance'
}
var trade_symbols = Trade_symbols.split(',')
var symbols = trade_symbols
var index = 1 //指数
if(trade_symbols.indexOf('BTC')<0){
symbols = trade_symbols.concat(['BTC'])
}
var update_profit_time = 0
var update_base_price_time= Date.now()
var assets = {}
var init_prices = {}
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,
btc_price:0, btc_change:1,btc_diff: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, update_time:0, init_balance:0, stop_balance:0, short_value:0, long_value:0, profit:0}
function updateAccount(){ //更新账户和持仓
exchange.SetContractType('swap')
var account = exchange.GetAccount()
var pos = exchange.GetPosition()
if (!account || !pos){
Log('update account time out')
return
}
assets.USDT.update_time = Date.now()
for(var i=0; i<trade_symbols.length; i++){
assets[trade_symbols[i]].margin = 0
assets[trade_symbols[i]].unrealised_profit = 0
assets[trade_symbols[i]].hold_price = 0
assets[trade_symbols[i]].amount = 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(trade_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)
if(assets.USDT.init_balance == 0){
if(_G('init_balance')){
assets.USDT.init_balance = _N(_G('init_balance'),2)
}else{
assets.USDT.init_balance = assets.USDT.total_balance
_G('init_balance',assets.USDT.init_balance)
}
}
assets.USDT.profit = _N(assets.USDT.margin_balance - assets.USDT.init_balance, 2)
assets.USDT.stop_balance = _N(Stop_loss*assets.USDT.init_balance, 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(trade_symbols.indexOf(coin) < 0){continue}
if(pos[k].positionSide != 'BOTH'){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 updateIndex(){ //更新指数
if(!_G('init_prices') || Reset){
Reset = false
for(var i=0; i<trade_symbols.length; i++){
init_prices[trade_symbols[i]] = (assets[trade_symbols[i]].ask_price+assets[trade_symbols[i]].bid_price)/(assets.BTC.ask_price+assets.BTC.bid_price)
}
Log('保存启动时的价格')
_G('init_prices',init_prices)
}else{
init_prices = _G('init_prices')
if(Date.now() - update_base_price_time > Update_base_price_time_interval*1000){
update_base_price_time = Date.now()
for(var i=0; i<trade_symbols.length; i++){ //更新初始价格
init_prices[trade_symbols[i]] = init_prices[trade_symbols[i]]*(1-Alpha)+Alpha*(assets[trade_symbols[i]].ask_price+assets[trade_symbols[i]].bid_price)/(assets.BTC.ask_price+assets.BTC.bid_price)
}
_G('init_prices',init_prices)
}
var temp = 0
for(var i=0; i<trade_symbols.length; i++){
assets[trade_symbols[i]].btc_price = (assets[trade_symbols[i]].ask_price+assets[trade_symbols[i]].bid_price)/(assets.BTC.ask_price+assets.BTC.bid_price)
if(!(trade_symbols[i] in init_prices)){
Log('添加新的币种',trade_symbols[i])
init_prices[trade_symbols[i]] = assets[trade_symbols[i]].btc_price / index
_G('init_prices',init_prices)
}
assets[trade_symbols[i]].btc_change = _N(assets[trade_symbols[i]].btc_price/init_prices[trade_symbols[i]],4)
temp += assets[trade_symbols[i]].btc_change
}
index = _N(temp/trade_symbols.length, 4)
}
}
function updateTick(){ //更新行情
var ticker = HttpQuery('https://fapi.binance.com/fapi/v1/ticker/bookTicker')
try {
ticker = JSON.parse(ticker)
}catch(e){
Log('get ticker time out')
return
}
assets.USDT.short_value = 0
assets.USDT.long_value = 0
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)
if(trade_symbols.indexOf(coin) < 0){continue}
if(assets[coin].amount<0){
assets.USDT.short_value += Math.abs((assets[coin].ask_value+assets[coin].bid_value)/2)
}else{
assets.USDT.long_value += Math.abs((assets[coin].ask_value+assets[coin].bid_value)/2)
}
assets.USDT.short_value = _N(assets.USDT.short_value,0)
assets.USDT.long_value = _N(assets.USDT.long_value,0)
}
updateIndex()
for(var i=0; i<trade_symbols.length; i++){
assets[trade_symbols[i]].btc_diff = _N(assets[trade_symbols[i]].btc_change - index, 4)
}
}
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)+1)
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) //订单会立即撤销
}
return 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, assets[symbols[i]].btc_diff, 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) + ' Index:' + index + '\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 stopLoss(){ //止损函数
while(true){
if(assets.USDT.margin_balance < Stop_loss*assets.USDT.init_balance && assets.USDT.init_balance > 0){
Log('触发止损,当前资金:', assets.USDT.margin_balance, '初始资金:', assets.USDT.init_balance)
Ice_value = 200 //止损的快一些,可修改
updateAccount()
updateTick()
var trading = false //是否正在交易
for(var i=0; i<trade_symbols.length; i++){
var symbol = trade_symbols[i]
if(assets[symbol].ask_price == 0){ continue }
if(assets[symbol].bid_value >= trade_info[symbol].minQty*assets[symbol].bid_price){
trade(symbol, 'sell', assets[symbol].bid_value)
trading = true
}
if(assets[symbol].ask_value <= -trade_info[symbol].minQty*assets[symbol].ask_price){
trade(symbol, 'buy', -assets[symbol].ask_value)
trading = true
}
}
Sleep(1000)
if(!trading){
throw '止损结束,如果需要重新运行策略,需要调低止损'
}
}else{ //不用止损
return
}
}
}
function onTick(){ //策略逻辑部分
for(var i=0; i<trade_symbols.length; i++){
var symbol = trade_symbols[i]
if(assets[symbol].ask_price == 0){ continue }
var aim_value = -Trade_value * _N(assets[symbol].btc_diff/0.01,3)
if(aim_value - assets[symbol].ask_value >= Adjust_value && assets[symbol].btc_diff > Min_diff && assets.USDT.long_value-assets.USDT.short_value <= 1.1*Trade_value){
trade(symbol,'buy', aim_value - assets[symbol].ask_value)
}
if(aim_value - assets[symbol].bid_value <= -Adjust_value && assets[symbol].btc_diff < Max_diff && assets.USDT.short_value-assets.USDT.long_value <= 1.1*Trade_value){
trade(symbol,'sell', -(aim_value - assets[symbol].bid_value))
}
}
}
function main() {
while(true){
updateAccount()
updateTick()
stopLoss() //止损
onTick()
updateStatus()
Sleep(Interval*1000)
}
}
```
> Detail
https://www.fmz.com/strategy/195226
> Last Modified
2020-08-20 16:03:49