在 GitHub 上查看
Iin MA Signal 策略
概述
本策略完整复刻了经典的 Iin MA Signal MQL5 智能交易系统。它跟踪快慢均线的交叉情况,并按照 SignalBar 指定的历史K线索引触发信号,与原始程序通过 CopyBuffer 读取指标缓冲区的方式一致。当出现多头交叉时,策略可以开多并选择性平掉已有空单;出现空头交叉时则相反。此外,还可以利用 StockSharp 的仓位保护功能自动附加止损和止盈。
交易逻辑
- 订阅由
CandleType 指定的单一K线序列(默认 1 小时K线)。
- 根据
FastMaType/FastPeriod 与 SlowMaType/SlowPeriod 创建两条移动平均线。支持 SMA、EMA、SMMA(RMA)与 LWMA,以覆盖 MQL 版本提供的全部选项。
- 维护一个滚动窗口保存均线数值,以便在
SignalBar 对应的K线上评估交叉情况,从而模拟原策略的缓冲区读取。
- 当窗口中上一根K线的快线低于慢线,而信号K线的快线穿越到慢线上方且当前趋势并非多头时,判定为多头交叉;反之则判定为空头交叉。
- 每次确认交叉后更新内部趋势标志,避免重复进场,同时对应 MQL 指标中的
trend 变量作用。
- 当
IsFormedAndOnlineAndAllowTrading() 返回 true 时,再根据入场/出场开关发送市场订单。
入场规则
- 做多:在检测到多头交叉且
AllowLongEntries 为 true 时触发,当前仓位必须为空或做空。若 CloseShortOnSignal 为 true,则会先行平掉持有的空单。
- 做空:在检测到空头交叉且
AllowShortEntries 为 true 时触发,当前仓位必须为空或做多。若 CloseLongOnSignal 为 true,则会先平掉持有的多单。
出场规则
- 根据
CloseLongOnSignal 与 CloseShortOnSignal 的设置,反向信号可以强制平仓。
- 当
StopLossPoints 或 TakeProfitPoints 大于 0 时,策略会调用 StartProtection,按绝对价格距离自动设置止损与止盈,并使用市价单执行。
参数说明
| 参数 |
说明 |
默认值 |
CandleType |
用于计算的K线数据类型。 |
1 小时时间框架 |
FastPeriod |
快速均线周期。 |
10 |
FastMaType |
快速均线类型(Sma、Ema、Smma、Lwma)。 |
Ema |
SlowPeriod |
慢速均线周期。 |
22 |
SlowMaType |
慢速均线类型(Sma、Ema、Smma、Lwma)。 |
Sma |
SignalBar |
用于判断交叉的已完成K线数量(1 对应 MQL 默认值)。 |
1 |
AllowLongEntries |
是否允许多头入场。 |
true |
AllowShortEntries |
是否允许空头入场。 |
true |
CloseLongOnSignal |
触发空头信号时是否平掉多单。 |
true |
CloseShortOnSignal |
触发多头信号时是否平掉空单。 |
true |
StopLossPoints |
止损的绝对价格距离(0 表示关闭)。 |
1000 |
TakeProfitPoints |
止盈的绝对价格距离(0 表示关闭)。 |
2000 |
实现细节
- 全程使用 StockSharp 高级 API:
SubscribeCandles 订阅行情,Bind 直接将均线数值传入策略,无需手动维护历史数据。
- 通过
CreateMa 工厂函数将枚举值映射到对应的均线指标,避免重新实现均线算法。
- 滚动缓冲区只保留
SignalBar + 2 个样本,足以比较信号K线与其前一根K线。
- 仅当止损或止盈距离大于 0 时才启动仓位保护,从而与原 MQL 模板中可选的资金管理模块保持一致。
- 代码注释全部为英文,符合仓库要求。
使用方法
- 编译解决方案(
dotnet build AlgoTrading.sln)以生成新的策略类。
- 在应用程序中实例化
IinMaSignalStrategy,配置参数,并绑定所需的连接器、证券和投资组合,然后启动策略。
- 如需可视化,可将策略附着到图表上,以显示快慢均线及成交记录。
- 可根据不同市场优化均线周期、信号K线与风险参数。
与原始 MQL 专家的差异
- 本实现依赖高级订阅与指标绑定机制,不再手动读取指标缓冲区。
TradeAlgorithms.mqh 中的资金管理函数由 StartProtection 替代,实现相同的止损/止盈自动化。
- 策略默认避免对冲:在相反仓位仍持有时不会开新仓,除非用户禁用相应的平仓开关。
- 图表渲染采用 StockSharp 的辅助方法,并未尝试绘制原指标中的箭头缓冲区。
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>
/// Iin MA Signal strategy. Uses fast/slow SMA crossover.
/// </summary>
public class IinMaSignalStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevSlow;
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 IinMaSignalStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
var fast = new SimpleMovingAverage { Length = FastPeriod };
var slow = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
if (_prevFast == null || _prevSlow == null)
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fastVal > slowVal;
_prevFast = fastVal;
_prevSlow = slowVal;
if (!prevAbove && currAbove && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (prevAbove && !currAbove && Position >= 0)
{
if (Position > 0)
SellMarket();
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.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class iin_ma_signal_strategy(Strategy):
def __init__(self):
super(iin_ma_signal_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 7) \
.SetDisplay("Fast SMA", "Fast SMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow SMA", "Slow SMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(iin_ma_signal_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(iin_ma_signal_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = SimpleMovingAverage()
fast.Length = self.FastPeriod
slow = SimpleMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return iin_ma_signal_strategy()