Turtle Trader SAR — это конвертация оригинальной MQL5 стратегии Turtle с возможностью трейлинг-стопа по Parabolic SAR в StockSharp.
Стратегия торгует пробои каналов Дончиана, рассчитывает размер позиции на основе риска по ATR и наращивает прибыльные позиции.
Как работает
Расчёт индикаторов
ATR(20) для оценки волатильности.
Каналы Дончиана по периодам ShortPeriod и ExitPeriod.
Опционально Parabolic SAR для трейлинг-стопа.
Размер позиции
В каждой сделке используется RiskFraction от текущего капитала.
Количество лотов ограничено параметром MaxUnits.
Вход в рынок
Закрытие выше максимума ShortPeriod — покупка.
Закрытие ниже минимума ShortPeriod — продажа.
Пирамидинг
Каждые AddInterval ATR в сторону профита добавляется новая позиция до MaxUnits.
Выход из рынка
Пробой канала ExitPeriod в противоположную сторону.
Стоп по ATR (StopAtr) и опционный тейк-профит (TakeAtr).
При UseSar=true добавляется трейлинг по Parabolic SAR.
Параметры
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 день
Теги
Категория: Следование тренду
Направление: Оба
Индикаторы: ATR, Highest, Lowest, Parabolic SAR
Стопы: ATR / SAR
Сложность: Средняя
Таймфрейм: Дневной
Сезонность: Нет
Нейросети: Нет
Дивергенция: Нет
Уровень риска: Средний
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()