Type/to search
8
Follow
1363
Followers
Version JavaScript de la stratégie SuperTrend
Discussions
Created 2020-04-17 16:36:24  Updated 2023-10-09 22:46:56
 16
 4251

img

Version JavaScript de la stratégie SuperTrend

Il existe plusieurs versions de l'indicateur SuperTrend sur la télévision. J'ai trouvé un algorithme plus facile à comprendre et je l'ai transplanté. Je l'ai comparé avec l'indicateur SuperTrend chargé sur le graphique TV du système de backtesting de la plateforme de trading quantitative Inventor et j'ai trouvé une légère différence. Je n'ai pas encore compris la raison. J'attends avec impatience les conseils de tous les lecteurs. Je vais juste lancer quelques idées.

Algorithme de la version JavaScript de l'indicateur 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) } }

Comparaison des backtests de codes de test :
img

img

Une stratégie simple utilisant l'indicateur SuperTrend

La logique de trading est relativement simple : elle consiste à ouvrir une position longue lorsque la tendance courte se transforme en tendance longue.
Ouvrez une position courte lorsque la tendance haussière se transforme en tendance baissière.

Paramètres de la stratégie :
img

Stratégie de trading 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) } }

Adresse de la stratégie : https://www.fmz.com/strategy/201837

Performances des backtests

Réglages des paramètres, période K-line, référence : homélieSuperTrend V.1 – Système de lignes Super Trend
La période de la ligne K est définie sur 15 minutes et les paramètres SuperTrend sont définis sur 45,3. Effectuez un backtest du contrat à terme trimestriel OKEX au cours de l'année écoulée, en définissant un contrat par transaction. Étant donné qu'un seul contrat est négocié à chaque fois, le taux d'utilisation du capital est très faible et il n'y a pas lieu de s'inquiéter de la valeur Sharpe.

img

Cette stratégie est destinée uniquement à l'apprentissage, veuillez l'utiliser avec prudence dans le trading réel.

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)