MA MACD BB BackTester
Strategy combining three selectable indicators: simple moving average crossover, MACD crossover, or Bollinger Bands breakout. Only one indicator mode is active at a time, and the trade direction can be long or short.
Parameters
CandleType— candle timeframe.Indicator— indicator to use (MA, MACD, BB).Direction— trade direction (Long or Short).MaLength— moving average period.FastLength— MACD fast EMA length.SlowLength— MACD slow EMA length.SignalLength— MACD signal length.BbLength— Bollinger Bands period.BbMultiplier— Bollinger Bands multiplier.StartDate— start date.EndDate— end date.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy using MA crossover with price for entry/exit.
/// </summary>
public class MaMacdBbBackTesterStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maLength;
private ExponentialMovingAverage _ma;
private decimal _prevClose;
private decimal _prevMa;
private bool _initialized;
private int _cooldown;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int MaLength { get => _maLength.Value; set => _maLength.Value = value; }
public MaMacdBbBackTesterStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_maLength = Param(nameof(MaLength), 20)
.SetDisplay("MA Length", "MA period", "Indicators");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevMa = default;
_initialized = false;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ma = new ExponentialMovingAverage { Length = MaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ma.IsFormed)
return;
if (!_initialized)
{
_prevClose = candle.ClosePrice;
_prevMa = maVal;
_initialized = true;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevClose = candle.ClosePrice;
_prevMa = maVal;
return;
}
var crossUp = _prevClose <= _prevMa && candle.ClosePrice > maVal;
var crossDown = _prevClose >= _prevMa && candle.ClosePrice < maVal;
if (crossUp && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_cooldown = 10;
}
else if (crossDown && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_cooldown = 10;
}
_prevClose = candle.ClosePrice;
_prevMa = maVal;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_macd_bb_back_tester_strategy(Strategy):
def __init__(self):
super(ma_macd_bb_back_tester_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._ma_length = self.Param("MaLength", 20) \
.SetDisplay("MA Length", "MA period", "Indicators")
self._prev_close = 0.0
self._prev_ma = 0.0
self._initialized = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(ma_macd_bb_back_tester_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ma = 0.0
self._initialized = False
self._cooldown = 0
def OnStarted2(self, time):
super(ma_macd_bb_back_tester_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_ma = 0.0
self._initialized = False
self._cooldown = 0
self._ma = ExponentialMovingAverage()
self._ma.Length = self._ma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ma, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ma_val):
if candle.State != CandleStates.Finished:
return
if not self._ma.IsFormed:
return
mv = float(ma_val)
close = float(candle.ClosePrice)
if not self._initialized:
self._prev_close = close
self._prev_ma = mv
self._initialized = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_close = close
self._prev_ma = mv
return
cross_up = self._prev_close <= self._prev_ma and close > mv
cross_down = self._prev_close >= self._prev_ma and close < mv
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown = 10
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown = 10
self._prev_close = close
self._prev_ma = mv
def CreateClone(self):
return ma_macd_bb_back_tester_strategy()