在 GitHub 上查看
楔形突破策略
概述
楔形突破策略 将 MetaTrader 专家顾问 Wedge pattern.mq4 迁移到了 StockSharp 高级 API。策略基于比尔·威廉姆斯分形识别对称楔形盘整,并在趋势与动能筛选同时满足时交易突破。
高阶实现保留了原始决策流程,同时使用 StockSharp 提供的管理功能:
- 趋势过滤器:比较基于典型价格计算的快、慢线性加权移动平均(LWMA)。
- 动能过滤器:评估 14 周期动能指标与 100 的距离,最近三次读数中至少一次必须超过阈值。
- MACD 确认:多头要求 MACD 主线高于信号线,空头则相反。
- 分形楔形检测:收集上下分形构建收敛趋势线,收盘价突破并超出确认缓冲区时触发信号。
- 风险控制:提供固定止损/止盈、自动保本(Break-even)以及跟踪止损,与 MQL 版本一致。
工作流程
- 按照
CandleType 参数订阅单一时间框的 K 线。
- 在每根完成的 K 线上更新指标,同时维护高低价缓冲区以侦测新分形。
- 使用最新两个高分形与低分形构建楔形趋势线,仅在高点降低、低点抬升时认定为有效楔形。
- 满足以下条件时开多:
- 快速 LWMA 高于慢速 LWMA;
- MACD 主线高于信号线;
- 最近三次动能读数中任意一次超过阈值;
- 收盘价突破上方趋势线并超过确认缓冲区。
- 空头条件与上述相反。
- 入场后立即设置止损与止盈,随后根据盈利情况自动移动至保本并启动跟踪止损。
参数说明
| 参数 |
含义 |
CandleType |
使用的交易时间框。 |
FastMaPeriod / SlowMaPeriod |
快慢 LWMA 的周期。 |
MomentumPeriod |
动能指标的回溯期。 |
MomentumThreshold |
认定动能有效所需的最小偏离量。 |
MacdFastPeriod / MacdSlowPeriod / MacdSignalPeriod |
MACD 的标准配置。 |
FractalDepth |
确认分形所需的左右柱数量。 |
StopLossPips / TakeProfitPips |
初始止损与止盈(单位:点)。 |
UseBreakeven、BreakevenTriggerPips、BreakevenOffsetPips |
保本功能开关及触发设置。 |
UseTrailing、TrailingActivationPips、TrailingDistancePips、TrailingStepPips |
跟踪止损相关设置。 |
BreakoutBufferPips |
突破确认缓冲距离。 |
所有与点值相关的设置都会根据交易品种的 PriceStep 自动转换为价格距离,能够兼容三位或五位小数的报价格式。
使用建议
- 选择目标品种并设置
CandleType 为期望的时间框(例如 15 分钟)。
- 通过
Strategy.Volume 调整仓位规模。
- 根据市场波动性微调各项过滤与风险参数。
- 启动策略后,它会自动订阅数据、绘制图表并在出现楔形突破时执行交易。
与 MQL 版本的差异
- 使用
SubscribeCandles 与指标绑定,避免逐笔处理。
- 止损、止盈、保本与跟踪功能通过
SetStopLoss / SetTakeProfit 实现,更易与内置风控集成。
- 仅保持单一仓位,不再逐单叠加最多 N 笔订单。
- 去除了原策略中的提示音、邮件和推送,相关通知可在外部实现。
在上述调整下,策略核心逻辑仍忠实复刻了原 MetaTrader 专家顾问,同时符合 StockSharp 的最佳实践。
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Wedge Pattern strategy: WMA crossover + Momentum.
/// Buys when fast WMA crosses above slow WMA and momentum > 100.
/// Sells on cross below with momentum < 100.
/// </summary>
public class WedgePatternStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _momPeriod;
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 int MomPeriod
{
get => _momPeriod.Value;
set => _momPeriod.Value = value;
}
public WedgePatternStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Fast WMA", "Fast WMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 40)
.SetGreaterThanZero()
.SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
_momPeriod = Param(nameof(MomPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Momentum", "Momentum period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new WeightedMovingAverage { Length = FastPeriod };
var slow = new WeightedMovingAverage { Length = SlowPeriod };
var mom = new Momentum { Length = MomPeriod };
decimal? prevFast = null;
decimal? prevSlow = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, mom, (candle, fastVal, slowVal, momVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (prevFast.HasValue && prevSlow.HasValue)
{
var crossUp = prevFast.Value <= prevSlow.Value && fastVal > slowVal;
var crossDown = prevFast.Value >= prevSlow.Value && fastVal < slowVal;
if (crossUp && momVal > 100m && Position <= 0)
BuyMarket();
else if (crossDown && momVal < 100m && Position >= 0)
SellMarket();
}
prevFast = fastVal;
prevSlow = slowVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
}
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, Momentum
from StockSharp.Algo.Strategies import Strategy
class wedge_pattern_strategy(Strategy):
def __init__(self):
super(wedge_pattern_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._fast_period = self.Param("FastPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Fast WMA", "Fast WMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 40) \
.SetGreaterThanZero() \
.SetDisplay("Slow WMA", "Slow WMA period", "Indicators")
self._mom_period = self.Param("MomPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Momentum", "Momentum period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(wedge_pattern_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(wedge_pattern_strategy, self).OnStarted2(time)
self._fast_ind = WeightedMovingAverage()
self._fast_ind.Length = self._fast_period.Value
self._slow_ind = WeightedMovingAverage()
self._slow_ind.Length = self._slow_period.Value
self._mom_ind = Momentum()
self._mom_ind.Length = self._mom_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast_ind, self._slow_ind, self._mom_ind, self._process_candle).Start()
def _process_candle(self, candle, fast_value, slow_value, mom_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
fast_val = float(fast_value)
slow_val = float(slow_value)
mom_val = float(mom_value)
if self._prev_fast is not None and self._prev_slow is not None:
cross_up = self._prev_fast <= self._prev_slow and fast_val > slow_val
cross_down = self._prev_fast >= self._prev_slow and fast_val < slow_val
if cross_up and mom_val > 100.0 and self.Position <= 0:
self.BuyMarket()
elif cross_down and mom_val < 100.0 and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return wedge_pattern_strategy()