Tripple MA
Strategy based on Triple Moving Average crossover
Testing indicates an average annual return of about 55%. It performs best in the stocks market.
Triple MA aligns three moving averages to define direction. When the shortest average is above the middle and long averages a long entry occurs. The reverse alignment opens shorts, and a cross of the short and middle lines closes the trade.
Using three averages helps filter out noise present in single-MA systems. This layered approach seeks to confirm momentum before committing to a trade.
Details
- Entry Criteria: Signals based on MA.
- Long/Short: Both directions.
- Exit Criteria: Opposite signal or stop.
- Stops: Yes.
- Default Values:
ShortMaPeriod= 5MiddleMaPeriod= 20LongMaPeriod= 50StopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5)
- Filters:
- Category: Trend
- Direction: Both
- Indicators: MA
- Stops: Yes
- Complexity: Basic
- Timeframe: Intraday (5m)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on Triple Moving Average crossover.
/// It enters long when the short MA is above both the middle and the long MA, and short when it is below both.
/// A cross of the short and the middle MA closes the position, and a percent stop protects it.
/// </summary>
public class TripleMAStrategy : Strategy
{
private readonly StrategyParam<int> _shortMaPeriod;
private readonly StrategyParam<int> _middleMaPeriod;
private readonly StrategyParam<int> _longMaPeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
// Current state
/// <summary>
/// Period for short moving average.
/// </summary>
public int ShortMaPeriod
{
get => _shortMaPeriod.Value;
set => _shortMaPeriod.Value = value;
}
/// <summary>
/// Period for middle moving average.
/// </summary>
public int MiddleMaPeriod
{
get => _middleMaPeriod.Value;
set => _middleMaPeriod.Value = value;
}
/// <summary>
/// Period for long moving average.
/// </summary>
public int LongMaPeriod
{
get => _longMaPeriod.Value;
set => _longMaPeriod.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize the Triple MA strategy.
/// </summary>
public TripleMAStrategy()
{
_shortMaPeriod = Param(nameof(ShortMaPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Short MA Period", "Period for short moving average", "Indicators")
.SetOptimize(3, 10, 1);
_middleMaPeriod = Param(nameof(MiddleMaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Middle MA Period", "Period for middle moving average", "Indicators")
.SetOptimize(15, 30, 5);
_longMaPeriod = Param(nameof(LongMaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Long MA Period", "Period for long moving average", "Indicators")
.SetOptimize(40, 100, 10);
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss (%)", "Stop loss as a percentage of entry price", "Risk parameters")
.SetOptimize(1, 3, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
var shortMa = new ExponentialMovingAverage { Length = ShortMaPeriod };
var middleMa = new ExponentialMovingAverage { Length = MiddleMaPeriod };
var longMa = new ExponentialMovingAverage { Length = LongMaPeriod };
// Create subscription and bind indicators
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(shortMa, middleMa, longMa, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, shortMa);
DrawIndicator(area, middleMa);
DrawIndicator(area, longMa);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, decimal shortMaValue, decimal middleMaValue, decimal longMaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var longSetup = shortMaValue > middleMaValue && shortMaValue > longMaValue;
var shortSetup = shortMaValue < middleMaValue && shortMaValue < longMaValue;
if (Position > 0)
{
// The short MA has crossed below the middle one.
if (shortMaValue < middleMaValue)
SellMarket(shortSetup ? Volume + Position : Position);
}
else if (Position < 0)
{
if (shortMaValue > middleMaValue)
BuyMarket(longSetup ? Volume - Position : -Position);
}
else if (longSetup)
{
BuyMarket(Volume);
}
else if (shortSetup)
{
SellMarket(Volume);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class triple_ma_strategy(Strategy):
"""
Strategy based on Triple Moving Average crossover.
Enters long when the short MA is above both the middle and the long MA, and short when it is below both.
A cross of the short and the middle MA closes the position, and a percent stop protects it.
"""
def __init__(self):
super(triple_ma_strategy, self).__init__()
self._short_ma_period = self.Param("ShortMaPeriod", 5).SetGreaterThanZero().SetDisplay("Short MA Period", "Period for short moving average", "Indicators")
self._middle_ma_period = self.Param("MiddleMaPeriod", 20).SetGreaterThanZero().SetDisplay("Middle MA Period", "Period for middle moving average", "Indicators")
self._long_ma_period = self.Param("LongMaPeriod", 50).SetGreaterThanZero().SetDisplay("Long MA Period", "Period for long moving average", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss (%)", "Stop loss as a percentage of entry price", "Risk parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(triple_ma_strategy, self).OnReseted()
def OnStarted2(self, time):
super(triple_ma_strategy, self).OnStarted2(time)
short_ma = ExponentialMovingAverage()
short_ma.Length = self._short_ma_period.Value
middle_ma = ExponentialMovingAverage()
middle_ma.Length = self._middle_ma_period.Value
long_ma = ExponentialMovingAverage()
long_ma.Length = self._long_ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(short_ma, middle_ma, long_ma, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, short_ma)
self.DrawIndicator(area, middle_ma)
self.DrawIndicator(area, long_ma)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, short_val, middle_val, long_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
long_setup = short_val > middle_val and short_val > long_val
short_setup = short_val < middle_val and short_val < long_val
position = self.Position
if position > 0:
# The short MA has crossed below the middle one.
if short_val < middle_val:
self.SellMarket(self.Volume + position if short_setup else position)
elif position < 0:
if short_val > middle_val:
self.BuyMarket(self.Volume - position if long_setup else -position)
elif long_setup:
self.BuyMarket(self.Volume)
elif short_setup:
self.SellMarket(self.Volume)
def CreateClone(self):
return triple_ma_strategy()