在 GitHub 上查看
Wajdyss Ichimoku Candle MMRec 策略
概述
本策略基于 MetaTrader 专家顾问 Exp_wajdyss_Ichimoku_Candle_MMRec 改写而成。它重新计算 Ichimoku 的基准线 Kijun,利用 Highest
和 Lowest 指标获取指定周期内的最高价和最低价,然后为每根收盘蜡烛赋予 4 种颜色之一。当较早的蜡烛位于 Kijun 之上而最新的信号
蜡烛跌破 Kijun 时,策略会平掉空头并考虑做多;相反的颜色切换会触发平多并进入空头。MMRec 模块会在连续出现指定数量的亏损后
把下单手数降低到防御水平。
移植版本完全使用 StockSharp 高级 API,通过一次 SubscribeCandles 订阅即可获取行情,所有判断均在蜡烛收盘后执行,以便在回测
和实时环境中保持一致。
蜡烛着色规则
| 颜色 |
条件 |
含义 |
0 |
收盘价低于 Kijun 且是阴线 |
强烈的空头动能 |
1 |
收盘价低于 Kijun 但为阳线 |
低位反弹 |
2 |
收盘价高于 Kijun 但为阴线 |
高位回落 |
3 |
收盘价高于 Kijun 且为阳线 |
强劲的多头延续 |
信号逻辑
- 做多:
SignalBarShift + 1 位置的颜色大于 1(价格位于 Kijun 之上),而 SignalBarShift 位置的颜色小于 2
(价格跌到 Kijun 下方)。策略可以选择平掉空头并开多。
- 做空:
SignalBarShift + 1 位置的颜色小于 2,而 SignalBarShift 位置的颜色大于 1。策略在需要时平掉多头并开空。
SignalBarShift 与原始 EA 中的 SignalBar 参数一致,默认值 1 表示使用最近两根已完成的蜡烛。增大该值会延迟进场。
资金管理
策略分别为多头和空头保存最近的交易结果。如果最近 LossTriggerCount 笔同方向交易全部亏损,下单手数会切换为 ReducedVolume;
一旦出现盈利或者历史交易数量不足,就恢复到 NormalVolume。该机制对应 MQL 库中的 BuyTradeMMRecounter 与
SellTradeMMRecounter 函数。
风险管理
止损与止盈使用价格步长表示。例如多头持仓时检查是否触及 开仓价 - StopLossPoints * PriceStep 或
开仓价 + TakeProfitPoints * PriceStep,空头逻辑正好相反。检查在每根蜡烛收盘时执行,与原策略使用固定距离的服务器挂单一致。
参数
| 参数 |
说明 |
默认值 |
CandleType |
用于计算指标的蜡烛周期 |
1 小时 |
KijunLength |
Kijun 计算窗口 |
26 |
SignalBarShift |
参与信号判断的蜡烛位移 |
1 |
BuyPosOpen / SellPosOpen |
是否允许开仓 |
true |
BuyPosClose / SellPosClose |
是否允许因反向信号平仓 |
true |
NormalVolume |
默认下单手数 |
1 |
ReducedVolume |
连续亏损后的下单手数 |
0.1 |
LossTriggerCount |
触发缩减手数所需的亏损次数 |
2 |
StopLossPoints |
止损距离(价格步长,0 表示关闭) |
1000 |
TakeProfitPoints |
止盈距离(价格步长,0 表示关闭) |
2000 |
使用提示
- 仅当颜色发生翻转且对应方向被启用时才会开仓。
- 资金管理依赖于策略生成的成交结果,在回测中这些结果会自动更新统计。
- 如果标的物没有定义价格步长,止损与止盈设置会被忽略。
- 将
SignalBarShift 设为 0 可以更快响应最新蜡烛,但可能带来更多噪音信号。
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>
/// Ichimoku-inspired strategy using Highest/Lowest midline (Kijun-Sen concept).
/// Trades when price crosses above/below the midline.
/// </summary>
public class WajdyssIchimokuCandleMmrecStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private decimal? _prevMid;
private decimal? _prevClose;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
public WajdyssIchimokuCandleMmrecStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_period = Param(nameof(Period), 26)
.SetGreaterThanZero()
.SetDisplay("Period", "Kijun-Sen lookback period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMid = null;
_prevClose = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMid = null;
_prevClose = null;
var middle = new SimpleMovingAverage { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(middle, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, middle);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal mid)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevMid = mid;
_prevClose = candle.ClosePrice;
return;
}
var close = candle.ClosePrice;
if (_prevMid == null || _prevClose == null)
{
_prevMid = mid;
_prevClose = close;
return;
}
// Price crosses above midline → buy
if (_prevClose.Value <= _prevMid.Value && close > mid && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Price crosses below midline → sell
else if (_prevClose.Value >= _prevMid.Value && close < mid && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevMid = mid;
_prevClose = close;
}
}
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 wajdyss_ichimoku_candle_mmrec_strategy(Strategy):
def __init__(self):
super(wajdyss_ichimoku_candle_mmrec_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._period = self.Param("Period", 26) \
.SetDisplay("Period", "Kijun-Sen lookback period", "Indicators")
self._prev_mid = None
self._prev_close = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def Period(self):
return self._period.Value
def OnReseted(self):
super(wajdyss_ichimoku_candle_mmrec_strategy, self).OnReseted()
self._prev_mid = None
self._prev_close = None
def OnStarted2(self, time):
super(wajdyss_ichimoku_candle_mmrec_strategy, self).OnStarted2(time)
self._prev_mid = None
self._prev_close = None
middle = SimpleMovingAverage()
middle.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(middle, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, middle)
self.DrawOwnTrades(area)
def _on_process(self, candle, mid_value):
if candle.State != CandleStates.Finished:
return
mv = float(mid_value)
close = float(candle.ClosePrice)
if self._prev_mid is None or self._prev_close is None:
self._prev_mid = mv
self._prev_close = close
return
# Price crosses above midline
if self._prev_close <= self._prev_mid and close > mv and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Price crosses below midline
elif self._prev_close >= self._prev_mid and close < mv and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_mid = mv
self._prev_close = close
def CreateClone(self):
return wajdyss_ichimoku_candle_mmrec_strategy()