在 GitHub 上查看
3874 Trendcapture 策略
概述
Trendcapture 策略 是 MetaTrader 专家顾问 MQL/7772/Trendcapture.mq4 的 StockSharp 高层 API 迁移版本。原始脚本通过 Parabolic SAR 判断趋势方向,并且只在 ADX 显示弱趋势时进场。每次平仓后,它会根据已实现的盈亏决定下一笔交易的方向,同时在持仓获利达到若干点时,把止损上移到保本价。
本移植版本保留了这些规则,使用 StockSharp 的指标绑定与订单辅助方法,只在选定周期的收盘 K 线上做出决策。
交易逻辑
- 指标
- Parabolic SAR(
ParabolicSar),步长与最大加速度均可配置。
- 平均趋向指数(
AverageDirectionalIndex),用于取得主线数值。
- 开仓条件
- 任意时刻最多持有一张仓位。
- 多头信号要求:
- 上一次平仓后得到的“期望方向”为买入。
- 当前 K 线收盘价高于 SAR 值。
- ADX 主线低于
20,对应原策略中“趋势疲弱”的判定。
- 空头条件完全镜像:期望方向为卖出、收盘价低于 SAR、ADX 低于
20。
- 持仓管理
- 每次成交后立即下达止损与止盈单,距离分别为
StopLossPoints 与 TakeProfitPoints(会根据品种的价格步长换算成绝对价格)。
- 当浮盈达到
GuardPoints 设定的阈值时,撤销原止损并在入场价重新挂出,以复刻原脚本的保本逻辑。
- 平仓后,如果本次交易盈利则保持原方向,若亏损或打平则反向——对应 MQL 代码里的
OrderProfit() 判断。
参数
| 参数 |
含义 |
默认值 |
CandleType |
进行信号运算的 K 线类型。 |
1 小时 K 线 |
SarStep |
Parabolic SAR 的初始加速度。 |
0.02 |
SarMax |
Parabolic SAR 的最大加速度。 |
0.2 |
AdxPeriod |
ADX 的平滑周期。 |
14 |
TakeProfitPoints |
止盈距离(价格步数)。 |
180 |
StopLossPoints |
止损距离(价格步数)。 |
50 |
GuardPoints |
触发保本的浮盈距离(价格步数)。 |
5 |
MaximumRisk |
成交量缩放因子;0.03 与原脚本的头寸大小一致。 |
0.03 |
使用说明
- 请确认所选品种提供
PriceStep(或至少 MinStep),策略才能把点数正确转换成价格。
Volume 属性表示在 MaximumRisk = 0.03 时的基础手数,提高风险系数会按比例放大下单数量。
- 策略只发送市价单,并立即挂出保护性止损/止盈,因此空仓状态下账簿里不会遗留挂单。
- 保本功能通过撤单并在入场价重新挂出止损来实现,对应 MQL 代码中
OrderModify 把止损移动到开仓价的行为。
文件列表
CS/TrendcaptureStrategy.cs —— Trendcapture 策略的 StockSharp 实现。
README.md —— 英文说明。
README_ru.md —— 俄文说明。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trendcapture strategy - EMA trend with ADX strength filter.
/// Buys when close is above EMA and ADX indicates trending.
/// Sells when close is below EMA and ADX indicates trending.
/// </summary>
public class TrendcaptureStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevEma;
private bool _hasPrev;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AdxPeriod { get => _adxPeriod.Value; set => _adxPeriod.Value = value; }
public decimal AdxThreshold { get => _adxThreshold.Value; set => _adxThreshold.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TrendcaptureStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetDisplay("EMA Period", "EMA lookback", "Indicators");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetDisplay("ADX Period", "ADX lookback", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 30m)
.SetDisplay("ADX Threshold", "Minimum ADX for trending", "Levels");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevClose = 0m; _prevEma = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = EmaPeriod };
var slow = new ExponentialMovingAverage { Length = EmaPeriod * 3 };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevClose = fast; _prevEma = slow; _hasPrev = true; return; }
if (_prevClose <= _prevEma && fast > slow && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (_prevClose >= _prevEma && fast < slow && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevClose = fast; _prevEma = 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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class trendcapture_strategy(Strategy):
def __init__(self):
super(trendcapture_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._adx_period = self.Param("AdxPeriod", 14).SetDisplay("ADX Period", "ADX lookback", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 30.0).SetDisplay("ADX Threshold", "Minimum ADX for trending", "Levels")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
@property
def ema_period(self): return self._ema_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(trendcapture_strategy, self).OnReseted()
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
def OnStarted2(self, time):
super(trendcapture_strategy, self).OnStarted2(time)
self._has_prev = False
fast = ExponentialMovingAverage(); fast.Length = self.ema_period
slow = ExponentialMovingAverage(); slow.Length = self.ema_period * 3
sub = self.SubscribeCandles(self.candle_type)
sub.Bind(fast, slow, self.process_candle).Start()
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished: return
f = float(fast); s = float(slow)
if not self._has_prev: self._prev_fast = f; self._prev_slow = s; self._has_prev = True; return
if self._prev_fast <= self._prev_slow and f > s and self.Position <= 0:
if self.Position < 0: self.BuyMarket()
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and f < s and self.Position >= 0:
if self.Position > 0: self.SellMarket()
self.SellMarket()
self._prev_fast = f; self._prev_slow = s
def CreateClone(self): return trendcapture_strategy()