Strategy vault / Trend following
An Optimization of Dual Moving Average Trend Following Strategy Based on Indicators Combination
This strategy generates trading signals by calculating fast and slow moving average lines and combining Parabolic SAR indicator. It belongs to the trend following strategy. When the fast MA crosses over the slow MA…

Overview
This strategy generates trading signals by calculating fast and slow moving average lines and combining Parabolic SAR indicator. It belongs to the trend following strategy. When the fast MA crosses over the slow MA, long position will be opened. When the fast MA crosses below the slow MA, short position will be opened. Parabolic SAR is used to filter fake breakouts.
Strategy Principle
- Calculate fast and slow moving average lines. The parameters can be customized.
- Compare the two MA lines to determine market trend. When fast MA crosses over slow MA, it indicates bullish trend. When fast MA crosses below slow MA, it indicates bearish trend.
- Further confirmation is made by checking if close price is above/below fast MA. Only when fast MA crosses over slow MA and close price is above fast MA, long signal is generated. Only when fast MA crosses below slow MA and close price is below fast MA, short signal is generated.
- Parabolic SAR is used to filter fake signals. Only when all the three criteria are met, final signal is generated.
- Stop loss is set based on maximum tolerable loss. ATR indicator is used to calculate dynamic stop loss price.
Advantages
- MA lines determine market trend and avoid excessive trading in range-bound market.
- Dual filters lower risk of fake breakout significantly.
- Stop loss strategy effectively limits per trade loss.
Risks
- Indicator strategies tend to generate false signals
- No consideration of currency exposure risk
- Potentially miss initial trend in opposite direction
The strategy can be optimized in below aspects:
- Optimize MA parameters to fit specific product
- Add other indicators or models for signal filtering
- Consider real-time hedging or auto currency conversion
Directions for Optimization
- Optimize MA parameters to better capture trends
- Increase model diversity to improve signal accuracy
- Multi-timeframe verification to avoid being trapped
- Enhance stop loss strategy to increase stability
Conclusion
This is a typical dual moving average cross and indicators combination trend following strategy. By comparing fast and slow MA directions, market trend is determined. Various filter indicators are used avoid false signals. At the same time, stop loss function is implemented to control per trade loss. The advantage is that the strategy logic is simple and easy to understand and optimize. The disadvantage is that as a coarse trend tool, there is still room to improve signal accuracy, by introducing machine learning models for example.
Parameters
| Name | Default | What it controls |
|---|---|---|
| v_input_bool_1 | true | (?Backtest Time Period)Filter Date Range of Backtest |
| v_input_1 | timestamp(1 jan 2000) | Start Date |
| v_input_2 | timestamp(1 Jul 2100) | End Date |
| v_input_bool_2 | true | (?Long & Short Position)On/Off Long Postion |
| v_input_bool_3 | true | On/Off Short Postion |
| v_input_string_1 | 0 | (?Slow MA Inputs)Slow MA Type: SMA | EMA | WMA | HMA | RMA | SWMA | ALMA | VWMA | VWAP |
| v_input_int_1 | 160 | Slow MA Length |
| v_input_3_close | 0 | Slow MA Source: close | high | low | open | hl2 | hlc3 | hlcc4 | ohlc4 |
| v_input_string_2 | 0 | (?Fast MA Inputs)Fast MA Type: SMA | EMA | WMA | HMA | RMA | SWMA | ALMA | VWMA | VWAP |
| v_input_int_2 | 40 | Fast MA Length |
| v_input_4_close | 0 | Fast MA Source: close | high | low | open | hl2 | hlc3 | hlcc4 | ohlc4 |
| v_input_bool_4 | true | (?Parabolic SAR Inputs)Use Parabolic Sar? |
| v_input_bool_5 | false | Display Parabolic Sar? |
| v_input_float_1 | 0.02 | Start |
| v_input_float_2 | 0.02 | Increment |
| v_input_float_3 | 0.2 | Maximum |
| v_input_int_3 | 14 | (?Risk Management Inputs)ATR Length |
| v_input_float_4 | 2 | Long Position - Stop Loss - ATR Multiplier |
| v_input_float_5 | 2 | Short Position - Stop Loss - ATR Multiplier |
| v_input_float_6 | 2 | % of Equity at Risk |
Source Pine Script
/*backtest
start: 2024-01-01 00:00:00
end: 2024-01-31 00:00:00
period: 4h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © sosacur01
//@version=5
strategy(title="2 MA | Trend Following", overlay=true, pyramiding=1, commission_type=strategy.commission.percent, commission_value=0.2, initial_capital=10000)
//==========================================
//BACKTEST RANGE
useDateFilter = input.bool(true, title="Filter Date Range of Backtest",
group="Backtest Time Period")
backtestStartDate = input(timestamp("1 jan 2000"),
title="Start Date", group="Backtest Time Period",
tooltip="This start date is in the time zone of the exchange " +
"where the chart's instrument trades. It doesn't use the time " +
"zone of the chart or of your computer.")
backtestEndDate = input(timestamp("1 Jul 2100"),
title="End Date", group="Backtest Time Period",
tooltip="This end date is in the time zone of the exchange " +
"where the chart's instrument trades. It doesn't use the time " +
"zone of the chart or of your computer.")
inTradeWindow = true
if not inTradeWindow and inTradeWindow[1]
strategy.cancel_all()
strategy.close_all(comment="Date Range Exit")
//--------------------------------------
//LONG/SHORT POSITION ON/OFF INPUT
LongPositions = input.bool(title='On/Off Long Postion', defval=true, group="Long & Short Position")
ShortPositions = input.bool(title='On/Off Short Postion', defval=true, group="Long & Short Position")
//---------------------------------------
//SLOW MA INPUTS
averageType1 = input.string(defval="SMA", group="Slow MA Inputs", title="Slow MA Type", options=["SMA", "EMA", "WMA", "HMA", "RMA", "SWMA", "ALMA", "VWMA", "VWAP"])
averageLength1 = input.int(defval=160, group="Slow MA Inputs", title="Slow MA Length", minval=50)
averageSource1 = input(close, title="Slow MA Source", group="Slow MA Inputs")
//SLOW MA TYPE
MovAvgType1(averageType1, averageSource1, averageLength1) =>
switch str.upper(averageType1)
"SMA" => ta.sma(averageSource1, averageLength1)
"EMA" => ta.ema(averageSource1, averageLength1)
"WMA" => ta.wma(averageSource1, averageLength1)
"HMA" => ta.hma(averageSource1, averageLength1)
"RMA" => ta.rma(averageSource1, averageLength1)
"SWMA" => ta.swma(averageSource1)
"ALMA" => ta.alma(averageSource1, averageLength1, 0.85, 6)
"VWMA" => ta.vwma(averageSource1, averageLength1)
"VWAP" => ta.vwap(averageSource1)
=> runtime.error("Moving average type '" + averageType1 +
"' not found!"), na
//----------------------------------
//FAST MA INPUTS
averageType2 = input.string(defval="SMA", group="Fast MA Inputs", title="Fast MA Type", options=["SMA","EMA","WMA","HMA","RMA","SWMA","ALMA","VWMA","VWAP"])
averageLength2 = input.int(defval=40, group="Fast MA Inputs", title="Fast MA Length", maxval=40)
averageSource2 = input(close, title="Fast MA Source", group="Fast MA Inputs")
//FAST MA TYPE
MovAvgType2(averageType2, averageSource2, averageLength2) =>
switch str.upper(averageType2)
"SMA" => ta.sma(averageSource2, averageLength2)
"EMA" => ta.ema(averageSource2, averageLength2)
"WMA" => ta.wma(averageSource2, averageLength2)
"HMA" => ta.hma(averageSource2, averageLength2)
"RMA" => ta.rma(averageSource2, averageLength2)
"SWMA" => ta.swma(averageSource2)
"ALMA" => ta.alma(averageSource2, averageLength2, 0.85, 6)
"VWMA" => ta.vwma(averageSource2, averageLength2)
"VWAP" => ta.vwap(averageSource2)
=> runtime.error("Moving average type '" + averageType2 +
"' not found!"), na
//---------------------------------------------------
//MA VALUES
FASTMA = MovAvgType2(averageType2, averageSource2, averageLength2)
SLOWMA = MovAvgType1(averageType1, averageSource1, averageLength1)
//BUY/SELL TRIGGERS
bullish_trend = FASTMA > SLOWMA and close > FASTMA
bearish_trend = FASTMA < SLOWMA and close < FASTMA
//MAs PLOT
plot1 = plot(SLOWMA,color=color.gray, linewidth=1, title="Slow-MA")
plot2 = plot(FASTMA,color=color.yellow, linewidth=1, title="Fast-MA")
fill(plot1, plot2, color=SLOWMA>FASTMA ? color.new(color.red, 70) : color.new(color.green, 70), title="EMA Clouds")
//-----------------------------------------------------
//PARABOLIC SAR USER INPUT
usepsarFilter = input.bool(title='Use Parabolic Sar?', defval=true, group = "Parabolic SAR Inputs")
psar_display = input.bool(title="Display Parabolic Sar?", defval=false, group="Parabolic SAR Inputs")
start = input.float(title="Start", defval=0.02, group="Parabolic SAR Inputs", step=0.001)
increment = input.float(title="Increment", defval=0.02, group="Parabolic SAR Inputs", step=0.001)
maximum = input.float(title="Maximum", defval=0.2, group="Parabolic SAR Inputs", step=0.001)
//SAR VALUES
psar = request.security(syminfo.tickerid, "D", ta.sar(start, increment, maximum))
//BULLISH & BEARISH PSAR CONDITIONS
bullish_psar = (usepsarFilter ? low > psar : bullish_trend )
bearsish_psar = (usepsarFilter ? high < psar : bearish_trend)
//SAR PLOT
psar_plot = if low > psar
color.rgb(198, 234, 199, 13)
else
color.rgb(219, 134, 134, 48)
plot(psar_display ? psar : na, color=psar_plot, title="Par SAR")
//-------------------------------------
//ENTRIES AND EXITS
long_entry = if inTradeWindow and bullish_trend and bullish_psar and LongPositions
true
long_exit = if inTradeWindow and bearish_trend
true
short_entry = if inTradeWindow and bearish_trend and bearsish_psar and ShortPositions
true
short_exit = if inTradeWindow and bullish_trend
true
//--------------------------------------
//RISK MANAGEMENT - SL, MONEY AT RISK, POSITION SIZING
atrPeriod = input.int(14, "ATR Length", group="Risk Management Inputs")
sl_atr_multiplier = input.float(title="Long Position - Stop Loss - ATR Multiplier", defval=2, group="Risk Management Inputs", step=0.5)
sl_atr_multiplier_short = input.float(title="Short Position - Stop Loss - ATR Multiplier", defval=2, group="Risk Management Inputs", step=0.5)
i_pctStop = input.float(2, title="% of Equity at Risk", step=.5, group="Risk Management Inputs")/100
//ATR VALUE
_atr = ta.atr(atrPeriod)
//CALCULATE LAST ENTRY PRICE
lastEntryPrice = strategy.opentrades.entry_price(strategy.opentrades - 1)
//STOP LOSS - LONG POSITIONS
var float sl = na
//CALCULTE SL WITH ATR AT ENTRY PRICE - LONG POSITION
if (strategy.position_size[1] != strategy.position_size)
sl := lastEntryPrice - (_atr * sl_atr_multiplier)
//IN TRADE - LONG POSITIONS
inTrade = strategy.position_size > 0
//PLOT SL - LONG POSITIONS
plot(inTrade ? sl : na, color=color.blue, style=plot.style_circles, title="Long Position - Stop Loss")
//CALCULATE ORDER SIZE - LONG POSITIONS
positionSize = (strategy.equity * i_pctStop) / (_atr * sl_atr_multiplier)
//============================================================================================
//STOP LOSS - SHORT POSITIONS
var float sl_short = na
//CALCULTE SL WITH ATR AT ENTRY PRICE - SHORT POSITIONS
if (strategy.position_size[1] != strategy.position_size)
sl_short := lastEntryPrice + (_atr * sl_atr_multiplier_short)
//IN TRADE SHORT POSITIONS
inTrade_short = strategy.position_size < 0
//PLOT SL - SHORT POSITIONS
plot(inTrade_short ? sl_short : na, color=color.red, style=plot.style_circles, title="Short Position - Stop Loss")
//CALCULATE ORDER - SHORT POSITIONS
positionSize_short = (strategy.equity * i_pctStop) / (_atr * sl_atr_multiplier_short)
//===============================================
//LONG STRATEGY
strategy.entry("Long", strategy.long, comment="Long", when = long_entry, qty=positionSize)
if (strategy.position_size > 0)
strategy.close("Long", when = (long_exit), comment="Close Long")
strategy.exit("Long", stop = sl, comment="Exit Long")
//SHORT STRATEGY
strategy.entry("Short", strategy.short, comment="Short", when = short_entry, qty=positionSize_short)
if (strategy.position_size < 0)
strategy.close("Short", when = (short_exit), comment="Close Short")
strategy.exit("Short", stop = sl_short, comment="Exit Short")
//ONE DIRECTION TRADING COMMAND (BELLOW ONLY ACTIVATE TO CORRECT BUGS)
Published by ChaoZhang on the FMZ strategy square · last modified 2024-02-01. 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.
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/An-Optimization-of-Dual-Moving-Average-Trend-Following-Strategy-Based-on-Indicators-Combination.md- npx agenkit install engineering-kit in your project
- Paste the command into your agent and approve each phase
- 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
- Indicator Verification Automated Trading StrategyPine Script · Trend following · 10m
- Dual ATR Channel Trend Following StrategyPine Script · Trend following · 15m
- Fixed Range Volume Profile And Anchored VWAP Trend Identification Strategy With Dynamic Stop LossPine Script · Trend following · 1h
- Trend Following Strategy Based on Moving Average 439858Pine Script · Trend following · 1d
- Multi Period Trend Confirmation Dynamic Risk Control Quantitative Trading StrategyPine Script · Trend following · 1h
- WaveTrend Oscillator Divergence StrategyPine Script · Trend following · 1d