在 GitHub 上查看
HPCS Inter4 策略 (3518)
概述
该策略将 MetaTrader 专家顾问“_HPCS_IntFourth_MT4_EA_V01_WE”移植到 StockSharp 高级 API。原版脚本在启动后立即建立多头仓位,按照 MetaTrader 点(pip)设置止损和止盈,并在持仓时间达到预设阈值后强制平仓。C# 版本通过保护管理器与 1 秒计时器的组合,完整复刻了上述流程。
交易逻辑
初始化阶段
- 启动时根据交易品种的
PriceStep 与小数位数计算 MetaTrader 点值(对于 5 位或 3 位报价的品种会乘以 10)。
- 调用
StartProtection 设置止损和止盈距离,其中止损距离包含原 EA 中使用 OrderModify 添加的额外缓冲。
- 交易量固定,由
OrderVolume 参数提供。
入场
- 策略启动后立即发送一张市价买单,不再追加新的建仓指令。
- 当收到首笔成交时记录成交时间,用于后续的持仓计时。
出场
- 计时器每秒检查一次当前仓位状态。
- 当持仓时间达到
CloseDelaySeconds 指定的阈值且仍持有多头时,发送市价卖单强制平仓。
- 止损与止盈由保护管理器维护,并使用市价方式执行,与原脚本中调用
OrderClose 的行为一致。
整体逻辑仅参与做多,与 MetaTrader 原策略保持一致。
参数
| 名称 |
说明 |
默认值 |
可优化 |
OrderVolume |
发送初始市价买单时使用的固定交易量。 |
1 |
否 |
StopLossPips |
初始止损的 MetaTrader 点距离。 |
10 |
否 |
ExtraStopPips |
建仓后额外再向下调整的 MetaTrader 点数。 |
10 |
否 |
TakeProfitPips |
止盈目标的 MetaTrader 点距离。 |
10 |
否 |
CloseDelaySeconds |
持仓达到该秒数后触发强制平仓,0 表示关闭该功能。 |
30 |
否 |
实现细节
- 点值计算会在 3 位或 5 位报价品种上乘以 10,以匹配 MetaTrader 的 pip 定义。
StartProtection 以 UnitTypes.Price 为单位管理止损和止盈,并使用市价平仓方式复现原始脚本。
- 在
OnNewMyTrade 中记录首次买入成交时间,并在仓位完全平掉时重置状态,确保计时逻辑正确运行。
- 采用 1 秒周期的计时器来模拟原脚本
OnTick 中的时间检查,即便市场暂时没有报价也能继续倒计时。
- 源码中的注释全部为英文,遵循仓库的统一规范。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Port of the "_HPCS_IntFourth_MT4_EA_V01_WE" MetaTrader expert advisor.
/// Uses SMA crossover to enter positions with time-based exit.
/// </summary>
public class HpcsInter4Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal _prevFast;
private decimal _prevSlow;
private bool _isFirstValue = true;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public HpcsInter4Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Time frame for calculation", "General");
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_isFirstValue = true;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_isFirstValue = true;
var fastSma = new ExponentialMovingAverage { Length = FastPeriod };
var slowSma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastSma, slowSma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastSma);
DrawIndicator(area, slowSma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_isFirstValue)
{
_prevFast = fastValue;
_prevSlow = slowValue;
_isFirstValue = false;
return;
}
// Fast crosses above slow - buy
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
BuyMarket();
}
// Fast crosses below slow - sell
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
SellMarket();
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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 hpcs_inter4_strategy(Strategy):
def __init__(self):
super(hpcs_inter4_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._fast_period = self.Param("FastPeriod", 20)
self._slow_period = self.Param("SlowPeriod", 50)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
def OnReseted(self):
super(hpcs_inter4_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(hpcs_inter4_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self._process_candle).Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if self._has_prev:
# Fast crosses above slow - buy
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
self.BuyMarket()
# Fast crosses below slow - sell
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
self._has_prev = True
def CreateClone(self):
return hpcs_inter4_strategy()