Sophia 1_1 is a grid-based martingale trading strategy.
The strategy opens a position after four consecutive candles move in the same direction:
Four rising candles trigger a short entry.
Four falling candles trigger a long entry.
Once in the market, the algorithm adds positions every time the price moves against the current position by a fixed number of price steps (Pip Step).
The volume of each additional trade is multiplied by Lot Exponent, forming a classic martingale grid.
Risk management is handled through Take Profit, Stop Loss and an optional trailing stop.
The trailing mechanism starts after the profit reaches Trail Start and trails the stop level by Trail Stop price steps.
Parameters
Volume – base volume for the first trade.
Pip Step – distance in price steps before adding a new position.
Lot Exponent – multiplier for the volume of each additional trade.
Max Trades – maximum number of positions in the grid.
Take Profit – profit target in price steps from the average entry price.
Stop Loss – loss threshold in price steps from the average entry price.
Use Trailing – enable or disable the trailing stop.
Trail Start – profit required before the trailing stop becomes active.
Trail Stop – distance of the trailing stop in price steps.
Candle Type – timeframe of the candles used for calculations.
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>
/// Grid mean-reversion strategy. Enters on 3-bar momentum, exits at SMA or ATR stop.
/// </summary>
public class Sophia11Strategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prev1, _prev2, _prev3;
public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Sophia11Strategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "SMA for exit target", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR for stops", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prev1 = _prev2 = _prev3 = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var atr = new StandardDeviation { Length = AtrPeriod };
SubscribeCandles(CandleType).Bind(sma, atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal sma, decimal atr)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (_prev3 > 0)
{
// 3-bar declining => counter-trend buy
if (_prev1 < _prev2 && _prev2 < _prev3 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// 3-bar rising => counter-trend sell
else if (_prev1 > _prev2 && _prev2 > _prev3 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
// Exit long at SMA or ATR stop
else if (Position > 0 && (close >= sma || (atr > 0 && close < sma - atr * 3)))
{
SellMarket();
}
// Exit short at SMA or ATR stop
else if (Position < 0 && (close <= sma || (atr > 0 && close > sma + atr * 3)))
{
BuyMarket();
}
}
_prev3 = _prev2;
_prev2 = _prev1;
_prev1 = 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 SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class sophia11_strategy(Strategy):
def __init__(self):
super(sophia11_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 20) \
.SetDisplay("SMA Period", "SMA for exit target", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR for stops", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle type", "General")
self._prev1 = 0.0
self._prev2 = 0.0
self._prev3 = 0.0
@property
def sma_period(self):
return self._sma_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(sophia11_strategy, self).OnReseted()
self._prev1 = 0.0
self._prev2 = 0.0
self._prev3 = 0.0
def OnStarted2(self, time):
super(sophia11_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self.sma_period
atr = StandardDeviation()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, atr, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, sma, atr):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
if self._prev3 > 0:
# 3-bar declining => counter-trend buy
if self._prev1 < self._prev2 and self._prev2 < self._prev3 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# 3-bar rising => counter-trend sell
elif self._prev1 > self._prev2 and self._prev2 > self._prev3 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
# Exit long at SMA or ATR stop
elif self.Position > 0 and (close >= sma or (atr > 0 and close < sma - atr * 3)):
self.SellMarket()
# Exit short at SMA or ATR stop
elif self.Position < 0 and (close <= sma or (atr > 0 and close > sma + atr * 3)):
self.BuyMarket()
self._prev3 = self._prev2
self._prev2 = self._prev1
self._prev1 = close
def CreateClone(self):
return sophia11_strategy()