Pure Martingale Strategy
This strategy implements a basic martingale system. It opens trades in a random direction and doubles the position size and the stop/take distance after each losing trade. After a winning trade it resets to the initial volume and distance.
The approach assumes that price will eventually return to profitability, but risk grows exponentially. Use only on liquid instruments with tight spreads.
Details
- Entry Criteria:
- No open position: randomly buy or sell on candle close.
- Long/Short: Both.
- Exit Criteria:
- Close when price moves in favor or against the position by the configured distance.
- Stops: Virtual stop loss and take profit managed by the strategy.
- Filters:
- None.
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>
/// Momentum strategy with EMA trend filter.
/// </summary>
public class PureMartingaleStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevPrevClose;
private int _barCount;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public PureMartingaleStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles for trade timing", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevPrevClose = 0;
_barCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
SubscribeCandles(CandleType).Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
_barCount++;
if (_barCount >= 3)
{
// Two consecutive rising closes above EMA => buy
if (close > _prevClose && _prevClose > _prevPrevClose && close > emaValue && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// Two consecutive falling closes below EMA => sell
else if (close < _prevClose && _prevClose < _prevPrevClose && close < emaValue && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
_prevPrevClose = _prevClose;
_prevClose = close;
}
}
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 pure_martingale_strategy(Strategy):
def __init__(self):
super(pure_martingale_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA trend period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles for trade timing", "General")
self._prev_close = 0.0
self._prev_prev_close = 0.0
self._bar_count = 0
@property
def ema_period(self):
return self._ema_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(pure_martingale_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_prev_close = 0.0
self._bar_count = 0
def OnStarted2(self, time):
super(pure_martingale_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
self._bar_count += 1
if self._bar_count >= 3:
# Two consecutive rising closes above EMA => buy
if close > self._prev_close and self._prev_close > self._prev_prev_close and close > ema_value and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Two consecutive falling closes below EMA => sell
elif close < self._prev_close and self._prev_close < self._prev_prev_close and close < ema_value and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_prev_close = self._prev_close
self._prev_close = close
def CreateClone(self):
return pure_martingale_strategy()