BreakRevert Pro 策略
BreakRevert Pro 是 MetaTrader 5 专家顾问 BreakRevertPro.mq5 的 StockSharp 版本。策略将 1 分钟级别的突破筛选与 15 分钟和 1 小时级别的趋势、波动率分析结合在一起。原始 EA 中的概率指标通过高层 API 指标进行了等效近似,从而在遵守 StockSharp 架构的同时最大限度地保留原有思路。
核心逻辑
- 主周期(1 分钟)
- ATR 估算当前的日内波动幅度。
- 收盘价的移动平均用于衡量短期趋势。
- 第二条移动平均统计大幅波动出现的频率,等效于原程序中的泊松突破概率。
- 绝对价差的指数移动平均提供指数型概率,用于安全过滤器。
- 确认周期(15 分钟)
- 简单移动平均刻画中期方向,阻止逆势交易。
- 背景周期(1 小时)
- 小时级别的最高价与最低价范围决定整体波动环境,并帮助判断区间行情是否适合做反转。
当泊松与“魏布尔”代理概率高于突破阈值,同时 M1 与 M15 趋势向上、H1 波动率放大时,策略通过市价单开多;当概率下降至均值回归阈值以下且 H1 趋势趋于平缓时,策略通过市价单开空,博弈价格回到区间。市价执行与原始 MT5 顾问保持一致。
风险控制
TradeDelaySeconds设定交易之间的最小时间间隔,避免过度交易。MaxPositions限制同向持仓数量。若出现反向信号,会一次性平掉旧仓并建立新仓位。- 头寸规模根据账户余额、ATR 以及
RiskPerTrade参数动态计算。若无法得到合理结果,则回退到品种的最小交易步长。 EnableSafetyTrade可在验证环境中打开,确保在没有信号时也能生成最少一笔交易。方向依据 M1 与 M15 趋势之和。StartProtection()启用 StockSharp 的保护机制,防止连接异常造成的悬挂仓位。
参数说明
| 参数 | 含义 |
|---|---|
RiskPerTrade |
每笔交易的风险百分比,用于计算下单手数。 |
LookbackPeriod |
参与所有移动统计的历史蜡烛数量。 |
BreakoutThreshold |
触发突破入场所需的最低综合概率。 |
MeanReversionThreshold |
允许执行均值回归空单的最高概率。 |
TradeDelaySeconds |
连续入场之间的最小秒数。 |
MaxPositions |
最大持仓数量(对多空皆适用)。 |
EnableSafetyTrade |
是否在无信号时启用安全性交易。 |
SafetyTradeIntervalSeconds |
安全性交易检查间隔。 |
CandleType |
主数据周期(默认 1 分钟)。 |
使用建议
- 在具备 1 分钟行情的品种上运行,StockSharp 会自动聚合 15 分钟与 1 小时数据。
- 若需要固定手数,可直接设置
Volume;否则系统会按照风险百分比自动计算。 - 根据交易品种调整阈值与周期。高波动市场适合提高阈值以减少假突破。
- 常规实盘通常无需安全性交易功能,它主要用于验证或回测环境。
该移植方案保留了“突破 + 回归”双重策略的核心思想,并充分利用 StockSharp 高层 API 的优势,方便测试与部署。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// BreakRevert Pro strategy converted from the MetaTrader 5 expert advisor.
/// The strategy blends breakout and mean-reversion logic using multi-timeframe candles.
/// </summary>
public class BreakRevertProStrategy : Strategy
{
private readonly StrategyParam<decimal> _riskPerTrade;
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<decimal> _breakoutThreshold;
private readonly StrategyParam<decimal> _meanReversionThreshold;
private readonly StrategyParam<int> _tradeDelaySeconds;
private readonly StrategyParam<int> _maxPositions;
private readonly StrategyParam<bool> _enableSafetyTrade;
private readonly StrategyParam<int> _safetyTradeIntervalSeconds;
private readonly StrategyParam<DataType> _candleType;
private ISubscriptionHandler<ICandleMessage> _m1Subscription;
private ISubscriptionHandler<ICandleMessage> _m15Subscription;
private ISubscriptionHandler<ICandleMessage> _h1Subscription;
private AverageTrueRange _m1Atr;
private SimpleMovingAverage _m1TrendAverage;
private SimpleMovingAverage _m15TrendAverage;
private SimpleMovingAverage _h1TrendAverage;
private SimpleMovingAverage _eventFrequency;
private ExponentialMovingAverage _volatilityEma;
private decimal _poissonProbability = 0.5m;
private decimal _weibullProbability = 0.5m;
private decimal _exponentialProbability = 0.5m;
private decimal _m1Trend;
private decimal _m15Trend;
private decimal _h1Trend;
private decimal _h1Volatility;
private decimal? _previousM1Close;
private decimal _latestAtr;
private DateTimeOffset? _lastTradeTime;
private DateTimeOffset? _lastSafetyCheck;
private bool _safetyTradeSent;
/// <summary>
/// Initializes a new instance of the <see cref="BreakRevertProStrategy"/> class.
/// </summary>
public BreakRevertProStrategy()
{
_riskPerTrade = Param(nameof(RiskPerTrade), 1m)
.SetDisplay("Risk %", "Risk per trade as percentage of portfolio value", "Risk")
.SetOptimize(0.5m, 5m, 0.5m);
_lookbackPeriod = Param(nameof(LookbackPeriod), 20)
.SetRange(10, 60)
.SetDisplay("Lookback", "Number of finished candles used for statistics", "Signals")
;
_breakoutThreshold = Param(nameof(BreakoutThreshold), 0.1m)
.SetDisplay("Breakout Threshold", "Minimum composite probability required for breakout entries", "Signals")
.SetOptimize(0.2m, 0.8m, 0.05m);
_meanReversionThreshold = Param(nameof(MeanReversionThreshold), 0.6m)
.SetDisplay("Reversion Threshold", "Maximum probability that still allows mean-reversion trades", "Signals")
.SetOptimize(0.2m, 0.8m, 0.05m);
_tradeDelaySeconds = Param(nameof(TradeDelaySeconds), 300)
.SetDisplay("Trade Delay", "Minimum delay between consecutive entries (seconds)", "Risk");
_maxPositions = Param(nameof(MaxPositions), 1)
.SetDisplay("Max Positions", "Maximum number of simultaneously open positions", "Risk");
_enableSafetyTrade = Param(nameof(EnableSafetyTrade), true)
.SetDisplay("Safety Trade", "Allow protective trades when validation requires at least one position", "Safety");
_safetyTradeIntervalSeconds = Param(nameof(SafetyTradeIntervalSeconds), 900)
.SetDisplay("Safety Interval", "Delay between safety trade checks (seconds)", "Safety");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Primary Candles", "Primary timeframe for signal generation", "Data");
}
/// <summary>
/// Gets or sets the risk per trade in percent.
/// </summary>
public decimal RiskPerTrade
{
get => _riskPerTrade.Value;
set => _riskPerTrade.Value = value;
}
/// <summary>
/// Gets or sets the number of candles used in rolling calculations.
/// </summary>
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
/// <summary>
/// Gets or sets the breakout probability threshold.
/// </summary>
public decimal BreakoutThreshold
{
get => _breakoutThreshold.Value;
set => _breakoutThreshold.Value = value;
}
/// <summary>
/// Gets or sets the mean-reversion probability threshold.
/// </summary>
public decimal MeanReversionThreshold
{
get => _meanReversionThreshold.Value;
set => _meanReversionThreshold.Value = value;
}
/// <summary>
/// Gets or sets the minimum delay between trades.
/// </summary>
public int TradeDelaySeconds
{
get => _tradeDelaySeconds.Value;
set => _tradeDelaySeconds.Value = value;
}
/// <summary>
/// Gets or sets the maximum simultaneous positions.
/// </summary>
public int MaxPositions
{
get => _maxPositions.Value;
set => _maxPositions.Value = value;
}
/// <summary>
/// Gets or sets a value indicating whether safety trades are allowed.
/// </summary>
public bool EnableSafetyTrade
{
get => _enableSafetyTrade.Value;
set => _enableSafetyTrade.Value = value;
}
/// <summary>
/// Gets or sets the safety trade interval in seconds.
/// </summary>
public int SafetyTradeIntervalSeconds
{
get => _safetyTradeIntervalSeconds.Value;
set => _safetyTradeIntervalSeconds.Value = value;
}
/// <summary>
/// Gets or sets the primary candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_m1Subscription = null;
_m15Subscription = null;
_h1Subscription = null;
_m1Atr = null;
_m1TrendAverage = null;
_m15TrendAverage = null;
_h1TrendAverage = null;
_eventFrequency = null;
_volatilityEma = null;
_poissonProbability = 0.5m;
_weibullProbability = 0.5m;
_exponentialProbability = 0.5m;
_m1Trend = 0m;
_m15Trend = 0m;
_h1Trend = 0m;
_h1Volatility = 0m;
_previousM1Close = null;
_latestAtr = 0m;
_lastTradeTime = null;
_lastSafetyCheck = null;
_safetyTradeSent = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var lookback = Math.Max(1, LookbackPeriod);
_m1Atr = new AverageTrueRange { Length = lookback };
_m1TrendAverage = new SimpleMovingAverage { Length = lookback };
_m15TrendAverage = new SimpleMovingAverage { Length = lookback };
_h1TrendAverage = new SimpleMovingAverage { Length = lookback };
_eventFrequency = new SimpleMovingAverage { Length = lookback };
_volatilityEma = new ExponentialMovingAverage { Length = lookback };
// Subscribe to the main one-minute flow.
_m1Subscription = SubscribeCandles(CandleType);
_m1Subscription
.Bind(_m1Atr, ProcessPrimaryCandle)
.Start();
// Additional fifteen-minute stream provides mid-term trend confirmation.
_m15Subscription = SubscribeCandles(TimeSpan.FromMinutes(15).TimeFrame());
_m15Subscription
.Bind(ProcessM15Candle)
.Start();
// Hourly candles track the broader context and volatility envelope.
_h1Subscription = SubscribeCandles(TimeSpan.FromHours(1).TimeFrame());
_h1Subscription
.Bind(ProcessH1Candle)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
}
private void ProcessPrimaryCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
_latestAtr = atrValue;
var close = candle.ClosePrice;
var time = candle.CloseTime;
var pip = GetPipSize();
if (_m1TrendAverage is not null)
{
var trendValue = _m1TrendAverage.Process(new DecimalIndicatorValue(_m1TrendAverage, close, time) { IsFinal = true }).ToDecimal();
if (_m1TrendAverage.IsFormed)
_m1Trend = close - trendValue;
}
if (_previousM1Close is decimal previousClose)
{
var move = Math.Abs(close - previousClose);
var eventValue = move >= pip * 5m ? 1m : 0m;
if (_eventFrequency is not null)
{
var avg = _eventFrequency.Process(new DecimalIndicatorValue(_eventFrequency, eventValue, time) { IsFinal = true }).ToDecimal();
if (_eventFrequency.IsFormed)
_poissonProbability = Clamp(avg, 0m, 1m);
}
if (_volatilityEma is not null)
{
var ema = _volatilityEma.Process(new DecimalIndicatorValue(_volatilityEma, move, time) { IsFinal = true }).ToDecimal();
if (_volatilityEma.IsFormed)
{
var normalized = pip > 0m ? ema / (pip * 10m) : 0m;
_exponentialProbability = Clamp(normalized, 0m, 1m);
}
}
}
_previousM1Close = close;
var normalizedAtr = pip > 0m ? atrValue / (pip * 10m) : 0m;
_weibullProbability = Clamp(normalizedAtr, 0m, 1m);
EvaluateSignals(candle);
}
private void ProcessM15Candle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_m15TrendAverage is null)
return;
var close = candle.ClosePrice;
var trend = _m15TrendAverage.Process(new DecimalIndicatorValue(_m15TrendAverage, close, candle.CloseTime) { IsFinal = true }).ToDecimal();
if (_m15TrendAverage.IsFormed)
_m15Trend = close - trend;
}
private void ProcessH1Candle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_h1Volatility = candle.HighPrice - candle.LowPrice;
if (_h1TrendAverage is null)
return;
var close = candle.ClosePrice;
var trend = _h1TrendAverage.Process(new DecimalIndicatorValue(_h1TrendAverage, close, candle.CloseTime) { IsFinal = true }).ToDecimal();
if (_h1TrendAverage.IsFormed)
_h1Trend = close - trend;
}
private void EvaluateSignals(ICandleMessage candle)
{
var now = candle.CloseTime;
var pip = GetPipSize();
if (_lastTradeTime is DateTimeOffset last && (now - last).TotalSeconds < TradeDelaySeconds)
return;
var tradeVolume = GetTradeVolume();
if (tradeVolume <= 0m)
return;
if (Position != 0)
return;
var breakout = IsBreakoutSignal(pip);
var reversion = IsMeanReversionSignal(pip);
if (breakout)
{
BuyMarket();
_lastTradeTime = now;
}
else if (reversion)
{
SellMarket();
_lastTradeTime = now;
}
}
private bool IsBreakoutSignal(decimal pip)
{
var trendUp = _m1Trend > 0m;
var probabilityOk = _poissonProbability >= BreakoutThreshold || _weibullProbability >= BreakoutThreshold;
return trendUp && probabilityOk;
}
private bool IsMeanReversionSignal(decimal pip)
{
var trendDown = _m1Trend < 0m;
var probabilityOk = _weibullProbability <= MeanReversionThreshold || _poissonProbability <= MeanReversionThreshold;
return trendDown && probabilityOk;
}
private void EnterLong(decimal volume)
{
var totalVolume = volume;
if (Position < 0m)
{
totalVolume += Math.Abs(Position);
}
// Execute a market order to align with the breakout signal.
BuyMarket(totalVolume);
}
private void EnterShort(decimal volume)
{
var totalVolume = volume;
if (Position > 0m)
{
totalVolume += Math.Abs(Position);
}
// Execute a market order to capture the expected pullback.
SellMarket(totalVolume);
}
private void CheckSafetyTrade(DateTimeOffset time, decimal volume)
{
if (!EnableSafetyTrade || _safetyTradeSent || Position != 0m)
return;
if (_lastSafetyCheck is DateTimeOffset last && (time - last).TotalSeconds < SafetyTradeIntervalSeconds)
return;
_lastSafetyCheck = time;
var direction = _m1Trend + _m15Trend;
if (direction > 0m)
{
BuyMarket(volume);
}
else
{
SellMarket(volume);
}
_safetyTradeSent = true;
_lastTradeTime = time;
}
private bool HasReachedMaxExposure(int direction, decimal tradeVolume)
{
if (MaxPositions <= 0 || tradeVolume <= 0m)
return false;
var limit = MaxPositions * tradeVolume;
return direction switch
{
> 0 => Position >= limit,
< 0 => -Position >= limit,
_ => Math.Abs(Position) >= limit,
};
}
private decimal GetTradeVolume()
{
if (Volume > 0m)
return Volume;
var stepVolume = Security?.VolumeStep ?? 1m;
var lotStep = Security?.VolumeStep ?? stepVolume;
var minVolume = Security?.MinVolume ?? stepVolume;
var maxVolume = Security?.MaxVolume ?? decimal.MaxValue;
var balance = Portfolio?.CurrentValue ?? Portfolio?.BeginValue ?? 0m;
var atr = Math.Max(_latestAtr, GetPipSize());
if (stepVolume <= 0m)
stepVolume = 1m;
if (lotStep <= 0m)
lotStep = stepVolume;
if (minVolume <= 0m)
minVolume = stepVolume;
if (balance <= 0m || atr <= 0m)
return minVolume;
var riskAmount = balance * RiskPerTrade / 100m;
if (riskAmount <= 0m)
return minVolume;
var riskPerUnit = atr;
var rawVolume = riskPerUnit > 0m ? riskAmount / riskPerUnit : minVolume;
rawVolume = Math.Max(rawVolume, minVolume);
var normalized = Math.Floor(rawVolume / lotStep) * lotStep;
if (normalized <= 0m)
normalized = minVolume;
if (maxVolume > 0m && normalized > maxVolume)
normalized = maxVolume;
return normalized;
}
private decimal GetPipSize()
{
var step = Security?.PriceStep;
if (step is null || step.Value <= 0m)
return 0.0001m;
return step.Value;
}
private static decimal Clamp(decimal value, decimal min, decimal max)
{
if (value < min)
return min;
return value > max ? max : value;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import AverageTrueRange, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class break_revert_pro_strategy(Strategy):
def __init__(self):
super(break_revert_pro_strategy, self).__init__()
self._risk_per_trade = self.Param("RiskPerTrade", 1.0) \
.SetDisplay("Risk %", "Risk per trade as percentage of portfolio value", "Risk")
self._lookback_period = self.Param("LookbackPeriod", 20) \
.SetDisplay("Lookback", "Number of finished candles used for statistics", "Signals")
self._breakout_threshold = self.Param("BreakoutThreshold", 0.1) \
.SetDisplay("Breakout Threshold", "Minimum composite probability required for breakout entries", "Signals")
self._mean_reversion_threshold = self.Param("MeanReversionThreshold", 0.6) \
.SetDisplay("Reversion Threshold", "Maximum probability that still allows mean-reversion trades", "Signals")
self._trade_delay_seconds = self.Param("TradeDelaySeconds", 300) \
.SetDisplay("Trade Delay", "Minimum delay between consecutive entries (seconds)", "Risk")
self._max_positions = self.Param("MaxPositions", 1) \
.SetDisplay("Max Positions", "Maximum number of simultaneously open positions", "Risk")
self._enable_safety_trade = self.Param("EnableSafetyTrade", True) \
.SetDisplay("Safety Trade", "Allow protective trades", "Safety")
self._safety_trade_interval_seconds = self.Param("SafetyTradeIntervalSeconds", 900) \
.SetDisplay("Safety Interval", "Delay between safety trade checks (seconds)", "Safety")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Primary Candles", "Primary timeframe for signal generation", "Data")
self._m1_atr = None
self._m1_trend_sma = None
self._poisson_probability = 0.5
self._weibull_probability = 0.5
self._exponential_probability = 0.5
self._m1_trend = 0.0
self._m15_trend = 0.0
self._h1_trend = 0.0
self._h1_volatility = 0.0
self._previous_m1_close = None
self._latest_atr = 0.0
self._last_trade_time = None
self._last_safety_check = None
self._safety_trade_sent = False
# manual rolling buffers for event_frequency (SMA) and volatility (EMA)
self._event_buffer = []
self._vol_ema = None
self._vol_ema_k = 0.0
self._lookback = 20
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RiskPerTrade(self):
return self._risk_per_trade.Value
@property
def LookbackPeriod(self):
return self._lookback_period.Value
@property
def BreakoutThreshold(self):
return self._breakout_threshold.Value
@property
def MeanReversionThreshold(self):
return self._mean_reversion_threshold.Value
@property
def TradeDelaySeconds(self):
return self._trade_delay_seconds.Value
@property
def MaxPositions(self):
return self._max_positions.Value
@property
def EnableSafetyTrade(self):
return self._enable_safety_trade.Value
@property
def SafetyTradeIntervalSeconds(self):
return self._safety_trade_interval_seconds.Value
def OnReseted(self):
super(break_revert_pro_strategy, self).OnReseted()
self._m1_atr = None
self._m1_trend_sma = None
self._poisson_probability = 0.5
self._weibull_probability = 0.5
self._exponential_probability = 0.5
self._m1_trend = 0.0
self._m15_trend = 0.0
self._h1_trend = 0.0
self._h1_volatility = 0.0
self._previous_m1_close = None
self._latest_atr = 0.0
self._last_trade_time = None
self._last_safety_check = None
self._safety_trade_sent = False
self._event_buffer = []
self._vol_ema = None
def OnStarted2(self, time):
super(break_revert_pro_strategy, self).OnStarted2(time)
self._lookback = max(1, self.LookbackPeriod)
self._m1_atr = AverageTrueRange()
self._m1_atr.Length = self._lookback
self._m1_trend_sma = SimpleMovingAverage()
self._m1_trend_sma.Length = self._lookback
self._m15_trend_sma = SimpleMovingAverage()
self._m15_trend_sma.Length = self._lookback
self._h1_trend_sma = SimpleMovingAverage()
self._h1_trend_sma.Length = self._lookback
# EMA multiplier for volatility EMA
self._vol_ema_k = 2.0 / (self._lookback + 1.0)
self._vol_ema = None
self._event_buffer = []
# Primary candle subscription with ATR and trend SMA bound
sub1 = self.SubscribeCandles(self.CandleType)
sub1.Bind(self._m1_atr, self._m1_trend_sma, self._process_primary_candle).Start()
# M15 subscription with trend SMA bound
sub15 = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(15)))
sub15.Bind(self._m15_trend_sma, self._process_m15_candle).Start()
# H1 subscription with trend SMA bound
sub_h1 = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromHours(1)))
sub_h1.Bind(self._h1_trend_sma, self._process_h1_candle).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent))
def _get_pip_size(self):
if self.Security is not None and self.Security.PriceStep is not None:
s = float(self.Security.PriceStep)
if s > 0:
return s
return 0.0001
def _clamp(self, value, lo, hi):
if value < lo:
return lo
if value > hi:
return hi
return value
def _process_primary_candle(self, candle, atr_value, trend_sma_value):
if candle.State != CandleStates.Finished:
return
self._latest_atr = float(atr_value)
close = float(candle.ClosePrice)
pip = self._get_pip_size()
# M1 trend: close - SMA(close)
if self._m1_trend_sma is not None and self._m1_trend_sma.IsFormed:
self._m1_trend = close - float(trend_sma_value)
if self._previous_m1_close is not None:
move = abs(close - self._previous_m1_close)
# event_frequency: manual SMA of event values
event_value = 1.0 if move >= pip * 5.0 else 0.0
self._event_buffer.append(event_value)
if len(self._event_buffer) > self._lookback:
self._event_buffer.pop(0)
if len(self._event_buffer) >= self._lookback:
avg = sum(self._event_buffer) / len(self._event_buffer)
self._poisson_probability = self._clamp(avg, 0.0, 1.0)
# volatility EMA: manual EMA of move
if self._vol_ema is None:
self._vol_ema = move
else:
self._vol_ema = move * self._vol_ema_k + self._vol_ema * (1.0 - self._vol_ema_k)
normalized = self._vol_ema / (pip * 10.0) if pip > 0 else 0.0
self._exponential_probability = self._clamp(normalized, 0.0, 1.0)
self._previous_m1_close = close
normalized_atr = self._latest_atr / (pip * 10.0) if pip > 0 else 0.0
self._weibull_probability = self._clamp(normalized_atr, 0.0, 1.0)
self._evaluate_signals(candle)
def _process_m15_candle(self, candle, trend_sma_value):
if candle.State != CandleStates.Finished:
return
if self._m15_trend_sma is not None and self._m15_trend_sma.IsFormed:
self._m15_trend = float(candle.ClosePrice) - float(trend_sma_value)
def _process_h1_candle(self, candle, trend_sma_value):
if candle.State != CandleStates.Finished:
return
self._h1_volatility = float(candle.HighPrice) - float(candle.LowPrice)
if self._h1_trend_sma is not None and self._h1_trend_sma.IsFormed:
self._h1_trend = float(candle.ClosePrice) - float(trend_sma_value)
def _evaluate_signals(self, candle):
now = candle.CloseTime
if self._last_trade_time is not None:
diff = now.Subtract(self._last_trade_time)
if diff.TotalSeconds < self.TradeDelaySeconds:
return
if self.Position != 0:
return
breakout = self._m1_trend > 0 and (self._poisson_probability >= self.BreakoutThreshold or self._weibull_probability >= self.BreakoutThreshold)
reversion = self._m1_trend < 0 and (self._weibull_probability <= self.MeanReversionThreshold or self._poisson_probability <= self.MeanReversionThreshold)
if breakout:
self.BuyMarket()
self._last_trade_time = now
elif reversion:
self.SellMarket()
self._last_trade_time = now
def CreateClone(self):
return break_revert_pro_strategy()