在 GitHub 上查看
AbsolutelyNoLagLWMA Digit MMRec 策略
概述
本策略是 MetaTrader 顾问 Exp_AbsolutelyNoLagLwma_Digit_NN3_MMRec 的 StockSharp 版本。保留了原始的多周期结构(12 小时、4 小时和 2 小时),并实现了同样的 MMRec 资金管理逻辑。三个模块(A/B/C)分别独立管理自己的仓位份额,策略聚合所有模块的净仓位。
每个模块都会在所选价格(收盘价、开盘价、典型价等)上运行两层加权移动平均(先 WMA 再对结果再次 WMA),从而复制 "AbsolutelyNoLagLWMA" 指标。平滑结果会按照指定的小数位数四舍五入。只要平滑线的斜率发生改变(值开始上升或下降),模块就会触发方向切换并执行交易操作。
交易逻辑
- 等待模块时间框架的蜡烛收盘。
- 根据
AppliedPrices 参数选取价格。
- 对价格运行主 WMA,再将输出传给第二个 WMA,得到双重平滑值。
- 将结果四舍五入后与上一值比较。
- 斜率向上 (
value > previous):
- 如果允许平掉空头,关闭该模块的空头仓位份额。
- 若允许做多且当前没有多头仓位,则按当前模块的下单量开多。
- 根据价格步长更新多头的止损与止盈价格。
- 斜率向下 (
value < previous):
- 如果允许平掉多头,关闭该模块的多头仓位份额。
- 若允许做空且当前没有空头仓位,则开空单。
- 更新空头的保护价格。
- 每根蜡烛都会检测最高/最低价是否触发当前的止损或止盈。如果被触发,仓位按照触发价平仓,并将交易结果记录到资金管理队列中。
- 资金管理会保存最近 N 笔(
N 等于触发参数)交易的结果。如果最近 N 笔全部亏损,则下一单使用 SmallVolume,否则使用 NormalVolume。亏损/盈利的判断基于开仓时保存的价格和实际的平仓价格(止损/止盈/信号平仓)。
策略使用市价单进出场:信号触发的订单假定按蜡烛收盘价成交,止损/止盈假定按对应的保护价成交。
参数说明
所有模块拥有相同的参数集合,默认值与原始 MQL 策略一致。
| 参数 |
说明 |
ACandleType / BCandleType / CCandleType |
模块使用的蜡烛周期(默认 12 小时 / 4 小时 / 2 小时)。 |
ALength / BLength / CLength |
AbsolutelyNoLagLWMA 的平滑长度(对两个 WMA 均生效)。 |
AAppliedPrice / BAppliedPrice / CAppliedPrice |
指标所用价格类型(收盘价、开盘价、最高价、最低价、中间价、典型价、加权价、简单价、四分位价、TrendFollow1、TrendFollow2、DeMark)。 |
ADigits / BDigits / CDigits |
平滑值四舍五入时的小数位数。 |
ABuyOpen、ASellOpen、ABuyClose、ASellClose(及 B/C 模块同名参数) |
控制模块是否允许开仓/平仓多头或空头。 |
ASmallVolume、ANormalVolume |
减少后的下单量与常规下单量(对多空共用)。 |
ABuyLossTrigger、ASellLossTrigger |
触发减少仓位的连续亏损次数(分别对应多头与空头)。 |
AStopLossPoints、ATakeProfitPoints |
以价格步长为单位的止损与止盈距离。模块 B/C 拥有同样的参数。 |
当某个触发参数为 0 时,对应方向的亏损队列会被清空。价格步长由 Security.Step 获取,如若未设置,则退化为 1。
实现细节
- 模块各自维护独立的仓位数量,因此不同模块可能出现多空同时存在的情况;策略的净仓位是三个模块仓位的和。
- 止损与止盈在每根完成的蜡烛上使用高/低价进行检查。
AppliedPrices 枚举与原始指标完全一致,包含 TrendFollow 与 DeMark 版本。
- 指标实例通过
Bind 管理,并未添加到策略的公共集合,符合仓库的编码规范。
- 只有当趋势方向发生变化时才会开仓或平仓,避免在连续的相同信号上重复下单。
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>
/// Double WMA crossover strategy with money management recovery.
/// Uses fast and slow weighted moving averages and trades on crossovers.
/// </summary>
public class AbsolutelyNoLagLwmaDigitMmRecStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private int _prevSignal;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
public AbsolutelyNoLagLwmaDigitMmRecStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastLength = Param(nameof(FastLength), 5)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Fast WMA period", "Indicators");
_slowLength = Param(nameof(SlowLength), 14)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Slow WMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSignal = 0;
var fastWma = new WeightedMovingAverage { Length = FastLength };
var slowWma = new WeightedMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastWma, slowWma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastWma);
DrawIndicator(area, slowWma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var signal = fastVal > slowVal ? 1 : fastVal < slowVal ? -1 : _prevSignal;
if (signal == _prevSignal)
return;
var oldSignal = _prevSignal;
_prevSignal = signal;
if (signal == 1 && oldSignal <= 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (signal == -1 && oldSignal >= 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class absolutely_no_lag_lwma_digit_mm_rec_strategy(Strategy):
def __init__(self):
super(absolutely_no_lag_lwma_digit_mm_rec_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_length = self.Param("FastLength", 5) \
.SetDisplay("Fast Length", "Fast WMA period", "Indicators")
self._slow_length = self.Param("SlowLength", 14) \
.SetDisplay("Slow Length", "Slow WMA period", "Indicators")
self._prev_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastLength(self):
return self._fast_length.Value
@property
def SlowLength(self):
return self._slow_length.Value
def OnReseted(self):
super(absolutely_no_lag_lwma_digit_mm_rec_strategy, self).OnReseted()
self._prev_signal = 0
def OnStarted2(self, time):
super(absolutely_no_lag_lwma_digit_mm_rec_strategy, self).OnStarted2(time)
self._prev_signal = 0
fast_wma = WeightedMovingAverage()
fast_wma.Length = self.FastLength
slow_wma = WeightedMovingAverage()
slow_wma.Length = self.SlowLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_wma, slow_wma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_wma)
self.DrawIndicator(area, slow_wma)
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 fv > sv:
signal = 1
elif fv < sv:
signal = -1
else:
signal = self._prev_signal
if signal == self._prev_signal:
return
old_signal = self._prev_signal
self._prev_signal = signal
if signal == 1 and old_signal <= 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif signal == -1 and old_signal >= 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return absolutely_no_lag_lwma_digit_mm_rec_strategy()