0bps

Strategy vault / Trend following

Twin Optimized Trend Tracker Strategy

The Twin Optimized Trend Tracker Strategy is an enhanced version of the OTT strategy that combines dual OTT lines and a coefficient to better handle false signals during sideways market. This strategy was developed…

Author
ChaoZhang
Complexity
Intermediate · 140 lines
Parameters
15
Backtest bar
4h · intraday
Tested on
BTC/USDT · futures

Overview

The Twin Optimized Trend Tracker Strategy is an enhanced version of the OTT strategy that combines dual OTT lines and a coefficient to better handle false signals during sideways market. This strategy was developed by Turkish trader Anıl Özekşi, who explained the design philosophy in his video tutorials.

Principles

The core of the Twin OTT strategy is to determine the trend direction using two optimized trend tracking lines - OTT. It first calculates the moving average MAvg, then obtains the long stop loss line longStop and short stop loss line shortStop based on a percentage of the MAvg value. When the price crosses above the longStop line, it is a long signal, and when it crosses below the shortStop line, it is a short signal.

To handle false signals during sideways market, the strategy improves the following two aspects:

  1. Two vertically displaced OTT lines, OTTup and OTTdn, are added. They are slight upward and downward shifts of the original OTT. Only when the price breaks through these two displaced lines, valid trading signals are generated.

  2. A small coefficient coef is introduced to fine tune the two displaced OTT lines for better precision.

With this twin OTT design, most noise from sideways market can be filtered to avoid wrong signals. It helps capture trend turning points and switch positions in a timely manner. This is the biggest advantage of the Twin OTT strategy.

Advantages

  • The twin OTT lines design can effectively filter out false signals and enhance strategy stability
  • The additional coef coefficient helps OTT lines respond better to the market
  • The author Anıl Özekşi explains the strategy logic clearly in his video tutorials, which is easy to understand
  • It combines multiple technical indicators like EMA, stop loss lines to determine market trends
  • The author Anıl Özekşi is a well-known Turkish trader, adding to the credibility

Risks

  • The OTT indicator itself tends to whipsaw and pullback tests. The twin OTT design alleviates this problem.
  • With violent fluctuations, the stop loss lines may get triggered frequently, causing overtrading.
  • The coef coefficient needs careful testing for optimum value, otherwise it undermines the effectiveness.
  • The tutorials are in Turkish. Language barrier may lead to misunderstandings of the logic.
  • Insufficient backtests. More periods and markets are needed to verify the strategy.

Counter measures:

  • Add a buffer between stop loss lines and twin OTT to prevent over-sensitivity
  • Optimize coef settings according to backtest results
  • Translate the tutorials to ensure correct understanding of the logic
  • Conduct backtests across more historical periods to verify reliability

Optimization Directions

  • Make parameters like period length adjustable inputs
  • Try other types of moving averages that better fit the OTT principles
  • Optimize coef for different trading instruments separately
  • Add filters to avoid wrong signals during minor trading sessions
  • Make the stop loss lines dynamic based on volatility
  • Introduce machine learning to auto optimize parameters

In summary, the Twin OTT strategy fully utilizes Anıl Özekşi's OTT experience and makes innovations. It has the potential to become a reliable, customizable trend tracking framework. But continuous optimization and testing are still needed to adapt to changing markets.

Conclusion

The Twin OTT strategy effectively handles false signals during sideways markets using dual optimized trend tracking lines and a fine tuning coefficient. It makes sensible use of moving average concepts and dynamic stop loss lines to track trends. This concise and practical strategy stems from a renowned trader's first-hand experience, making it worth in-depth research and application. But we should also be aware of its limitations and avoid complacency. Only through continuous optimizations and rigorous testing can it become a robust trend tracking strategy.

Parameters

NameDefaultWhat it controls
v_input_1_close0Source: close | high | low | open | hl2 | hlc3 | hlcc4 | ohlc4
v_input_240OTT Period
v_input_3trueOptimization Constant
v_input_40.001Twin OTT Coefficient
v_input_5trueShow Support Line?
v_input_6trueShow Signals?
v_input_70Moving Average Type: VAR | EMA | WMA | TMA | SMA | WWMA | ZLEMA | TSF
v_input_8trueHighlighter On/Off ?
v_input_9true=Backtest Inputs=
v_input_10trueFrom Day
v_input_11trueFrom Month
v_input_122005From Year
v_input_13trueTo Day
v_input_14trueTo Month
v_input_159999To Year

Source Pine Script

/*backtest
start: 2023-09-07 00:00:00
end: 2023-10-07 00:00:00
period: 4h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//@version=4
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © KivancOzbilgic

//created by: @Anil_Ozeksi
//developer: ANIL ÖZEKŞİ
//author: @kivancozbilgic

strategy("Twin Optimized Trend Tracker","TOTT", overlay=true)
src = input(close, title="Source")
length=input(40, "OTT Period", minval=1)
percent=input(1, "Optimization Constant", type=input.float, step=0.1, minval=0)
coeff=input(0.001, "Twin OTT Coefficient", type=input.float, step=0.001, minval=0)
showsupport = input(title="Show Support Line?", type=input.bool, defval=true)
showsignalsk = input(title="Show Signals?", type=input.bool, defval=true)
mav = input(title="Moving Average Type", defval="VAR", options=["SMA", "EMA", "WMA", "TMA", "VAR", "WWMA", "ZLEMA", "TSF"])
highlighting = input(title="Highlighter On/Off ?", type=input.bool, defval=true)
Var_Func(src,length)=>
    valpha=2/(length+1)
    vud1=src>src[1] ? src-src[1] : 0
    vdd1=src<src[1] ? src[1]-src : 0
    vUD=sum(vud1,9)
    vDD=sum(vdd1,9)
    vCMO=nz((vUD-vDD)/(vUD+vDD))
    VAR=0.0
    VAR:=nz(valpha*abs(vCMO)*src)+(1-valpha*abs(vCMO))*nz(VAR[1])
VAR=Var_Func(src,length)
Wwma_Func(src,length)=>
    wwalpha = 1/ length
    WWMA = 0.0
    WWMA := wwalpha*src + (1-wwalpha)*nz(WWMA[1])
WWMA=Wwma_Func(src,length)
Zlema_Func(src,length)=>
    zxLag = length/2==round(length/2) ? length/2 : (length - 1) / 2
    zxEMAData = (src + (src - src[zxLag]))
    ZLEMA = ema(zxEMAData, length)
ZLEMA=Zlema_Func(src,length)
Tsf_Func(src,length)=>
    lrc = linreg(src, length, 0)
    lrc1 = linreg(src,length,1)
    lrs = (lrc-lrc1)
    TSF = linreg(src, length, 0)+lrs
TSF=Tsf_Func(src,length)
getMA(src, length) =>
    ma = 0.0
    if mav == "SMA"
        ma := sma(src, length)
        ma

    if mav == "EMA"
        ma := ema(src, length)
        ma

    if mav == "WMA"
        ma := wma(src, length)
        ma

    if mav == "TMA"
        ma := sma(sma(src, ceil(length / 2)), floor(length / 2) + 1)
        ma

    if mav == "VAR"
        ma := VAR
        ma

    if mav == "WWMA"
        ma := WWMA
        ma

    if mav == "ZLEMA"
        ma := ZLEMA
        ma

    if mav == "TSF"
        ma := TSF
        ma
    ma
    
MAvg=getMA(src, length)
fark=MAvg*percent*0.01
longStop = MAvg - fark
longStopPrev = nz(longStop[1], longStop)
longStop := MAvg > longStopPrev ? max(longStop, longStopPrev) : longStop
shortStop =  MAvg + fark
shortStopPrev = nz(shortStop[1], shortStop)
shortStop := MAvg < shortStopPrev ? min(shortStop, shortStopPrev) : shortStop
dir = 1
dir := nz(dir[1], dir)
dir := dir == -1 and MAvg > shortStopPrev ? 1 : dir == 1 and MAvg < longStopPrev ? -1 : dir
MT = dir==1 ? longStop: shortStop
OTT=MAvg>MT ? MT*(200+percent)/200 : MT*(200-percent)/200 
OTTup=OTT*(1+coeff)
OTTdn=OTT*(1-coeff)

PPLOT=plot(showsupport ? MAvg : na, color=#0585E1, linewidth=2, title="Support Line")

pALLup=plot(nz(OTTup[2]), color=color.green, linewidth=2, title="OTTup", transp=0)
pALLdn=plot(nz(OTTdn[2]), color=color.red, linewidth=2, title="OTTdown", transp=0)

buySignalk = crossover(MAvg, OTTup[2])
sellSignalk = crossunder(MAvg, OTTdn[2])
K1=barssince(buySignalk)
K2=barssince(sellSignalk)
O1=barssince(buySignalk[1])
O2=barssince(sellSignalk[1])

plotshape(buySignalk and showsignalsk and O1>K2 ? min(low-abs(roc(low,1)),OTTdn-abs(roc(low,1))) : na, title="Buy", text="Buy", location=location.absolute, style=shape.labelup, size=size.tiny, color=color.green, textcolor=color.white, transp=0)
plotshape(sellSignalk and showsignalsk and O2>K1 ? max(high+abs(roc(high,1)),OTTup+abs(roc(high,1))) : na, title="Sell", text="Sell", location=location.absolute, style=shape.labeldown, size=size.tiny, color=color.red, textcolor=color.white, transp=0)
mPlot = plot(ohlc4, title="", style=plot.style_circles, linewidth=0,display=display.none)
longFillColor = highlighting ? (O2>K1 ? color.green : na) : na
shortFillColor = highlighting ? (O1>K2 ? color.red : na) : na
fill(mPlot, PPLOT, title="UpTrend Highligter", color=longFillColor,transp=90)
fill(mPlot, PPLOT, title="DownTrend Highligter", color=shortFillColor,transp=90)
fill(pALLup, pALLdn, title="Flat Zone Highligter", color=color.blue,transp=90)



dummy0 = input(true, title = "=Backtest Inputs=")
FromDay    = input(defval = 1, title = "From Day", minval = 1, maxval = 31)
FromMonth  = input(defval = 1, title = "From Month", minval = 1, maxval = 12)
FromYear   = input(defval = 2005, title = "From Year", minval = 2005)
ToDay      = input(defval = 1, title = "To Day", minval = 1, maxval = 31)
ToMonth    = input(defval = 1, title = "To Month", minval = 1, maxval = 12)
ToYear     = input(defval = 9999, title = "To Year", minval = 2006)
Start     = timestamp(FromYear, FromMonth, FromDay, 00, 00)
Finish    = timestamp(ToYear, ToMonth, ToDay, 23, 59)
Timerange() =>
    time >= Start and time <= Finish ? true : false
if buySignalk
    strategy.entry("Long", strategy.long)
if sellSignalk
    strategy.entry("Short", strategy.short)

Published by ChaoZhang on the FMZ strategy square · last modified 2023-10-08. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.

Build this with AgenKit

A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.

in Claude Code, Cursor, Codex & 4 more
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/Twin-Optimized-Trend-Tracker-Strategy.md
  1. npx agenkit install engineering-kit in your project
  2. Paste the command into your agent and approve each phase
  3. Get tests, backtest, risk controls and a paper-trading switch

Understand it first

Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.

Related specs

esc

Try , , or .

↑↓ move↵ open