在 GitHub 上查看
Fractured Fractals (MT4) 策略
本项目为 MetaTrader 4 智能交易系统 MQL/7696/Fractured_fractals.mq4 的 C# 高精度移植版本。策略捕捉最新确认的威廉姆斯
分形,在突破位置挂入止损单,并利用上一个分形摆动更新保护性止损。仓位规模遵循原版按风险计算的思路,并在亏损连
续出现时通过 DecreaseFactor 参数自动缩减手数。
详情
- 来源:改写自
MQL/7696/Fractured_fractals.mq4。
- 市场环境:突破跟随策略,适用于能够形成稳定分形结构的任何品种。
- 委托类型:进场采用止损单,离场使用保护性止损单。
- 仓位管理:
MaximumRiskPercent 控制单笔风险百分比,DecreaseFactor 在连亏时降低下次下单量。
- 默认参数:
MaximumRiskPercent = 2%
DecreaseFactor = 3
CandleType = 1 小时时间框架
- 核心指标:策略内部重建的五根 K 线威廉姆斯分形。
- 策略类型:多空对称的突破策略,并结合分形追踪止损。
策略逻辑
分形识别
- 维护最近五根 K 线的高点与低点队列,模拟 MetaTrader 的
iFractals 缓冲区。
- 当中间 K 线高点高于两侧各两根高点时判定为上分形;当中间低点低于两侧各两根低点时判定为下分形。
- 每次发现新分形时,会与前三个历史值一起保存,对应原版中的
cfu、pfu、pfu.1 缓冲,供后续比较和移动止损使用。
入场逻辑
- 多头需要最新上分形高于上一个上分形,同时最新下分形提供止损基准。策略在分形上方(加上点差补偿)挂出 buy stop,
并把保护性止损放在下分形下方。
- 空头完全对称:下分形创更低点而上分形仍处于更高位置时,挂出 sell stop,并把保护性止损放在上分形上方加点差。
- 每个方向仅允许一个挂单。如果分形结构被破坏(例如最新分形不再优于前一个),挂单会立即取消。
止损管理
- 持仓后,策略使用上一分形位置加/减当前点差来移动止损,且只向盈利方向移动。
- 当仓位平掉或反向时,会撤销多余的保护性止损,防止遗留订单。
风险控制
CalculateOrderVolume 完全复刻 EA 中的风控:仓位 = 允许亏损金额 / 入场价与止损价之间的距离。
- 首选
Portfolio.CurrentValue 作为账户净值;若不可用,则使用策略 Volume 乘以价格的回退方案。
- 当连续亏损次数超过 1 时,按照
losses / DecreaseFactor 的比例削减下次手数,模拟 MT4 中的 DecreaseFactor 机制。
交易周期跟踪
OnOwnTradeReceived 将成交归集成交易周期,计算盈亏,并在仓位回到零后更新亏损计数,从而替代原程序对
HistoryTotal 的依赖。
使用提示
- 绑定所需的证券与投资组合,并将
CandleType 调整为与原 MT4 设置一致的周期。
- 建议订阅一级行情,策略使用最优买/卖价估算点差;若行情缺失,会退回到
PriceStep。
- 代码假设经纪商支持服务器端止损单;如需市价平仓,可在相应方法内改为
Buy/SellMarket 调用。
- 逻辑在每根 K 线收盘时执行,因此分形信号也会在收盘时处理,与原版逐 K 线扫描的方式保持一致。
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>
/// Fractured Fractals strategy - Highest/Lowest channel breakout with ATR filter.
/// Buys when close crosses above channel midpoint and ATR confirms volatility.
/// Sells when close crosses below channel midpoint.
/// </summary>
public class FracturedFractalsMql4Strategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevMid;
private bool _hasPrev;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FracturedFractalsMql4Strategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 15)
.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "ATR lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevClose = 0m; _prevMid = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
var mid = (highest + lowest) / 2;
if (!_hasPrev)
{
_prevClose = close;
_prevMid = mid;
_hasPrev = true;
return;
}
// Close crosses above midpoint = buy
if (_prevClose <= _prevMid && close > mid && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Close crosses below midpoint = sell
else if (_prevClose >= _prevMid && close < mid && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevClose = close;
_prevMid = mid;
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class fractured_fractals_mql4_strategy(Strategy):
def __init__(self):
super(fractured_fractals_mql4_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 15).SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14).SetDisplay("ATR Period", "ATR lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
@property
def channel_period(self): return self._channel_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(fractured_fractals_mql4_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(fractured_fractals_mql4_strategy, self).OnStarted2(time)
self._has_prev = False
highest = Highest()
highest.Length = self.channel_period
lowest = Lowest()
lowest.Length = self.channel_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.process_candle).Start()
def process_candle(self, candle, highest, lowest):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
mid = (float(highest) + float(lowest)) / 2.0
if not self._has_prev:
self._prev_close = close
self._prev_mid = mid
self._has_prev = True
return
if self._prev_close <= self._prev_mid and close > mid and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_close >= self._prev_mid and close < mid and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_close = close
self._prev_mid = mid
def CreateClone(self):
return fractured_fractals_mql4_strategy()