Name
币安策略二去掉高低分
Author
区班量化
Strategy Description
感谢FMZ公开策略,感谢卫兵支持! 本人做了两项修改: 1、因为很多朋友默认都是10倍,20倍杠杆,而该策略为全仓模式,一个币爆仓,就会被团灭。所以加上了一个Max_amount,单个币种买入量不会超过这个数,防止爆仓 2、如果币池里有超涨超跌,特例独行的币,容易把大家拖下水,整体策略容易失效。因此我计算指数时去掉一个最高分,去掉一个最低分,得出的指数更公平。 等特例独行的币恢复正常,还是可以正常买入的。 注意:因条件所限,本策略未经回测,仅供参考,如有损失,概不负责!
- 一定要先看这篇研究 https://www.fmz.com/digest-topic/5294 。了解策略原理、风险、交易对如何筛选、如何设置参数、开仓和总资金的比例等等一系列问题。
- 上一篇研究报告需要下载并上传到自己的研究环境中。实际修改运行一遍。如果你已经看过这篇报告,最近又更新了最新一周的数据。
- 策略不能直接回测,需要在研究环境中回测。
- 策略代码以及默认参数仅供研究,实盘运行需要谨慎,自担风险。
- 代码是公开的,可以自行修改,如果有任何问题,欢迎评论反馈,最好加入发明者币安交流群(研究报告中有加入方法)
- 策略需要在全仓模式运行,策略支持币安,创建机器人时用默认交易对和K线周期即可,策略没有用到K线
将做空价格高于山寨币-比特币价格指数的币种,做多低于指数的币种,偏离越大,仓位越大。(本策略没有对冲,也可以把BTC加入交易对)。最近两个月的表现(3倍左右杠杆,数据更新到4.8):
1.更新行情和账户持仓,第一次运行会记录初始价格(新加入的币种按照加入时间计算) 2.更新指数,指数是山寨币-比特币价格指数 = mean(sum((山寨币价格/比特币价格)/(山寨币初始价格/比特币初始价格))) 3.根据偏离指数判断做多做空,根据偏离大小判断仓位 4.下单,下单量由冰山委托决定,按照对手价成交(买入用卖一价)。下单后立即撤销(所以会看到很多撤销失败的单子,正常) 5.再次循环
- Trade_symbols:交易的币种,需要自己根据研究平台自己筛选,也可以加入BTC
- Trade_value:山寨币做空持有价值,需要根据自己投入的总资金决定,可通过研究环境的回测看杠杆的大小
- Adjust_value: 合约价值(USDT计价)调整偏离值,过大调整较慢,不能低于20,否则会达不到最小成交
- Ice_value:冰山委托价值,同样不能低于20,实际下单选Adjust_value/Ice_value 中较小的那一个
- Reset:重置历史数据
注意到如果某个币走出了独立的行情,比如相对于指数上涨了几倍,将会在该币种上积累大量的做空仓位,同样的大幅下跌也会使得策略大量做多。可限制开仓量或者止损不再交易。
Strategy Arguments
Argument | Default | Description |
---|---|---|
Trade_symbols | IOST,TRX,XLM,QTUM,DASH,ADA,XMR,ZEC,ATOM,IOTA,NEO,ONT,XRP,VET,EOS,ETC,LTC | 交易的币种 |
Trade_value | 50 | 每偏离指数1%持有价值 |
Adjust_value | 20 | 合约价值调整偏离值 |
Ice_value | 20 | 冰山委托订单的大小 |
Log_profit_interval | 600 | Log总权益间隔s |
Interval | 5 | 休眠时间s |
Reset | false | 重置历史数据 |
Max_amount | 300 | 最大成交量,超过则不买入 |
Source (javascript)
//向上偏离最大的币的索引
var highIndex=0;
//向下偏离最大的币的索引
var lowIndex=0;
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 assets = {}
var trade_info = {}
var exchange_info = HttpQuery('https://fapi.binance.com/fapi/v1/exchangeInfo')
if(!exchange_info){
Log('无法连接网络')
return
}
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}
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<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
assets[trade_symbols[i]].unrealised_profit = 0
}
for(var j=0; j<account.Info.positions.length; j++){
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)
if(pos.length > 0){
pos = JSON.parse(exchange.GetRawJSON())
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 updateIndex(){ //更新指数
var init_prices = {}
if(!_G('init_prices') || Reset){
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')
var temp = 0
highIndex=0;
lowIndex=0;
var highChange;var lowChange;
//本次计算找出最大偏离的高低分
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(!init_prices[trade_symbols[i]]){
Log('添加新的币种',trade_symbols[i])
init_prices[trade_symbols[i]] = assets[trade_symbols[i]].btc_price
_G('init_prices',init_prices)
}
assets[trade_symbols[i]].btc_change = _N(assets[trade_symbols[i]].btc_price/init_prices[trade_symbols[i]],4)
if(i==0){
highChange=assets[trade_symbols[i]].btc_change;
lowChange=assets[trade_symbols[i]].btc_change;
}
if(highChange<assets[trade_symbols[i]].btc_change){
highChange=assets[trade_symbols[i]].btc_change;
highIndex=i;
}
if(lowChange>assets[trade_symbols[i]].btc_change){
lowChange=assets[trade_symbols[i]].btc_change;
lowIndex=i;
}
}
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)
assets[trade_symbols[i]].btc_change = _N(assets[trade_symbols[i]].btc_price/init_prices[trade_symbols[i]],4)
if(i!=lowIndex&&i!=highIndex){ //去掉高低分的影响
temp += assets[trade_symbols[i]].btc_change
}
}
//因为去掉了最高最低分,所以减2
index = _N(temp/(trade_symbols.length-2), 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
}
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)
}
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))
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: ['Symbol', 'amount', 'hold_price', 'price', 'diff', 'value', 'margin', 'unrealised_profit'],
rows: []}
var infoList;
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)
if(i==lowIndex){
infoList = [symbols[i]+"Low", 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)]
}else if(i==highIndex){
infoList = [symbols[i]+"High", 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)]
}else{
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 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,1)
if(i!=lowIndex&&i!=highIndex){ //高低分的货币不交易
if(aim_value - assets[symbol].ask_value > Adjust_value&&assets[symbol].ask_value<Max_amount){
trade(symbol,'buy', aim_value - assets[symbol].ask_value)
}
if(aim_value - assets[symbol].bid_value < -Adjust_value&&assets[symbol].bid_value<Max_amount){
trade(symbol,'sell', -(aim_value - assets[symbol].bid_value))
}
}
}
}
function main() {
while(true){
updateAccount()
updateTick()
onTick()
updateStatus()
Sleep(Interval*1000)
}
}
Detail
https://www.fmz.com/strategy/196445
Last Modified
2020-04-09 21:51:18