Turtle Trader SAR 将原始的 MQL5 Turtle 系统(可选 Parabolic SAR 追踪)转换为 StockSharp C#。 该策略利用 Donchian 通道突破进行交易,使用 ATR 计算风险并可对盈利头寸进行加仓。
工作原理
- 指标计算
- 使用 20 周期 ATR 衡量波动性。
ShortPeriod与ExitPeriod的 Donchian 通道。- 可选 Parabolic SAR 用于跟踪止损。
- 头寸大小
- 每笔交易风险为账户权益的
RiskFraction。 - 单位数量受
MaxUnits限制。
- 每笔交易风险为账户权益的
- 入场规则
- 收盘价高于
ShortPeriod最高价 → 买入。 - 收盘价低于
ShortPeriod最低价 → 卖出。
- 收盘价高于
- 金字塔加仓
- 价格每向盈利方向移动
AddInterval个 ATR,加仓一次,直至MaxUnits。
- 价格每向盈利方向移动
- 出场规则
ExitPeriod通道的反向突破。- 使用
StopAtr的 ATR 止损及可选TakeAtr止盈。 - 若
UseSar=true,则 Parabolic SAR 触发额外出场。
参数
ExitPeriod= 10ShortPeriod= 20LongPeriod= 55RiskFraction= 0.01MaxUnits= 4AddInterval= 1StopAtr= 1TakeAtr= 1UseSar= falseSarStep= 0.02SarMax= 0.2CandleType= 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()