Turtle Trader SAR converts the original MQL5 Turtle system with an optional Parabolic SAR trail into StockSharp C#.
The strategy trades breakouts of Donchian channels, sizes positions by ATR based risk and can pyramid winning trades.
How It Works
Indicator Calculation
20 period ATR for volatility.
Donchian channels for ShortPeriod and ExitPeriod.
Optional Parabolic SAR for trailing stops.
Position Sizing
Each entry risks RiskFraction of current equity.
Unit size is limited by MaxUnits.
Entry Rules
Close above ShortPeriod high -> buy.
Close below ShortPeriod low -> sell.
Pyramiding
Adds new unit every AddInterval ATR move in favor until MaxUnits.
Exit Rules
Opposite ExitPeriod breakout.
ATR stop using StopAtr and optional take profit TakeAtr.
If UseSar is true, Parabolic SAR stop also applies.
Parameters
ExitPeriod = 10
ShortPeriod = 20
LongPeriod = 55
RiskFraction = 0.01
MaxUnits = 4
AddInterval = 1
StopAtr = 1
TakeAtr = 1
UseSar = false
SarStep = 0.02
SarMax = 0.2
CandleType = 1 day
Tags
Category: Trend Following
Direction: Both
Indicators: ATR, Highest, Lowest, Parabolic SAR
Stops: ATR / SAR
Complexity: Intermediate
Timeframe: Daily
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>
/// Turtle Trader strategy with Donchian breakout entries and ATR-based stops.
/// </summary>
public class TurtleTraderSarStrategy : Strategy
{
private readonly StrategyParam<int> _shortPeriod;
private readonly StrategyParam<decimal> _stopMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private readonly List<decimal> _closes = new();
private decimal _stopPrice;
public int ShortPeriod { get => _shortPeriod.Value; set => _shortPeriod.Value = value; }
public decimal StopMultiplier { get => _stopMultiplier.Value; set => _stopMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TurtleTraderSarStrategy()
{
_shortPeriod = Param(nameof(ShortPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Short Period", "Donchian breakout period", "General");
_stopMultiplier = Param(nameof(StopMultiplier), 2m)
.SetDisplay("Stop Multiplier", "ATR multiplier for stop", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highs.Clear();
_lows.Clear();
_closes.Clear();
_stopPrice = 0m;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highs.Clear();
_lows.Clear();
_closes.Clear();
_stopPrice = 0m;
var sub = SubscribeCandles(CandleType);
sub.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, sub);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_highs.Add(candle.HighPrice);
_lows.Add(candle.LowPrice);
_closes.Add(candle.ClosePrice);
if (_highs.Count < ShortPeriod + 1)
return;
// Trim to keep memory bounded
while (_highs.Count > ShortPeriod + 10)
{
_highs.RemoveAt(0);
_lows.RemoveAt(0);
_closes.RemoveAt(0);
}
// Compute Donchian channel (excluding current candle)
var len = _highs.Count;
decimal highest = 0, lowest = decimal.MaxValue;
for (int i = len - 1 - ShortPeriod; i < len - 1; i++)
{
if (_highs[i] > highest) highest = _highs[i];
if (_lows[i] < lowest) lowest = _lows[i];
}
// Simple ATR approximation: average of (high-low) over last 20 candles
var atrPeriod = Math.Min(20, len);
decimal sumRange = 0;
for (int i = len - atrPeriod; i < len; i++)
sumRange += _highs[i] - _lows[i];
var atr = sumRange / atrPeriod;
var price = candle.ClosePrice;
// Manage existing position
if (Position > 0 && _stopPrice > 0 && price <= _stopPrice)
{
SellMarket();
return;
}
else if (Position < 0 && _stopPrice > 0 && price >= _stopPrice)
{
BuyMarket();
return;
}
if (Position != 0)
return;
// Breakout entry
if (price > highest && atr > 0)
{
_stopPrice = price - StopMultiplier * atr;
BuyMarket();
}
else if (price < lowest && atr > 0)
{
_stopPrice = price + StopMultiplier * atr;
SellMarket();
}
}
}
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.Strategies import Strategy
class turtle_trader_sar_strategy(Strategy):
def __init__(self):
super(turtle_trader_sar_strategy, self).__init__()
self._short_period = self.Param("ShortPeriod", 20)
self._stop_multiplier = self.Param("StopMultiplier", 2.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._highs = []
self._lows = []
self._closes = []
self._stop_price = 0.0
@property
def ShortPeriod(self):
return self._short_period.Value
@ShortPeriod.setter
def ShortPeriod(self, value):
self._short_period.Value = value
@property
def StopMultiplier(self):
return self._stop_multiplier.Value
@StopMultiplier.setter
def StopMultiplier(self, value):
self._stop_multiplier.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(turtle_trader_sar_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
self._closes = []
self._stop_price = 0.0
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
h = float(candle.HighPrice)
l = float(candle.LowPrice)
c = float(candle.ClosePrice)
self._highs.append(h)
self._lows.append(l)
self._closes.append(c)
period = int(self.ShortPeriod)
if len(self._highs) < period + 1:
return
while len(self._highs) > period + 10:
self._highs.pop(0)
self._lows.pop(0)
self._closes.pop(0)
length = len(self._highs)
highest = 0.0
lowest = float('inf')
for i in range(length - 1 - period, length - 1):
if self._highs[i] > highest:
highest = self._highs[i]
if self._lows[i] < lowest:
lowest = self._lows[i]
atr_period = min(20, length)
sum_range = 0.0
for i in range(length - atr_period, length):
sum_range += self._highs[i] - self._lows[i]
atr = sum_range / atr_period if atr_period > 0 else 0.0
price = c
if self.Position > 0 and self._stop_price > 0.0 and price <= self._stop_price:
self.SellMarket()
return
elif self.Position < 0 and self._stop_price > 0.0 and price >= self._stop_price:
self.BuyMarket()
return
if self.Position != 0:
return
if price > highest and atr > 0.0:
self._stop_price = price - float(self.StopMultiplier) * atr
self.BuyMarket()
elif price < lowest and atr > 0.0:
self._stop_price = price + float(self.StopMultiplier) * atr
self.SellMarket()
def OnReseted(self):
super(turtle_trader_sar_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._closes = []
self._stop_price = 0.0
def CreateClone(self):
return turtle_trader_sar_strategy()