Type/to search
8
Follow
1363
Followers
Versão JavaScript da estratégia SuperTrend
Discussions
Created 2020-04-17 16:36:24  Updated 2023-10-09 22:46:56
 16
 4251

img

Versão JavaScript da estratégia SuperTrend

Existem várias versões do indicador SuperTrend na TV. Encontrei um algoritmo mais fácil de entender e o transplantei. Comparei-o com o indicador SuperTrend carregado no gráfico de TV do Inventor Quantitative Trading Platform Backtesting System e encontrei uma pequena diferença. Ainda não descobri o motivo. Estou ansioso pela orientação de todos os leitores. Vou apenas dar algumas ideias.

Algoritmo da versão JavaScript do indicador SuperTrend

javascript
// VIA: https://github.com/freqtrade/freqtrade-strategies/issues/30 function SuperTrend(r, period, multiplier) { // atr var atr = talib.ATR(r, period) // baseUp , baseDown var baseUp = [] var baseDown = [] for (var i = 0; i < r.length; i++) { if (isNaN(atr[i])) { baseUp.push(NaN) baseDown.push(NaN) continue } baseUp.push((r[i].High + r[i].Low) / 2 + multiplier * atr[i]) baseDown.push((r[i].High + r[i].Low) / 2 - multiplier * atr[i]) } // fiUp , fiDown var fiUp = [] var fiDown = [] var prevFiUp = 0 var prevFiDown = 0 for (var i = 0; i < r.length; i++) { if (isNaN(baseUp[i])) { fiUp.push(NaN) } else { fiUp.push(baseUp[i] < prevFiUp || r[i - 1].Close > prevFiUp ? baseUp[i] : prevFiUp) prevFiUp = fiUp[i] } if (isNaN(baseDown[i])) { fiDown.push(NaN) } else { fiDown.push(baseDown[i] > prevFiDown || r[i - 1].Close < prevFiDown ? baseDown[i] : prevFiDown) prevFiDown = fiDown[i] } } var st = [] var prevSt = NaN for (var i = 0; i < r.length; i++) { if (i < period) { st.push(NaN) continue } var nowSt = 0 if (((isNaN(prevSt) && isNaN(fiUp[i - 1])) || prevSt == fiUp[i - 1]) && r[i].Close <= fiUp[i]) { nowSt = fiUp[i] } else if (((isNaN(prevSt) && isNaN(fiUp[i - 1])) || prevSt == fiUp[i - 1]) && r[i].Close > fiUp[i]) { nowSt = fiDown[i] } else if (((isNaN(prevSt) && isNaN(fiDown[i - 1])) || prevSt == fiDown[i - 1]) && r[i].Close >= fiDown[i]) { nowSt = fiDown[i] } else if (((isNaN(prevSt) && isNaN(fiDown[i - 1])) || prevSt == fiDown[i - 1]) && r[i].Close < fiDown[i]) { nowSt = fiUp[i] } st.push(nowSt) prevSt = st[i] } var up = [] var down = [] for (var i = 0; i < r.length; i++) { if (isNaN(st[i])) { up.push(st[i]) down.push(st[i]) } if (r[i].Close < st[i]) { down.push(st[i]) up.push(NaN) } else { down.push(NaN) up.push(st[i]) } } return [up, down] } // 测试指标用的main函数,并非交易策略 function main() { while (1) { var r = _C(exchange.GetRecords) var st = SuperTrend(r, 10, 3) $.PlotRecords(r, "K") $.PlotLine("L", st[0][st[0].length - 2], r[r.length - 2].Time) $.PlotLine("S", st[1][st[1].length - 2], r[r.length - 2].Time) Sleep(2000) } }

Comparação de backtest de código de teste:
img

img

Uma estratégia simples usando o indicador SuperTrend

A lógica de negociação é relativamente simples: abrir uma posição longa quando a tendência curta se transforma em uma tendência longa.
Abra uma posição curta quando a tendência de alta se transformar em tendência de baixa.

Parâmetros de estratégia:
img

Estratégia de negociação SuperTrend

javascript
/*backtest start: 2019-08-01 00:00:00 end: 2020-03-11 00:00:00 period: 15m basePeriod: 5m exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}] */ // 全局变量 var OpenAmount = 0 // 开仓后持仓的数量 var KeepAmount = 0 // 保留仓位 var IDLE = 0 var LONG = 1 var SHORT = 2 var COVERLONG = 3 var COVERSHORT = 4 var COVERLONG_PART = 5 var COVERSHORT_PART = 6 var OPENLONG = 7 var OPENSHORT = 8 var State = IDLE // 交易逻辑部分 function GetPosition(posType) { var positions = _C(exchange.GetPosition) /* if(positions.length > 1){ throw "positions error:" + JSON.stringify(positions) } */ var count = 0 for(var j = 0; j < positions.length; j++){ if(positions[j].ContractType == Symbol){ count++ } } if(count > 1){ throw "positions error:" + JSON.stringify(positions) } for (var i = 0; i < positions.length; i++) { if (positions[i].ContractType == Symbol && positions[i].Type === posType) { return [positions[i].Price, positions[i].Amount]; } } Sleep(TradeInterval); return [0, 0] } function CancelPendingOrders() { while (true) { var orders = _C(exchange.GetOrders) for (var i = 0; i < orders.length; i++) { exchange.CancelOrder(orders[i].Id); Sleep(TradeInterval); } if (orders.length === 0) { break; } } } function Trade(Type, Price, Amount, CurrPos, OnePriceTick){ // 处理交易 if(Type == OPENLONG || Type == OPENSHORT){ // 处理开仓 exchange.SetDirection(Type == OPENLONG ? "buy" : "sell") var pfnOpen = Type == OPENLONG ? exchange.Buy : exchange.Sell var idOpen = pfnOpen(Price, Amount, CurrPos, OnePriceTick, Type) Sleep(TradeInterval) if(idOpen) { exchange.CancelOrder(idOpen) } else { CancelPendingOrders() } } else if(Type == COVERLONG || Type == COVERSHORT){ // 处理平仓 exchange.SetDirection(Type == COVERLONG ? "closebuy" : "closesell") var pfnCover = Type == COVERLONG ? exchange.Sell : exchange.Buy var idCover = pfnCover(Price, Amount, CurrPos, OnePriceTick, Type) Sleep(TradeInterval) if(idCover){ exchange.CancelOrder(idCover) } else { CancelPendingOrders() } } else { throw "Type error:" + Type } } function SuperTrend(r, period, multiplier) { // atr var atr = talib.ATR(r, period) // baseUp , baseDown var baseUp = [] var baseDown = [] for (var i = 0; i < r.length; i++) { if (isNaN(atr[i])) { baseUp.push(NaN) baseDown.push(NaN) continue } baseUp.push((r[i].High + r[i].Low) / 2 + multiplier * atr[i]) baseDown.push((r[i].High + r[i].Low) / 2 - multiplier * atr[i]) } // fiUp , fiDown var fiUp = [] var fiDown = [] var prevFiUp = 0 var prevFiDown = 0 for (var i = 0; i < r.length; i++) { if (isNaN(baseUp[i])) { fiUp.push(NaN) } else { fiUp.push(baseUp[i] < prevFiUp || r[i - 1].Close > prevFiUp ? baseUp[i] : prevFiUp) prevFiUp = fiUp[i] } if (isNaN(baseDown[i])) { fiDown.push(NaN) } else { fiDown.push(baseDown[i] > prevFiDown || r[i - 1].Close < prevFiDown ? baseDown[i] : prevFiDown) prevFiDown = fiDown[i] } } var st = [] var prevSt = NaN for (var i = 0; i < r.length; i++) { if (i < period) { st.push(NaN) continue } var nowSt = 0 if (((isNaN(prevSt) && isNaN(fiUp[i - 1])) || prevSt == fiUp[i - 1]) && r[i].Close <= fiUp[i]) { nowSt = fiUp[i] } else if (((isNaN(prevSt) && isNaN(fiUp[i - 1])) || prevSt == fiUp[i - 1]) && r[i].Close > fiUp[i]) { nowSt = fiDown[i] } else if (((isNaN(prevSt) && isNaN(fiDown[i - 1])) || prevSt == fiDown[i - 1]) && r[i].Close >= fiDown[i]) { nowSt = fiDown[i] } else if (((isNaN(prevSt) && isNaN(fiDown[i - 1])) || prevSt == fiDown[i - 1]) && r[i].Close < fiDown[i]) { nowSt = fiUp[i] } st.push(nowSt) prevSt = st[i] } var up = [] var down = [] for (var i = 0; i < r.length; i++) { if (isNaN(st[i])) { up.push(st[i]) down.push(st[i]) } if (r[i].Close < st[i]) { down.push(st[i]) up.push(NaN) } else { down.push(NaN) up.push(st[i]) } } return [up, down] } var preTime = 0 function main() { exchange.SetContractType(Symbol) while (1) { var r = _C(exchange.GetRecords) var currBar = r[r.length - 1] if (r.length < pd) { Sleep(5000) continue } var st = SuperTrend(r, pd, factor) $.PlotRecords(r, "K") $.PlotLine("L", st[0][st[0].length - 2], r[r.length - 2].Time) $.PlotLine("S", st[1][st[1].length - 2], r[r.length - 2].Time) if(!isNaN(st[0][st[0].length - 2]) && isNaN(st[0][st[0].length - 3])){ if (State == SHORT) { State = COVERSHORT } else if(State == IDLE) { State = OPENLONG } } if(!isNaN(st[1][st[1].length - 2]) && isNaN(st[1][st[1].length - 3])){ if (State == LONG) { State = COVERLONG } else if (State == IDLE) { State = OPENSHORT } } // 执行信号 var pos = null var price = null if(State == OPENLONG){ // 开多仓 pos = GetPosition(PD_LONG) // 检查持仓 // 判断是不是 满足状态,如果满足 修改状态 if(pos[1] >= Amount){ // 持仓超过或者等于参数设置的 开仓量 Sleep(1000) $.PlotFlag(currBar.Time, "开多仓", 'OL') // 标记 OpenAmount = pos[1] // 记录开仓数 State = LONG // 标记为 做多状态 continue } price = currBar.Close - (currBar.Close % PriceTick) + PriceTick * 2 // 计算价格 Trade(OPENLONG, price, Amount - pos[1], pos, PriceTick) // 下单函数 (Type, Price, Amount, CurrPos, PriceTick) } if(State == OPENSHORT){ // 开空仓 pos = GetPosition(PD_SHORT) // 检查持仓 if(pos[1] >= Amount){ Sleep(1000) $.PlotFlag(currBar.Time, "开空仓", 'OS') OpenAmount = pos[1] State = SHORT continue } price = currBar.Close - (currBar.Close % PriceTick) - PriceTick * 2 Trade(OPENSHORT, price, Amount - pos[1], pos, PriceTick) } if(State == COVERLONG){ // 处理平多仓 pos = GetPosition(PD_LONG) // 获取持仓信息 if(pos[1] == 0){ // 判断持仓是否为 0 $.PlotFlag(currBar.Time, "平多仓", '----CL') // 标记 State = IDLE continue } price = currBar.Close - (currBar.Close % PriceTick) - PriceTick * 2 Trade(COVERLONG, price, pos[1], pos, PriceTick) } if(State == COVERSHORT){ // 处理做多仓 pos = GetPosition(PD_SHORT) if(pos[1] == 0){ $.PlotFlag(currBar.Time, "平空仓", '----CS') State = IDLE continue } price = currBar.Close - (currBar.Close % PriceTick) + PriceTick * 2 Trade(COVERSHORT, price, pos[1], pos, PriceTick) } if(State == COVERLONG_PART) { // 部分平多仓 pos = GetPosition(PD_LONG) // 获取持仓 if(pos[1] <= KeepAmount){ // 持仓小于等于 保持量,本次平仓完成 $.PlotFlag(currBar.Time, "平多仓,保留:" + KeepAmount, '----CL') // 标记 State = pos[1] == 0 ? IDLE : LONG // 更新状态 continue } price = currBar.Close - (currBar.Close % PriceTick) - PriceTick * 2 Trade(COVERLONG, price, pos[1] - KeepAmount, pos, PriceTick) } if(State == COVERSHORT_PART){ pos = GetPosition(PD_SHORT) if(pos[1] <= KeepAmount){ $.PlotFlag(currBar.Time, "平空仓,保留:" + KeepAmount, '----CS') State = pos[1] == 0 ? IDLE : SHORT continue } price = currBar.Close - (currBar.Close % PriceTick) + PriceTick * 2 Trade(COVERSHORT, price, pos[1] - KeepAmount, pos, PriceTick) } LogStatus(_D()) Sleep(1000) } }

Endereço estratégico: https://www.fmz.com/strategy/201837

Desempenho do Backtest

Configurações de parâmetros, período da linha K, referência: homiliaSuperTrend V.1--Sistema de Super Trend Line
O período da linha K é definido como 15 minutos e os parâmetros do SuperTrend são definidos como 45,3. Faça o backtest do contrato trimestral de futuros da OKEX no ano passado, definindo um contrato por transação. Como apenas um contrato é negociado a cada vez, a taxa de utilização de capital é muito baixa e não há necessidade de se preocupar com o valor de Sharpe.

img

A estratégia é apenas para aprendizado, use-a com cautela em negociações reais.

Related Recommendations
Comment
All comments (16)

    梦总,这个策略的原问题解决了吗?
    我看现在TV有了TA.superstend策略,但写javascript更自由点,期待在能够Javascipt的TA中也封装一个正确的superstrend版本

    2 years ago

    暂时还没有办法直接调用PINE脚本的函数,可能后期会增加兼容支持。

    2 years ago

    算法return(up,down)是什么意思,返回的内容是什么样子的,超级趋势不也是一个数字么,,想用一下这个函数,不会啊。。求教f

    4 years ago

    返回的是一个二维数组,up就是上面的那条线,down就是下线。返回的是整个指标数据。

    4 years ago

    怎么确定sp的周期呢,需要修改下哪个参数

    4 years ago

    $.PlotRecords(r, "K")
    $.PlotLine("L", st[0][st[0].length - 2], r[r.length - 2].Time)
    $.PlotLine("S", st[1][st[1].length - 2], r[r.length - 2].Time)
    请问下,L后边的代码代表什么意思,如果是低线的话,后边的r需要跟它配合使用么,用来表征L出现的时间?

    4 years ago

    $.PlotRecords(r, "K")
    $.PlotLine("L", st[0][st[0].length - 2], r[r.length - 2].Time)
    $.PlotLine("S", st[1][st[1].length - 2], r[r.length - 2].Time)
    请问下,L后边的代码代表什么意思,如果是低线的话,后边的r需要跟它配合使用么

    4 years ago

    $.PlotLine 是画线类库的接口函数,具体可以到策略广场复制画线类库这个模版,代码公开的。看下源码即可明白。

    4 years ago

    Up.length-1就是上边线最近的一个数字吗

    4 years ago

    up[up.length - 1],指标的倒数第一个数据,对应K线倒数第一个BAR。

    4 years ago

    梦总牛逼

    6 years ago

    与TV上有差异应该是fmz封装的ATR计算的问题,比如传入的周期是7,计算出来ATR前6个值应该是null,但实际上并不是

    6 years ago

    好的,感谢感谢,但是我使用的是talib库的ATR,算出的好像也不对,按照算法直接实现ATR得出的数据也是有点差异。

    6 years ago

    是的,大佬确认一下问题所在,有点纠结了,不知道用哪个了

    6 years ago

    嗯,再详细研究下tradingview上的算法。

    6 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)