追踪管理策略
该策略开仓一笔多头头寸,然后使用多种风险控制规则进行管理:
- 基于百分比的 止盈 和 止损。
- 达到设定收益后启动的 跟踪止盈。
- 在自定义盈利水平进行 部分平仓。
该示例演示了如何在 StockSharp 中仅用 K 线数据管理已有头寸。
细节
- 入场条件:第一根收盘 K 线买入。
- 多空方向:仅做多。
- 出场条件:
- 百分比止盈。
- 百分比止损。
- 跟踪止盈触发。
- 部分平仓。
- 止损:有,基于百分比。
- 过滤器:无。
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>
/// EMA trend following with ATR trailing stop management.
/// Enters on EMA crossover, exits via trailing stop based on ATR.
/// </summary>
public class ManagerTrailingStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _trailMult;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
private decimal _trailStop;
private decimal _highSinceLong;
private decimal _lowSinceShort;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal TrailMult { get => _trailMult.Value; set => _trailMult.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ManagerTrailingStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for trailing", "Indicators");
_trailMult = Param(nameof(TrailMult), 2.0m)
.SetDisplay("Trail Mult", "ATR multiplier for trailing stop", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _hasPrev = false;
_trailStop = 0; _highSinceLong = 0; _lowSinceShort = decimal.MaxValue;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var atr = new StandardDeviation { Length = AtrPeriod };
SubscribeCandles(CandleType)
.Bind(fast, slow, atr, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal atr)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
var close = candle.ClosePrice;
// Trail stop management
if (Position > 0 && atr > 0)
{
_highSinceLong = Math.Max(_highSinceLong, candle.HighPrice);
var newTrail = _highSinceLong - atr * TrailMult;
if (newTrail > _trailStop) _trailStop = newTrail;
if (close <= _trailStop)
{
SellMarket();
_trailStop = 0;
_prevFast = fast; _prevSlow = slow;
return;
}
}
else if (Position < 0 && atr > 0)
{
_lowSinceShort = Math.Min(_lowSinceShort, candle.LowPrice);
var newTrail = _lowSinceShort + atr * TrailMult;
if (newTrail < _trailStop || _trailStop == 0) _trailStop = newTrail;
if (close >= _trailStop)
{
BuyMarket();
_trailStop = 0;
_prevFast = fast; _prevSlow = slow;
return;
}
}
// Entry signals: EMA crossover
if (_prevFast <= _prevSlow && fast > slow)
{
if (Position < 0) BuyMarket();
if (Position <= 0)
{
BuyMarket();
_highSinceLong = candle.HighPrice;
_trailStop = close - atr * TrailMult;
}
}
else if (_prevFast >= _prevSlow && fast < slow)
{
if (Position > 0) SellMarket();
if (Position >= 0)
{
SellMarket();
_lowSinceShort = candle.LowPrice;
_trailStop = close + atr * TrailMult;
}
}
_prevFast = fast; _prevSlow = slow;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class manager_trailing_strategy(Strategy):
def __init__(self):
super(manager_trailing_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 8) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period for trailing", "Indicators")
self._trail_mult = self.Param("TrailMult", 2.0) \
.SetDisplay("Trail Mult", "ATR multiplier for trailing stop", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._trail_stop = 0.0
self._high_since_long = 0.0
self._low_since_short = 1e18
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def trail_mult(self):
return self._trail_mult.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(manager_trailing_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._trail_stop = 0.0
self._high_since_long = 0.0
self._low_since_short = 1e18
def OnStarted2(self, time):
super(manager_trailing_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
atr = StandardDeviation()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, 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, fast, slow, atr):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_fast = fast
self._prev_slow = slow
self._has_prev = True
return
close = candle.ClosePrice
# Trail stop management
if self.Position > 0 and atr > 0:
self._high_since_long = max(self._high_since_long, float(candle.HighPrice))
new_trail = self._high_since_long - atr * self.trail_mult
if new_trail > self._trail_stop:
self._trail_stop = new_trail
if close <= self._trail_stop:
self.SellMarket()
self._trail_stop = 0
self._prev_fast = fast
self._prev_slow = slow
return
elif self.Position < 0 and atr > 0:
self._low_since_short = min(self._low_since_short, float(candle.LowPrice))
new_trail = self._low_since_short + atr * self.trail_mult
if new_trail < self._trail_stop or self._trail_stop == 0:
self._trail_stop = new_trail
if close >= self._trail_stop:
self.BuyMarket()
self._trail_stop = 0
self._prev_fast = fast
self._prev_slow = slow
return
# Entry signals: EMA crossover
if self._prev_fast <= self._prev_slow and fast > slow:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
self._high_since_long = float(candle.HighPrice)
self._trail_stop = close - atr * self.trail_mult
elif self._prev_fast >= self._prev_slow and fast < slow:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._low_since_short = float(candle.LowPrice)
self._trail_stop = close + atr * self.trail_mult
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return manager_trailing_strategy()