Fractured Fractals 策略
该策略移植自 MetaTrader 上的 “Fractured Fractals” 专家顾问。算法跟踪确认的威廉姆斯分形,在最新突破价位放置止损入场单,并使用相反方向的分形自动跟踪保护性止损单。
详情
- 来源:由
MQL/20127/Fractured Fractals.mq5转换而来。 - 市场环境:适用于任意支持 StockSharp 的品种的突破延续行情。
- 委托类型:入场使用止损委托,离场使用保护性止损委托。
- 仓位规模:基于风险计算,由
MaximumRiskPercent和连续亏损衰减系数DecreaseFactor控制。 - 默认参数:
MaximumRiskPercent= 2%DecreaseFactor= 10ExpirationHours= 1 小时CandleType= 1 小时时间框架
- 核心指标:即时计算的五根 K 线威廉姆斯分形。
- 策略类型:多空双向突破并带动态止损。
策略逻辑
分形序列跟踪
- 维护最近五根 K 线的高点和低点队列,复现 MT5 中的
iFractals缓冲区。 - 每当出现新分形,会将“最新 / 中间 / 最旧”三个槽位前移,并利用品种的最小报价步长忽略重复值。
- 多头条件要求最新上分形高于中间分形;空头条件要求最新下分形低于前一分形。
入场委托与到期
- 当不存在多头头寸或待执行的买入止损单时,在最新上分形放置买入止损,止损价设置为最近的下分形。
- 空头逻辑对称:在最新下分形放置卖出止损,保护性止损位于最近的上分形。
- 未成交的委托带有
ExpirationHours定义的有效期。若收盘时间超过有效期,或结构被新的分形破坏(多头出现更低的上分形、空头出现更高的下分形),委托会被撤销。 - 一旦有仓位建立,会立即取消方向相反的委托,避免帐面遗留多余订单。
保护性跟踪止损
- 每个确认的相反方向分形都会更新保护性止损:多头追踪最新下分形,空头追踪最新上分形。
- 仅在新分形带来更优价格时才会替换旧的止损委托,从而实现“只收紧、不放松”。
- 仓位关闭后,所有剩余止损委托会被立刻取消。
风险管理与亏损序列控制
CalculateOrderVolume复刻了 MT5 的风险计算:单位风险 = 入场价与止损价的差值(空头反向计算)。- 目标风险金额 =
Portfolio.CurrentValue * MaximumRiskPercent / 100,若账户估值不可用则回退到策略的Volume属性。 - 结果数量会依据
Security的手数、成交步长、最小与最大交易量进行归一化。 - 连续亏损会递增计数器,盈利或打平则清零。当亏损次数大于 1 时,仓位规模会按
losses / DecreaseFactor的比例缩减。
交易结果跟踪
- 重写
OnOwnTradeReceived,汇总持仓周期内的成交,判断最终是盈利、亏损还是持平。 - 连续亏损计数与最近盈利时间会同步更新,完全保留原版专家顾问的逻辑,便于进一步统计分析。
使用建议
- 将策略绑定到目标证券与投资组合,按照账户规模调整
CandleType及风险参数。 - 确认经纪商或适配器支持止损委托;若不支持,可在
UpdateTrailingStops中改为手动平仓逻辑。 - 策略仅处理已完成的 K 线,细小的盘中波动不会像 MT5 的逐笔回测那样触发委托。
- 建议开启日志查看移植后的提示信息,以便验证与原 MQL 版本的一致性。
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;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Port of the "Fractured Fractals" MetaTrader strategy using high-level StockSharp API.
/// Places stop orders on newly confirmed fractals and trails the stop with the opposite fractal.
/// </summary>
public class FracturedFractalsStrategy : Strategy
{
private readonly StrategyParam<decimal> _maximumRiskPercent;
private readonly StrategyParam<decimal> _decreaseFactor;
private readonly StrategyParam<int> _expirationHours;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _highBuffer = new();
private readonly List<decimal> _lowBuffer = new();
private decimal? _lastUpFractal;
private decimal? _lastDownFractal;
private decimal? _upYoungest;
private decimal? _upMiddle;
private decimal? _upOld;
private decimal? _downYoungest;
private decimal? _downMiddle;
private decimal? _downOld;
private decimal? _buyStopLevel;
private decimal? _sellStopLevel;
private decimal? _longStopLevel;
private decimal? _shortStopLevel;
private DateTimeOffset? _buyStopExpiry;
private DateTimeOffset? _sellStopExpiry;
private decimal _buyStopVolume;
private decimal _sellStopVolume;
private decimal _entryPrice;
private int _consecutiveLosses;
/// <summary>
/// Maximum risk per trade expressed as percentage of portfolio value.
/// </summary>
public decimal MaximumRiskPercent
{
get => _maximumRiskPercent.Value;
set => _maximumRiskPercent.Value = value;
}
/// <summary>
/// Factor that reduces position size after consecutive losing trades.
/// </summary>
public decimal DecreaseFactor
{
get => _decreaseFactor.Value;
set => _decreaseFactor.Value = value;
}
/// <summary>
/// Pending order lifetime in hours.
/// </summary>
public int ExpirationHours
{
get => _expirationHours.Value;
set => _expirationHours.Value = value;
}
/// <summary>
/// Candle type used for fractal calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="FracturedFractalsStrategy"/> with default parameters.
/// </summary>
public FracturedFractalsStrategy()
{
_maximumRiskPercent = Param(nameof(MaximumRiskPercent), 2m)
.SetRange(0.0001m, 100m)
.SetDisplay("Max Risk %", "Maximum risk per trade", "Risk");
_decreaseFactor = Param(nameof(DecreaseFactor), 10m)
.SetRange(0m, 1000m)
.SetDisplay("Decrease Factor", "Loss streak position size dampener", "Risk");
_expirationHours = Param(nameof(ExpirationHours), 1)
.SetRange(0, 240)
.SetDisplay("Expiration", "Pending order lifetime (hours)", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highBuffer.Clear();
_lowBuffer.Clear();
_lastUpFractal = null;
_lastDownFractal = null;
_upYoungest = null;
_upMiddle = null;
_upOld = null;
_downYoungest = null;
_downMiddle = null;
_downOld = null;
_buyStopLevel = null;
_sellStopLevel = null;
_longStopLevel = null;
_shortStopLevel = null;
_buyStopExpiry = null;
_sellStopExpiry = null;
_buyStopVolume = 0m;
_sellStopVolume = 0m;
_entryPrice = 0m;
_consecutiveLosses = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_highBuffer.Add(candle.HighPrice);
_lowBuffer.Add(candle.LowPrice);
if (_highBuffer.Count > 5)
_highBuffer.RemoveAt(0);
if (_lowBuffer.Count > 5)
_lowBuffer.RemoveAt(0);
if (_highBuffer.Count < 5 || _lowBuffer.Count < 5)
return;
DetectFractals();
// Check protective stop levels
CheckProtectiveStops(candle);
// Validate pending levels
ValidatePendingLevels(candle.CloseTime);
// Check if pending buy/sell stop levels are triggered
CheckPendingTriggers(candle);
// Update trailing stops
UpdateTrailingStops();
// Try to set new pending levels
if (Position == 0)
{
if (!TrySetBuyStopLevel(candle.CloseTime))
TrySetSellStopLevel(candle.CloseTime);
}
}
private void DetectFractals()
{
var highs = _highBuffer.ToArray();
var lows = _lowBuffer.ToArray();
decimal? upFractal = null;
decimal? downFractal = null;
if (highs[2] > highs[0] && highs[2] > highs[1] && highs[2] > highs[3] && highs[2] > highs[4])
upFractal = highs[2];
if (lows[2] < lows[0] && lows[2] < lows[1] && lows[2] < lows[3] && lows[2] < lows[4])
downFractal = lows[2];
if (upFractal is decimal up && !AreEqual(_lastUpFractal, up))
{
_lastUpFractal = up;
_upOld = _upMiddle;
_upMiddle = _upYoungest;
_upYoungest = up;
}
if (downFractal is decimal down && !AreEqual(_lastDownFractal, down))
{
_lastDownFractal = down;
_downOld = _downMiddle;
_downMiddle = _downYoungest;
_downYoungest = down;
}
}
private void CheckProtectiveStops(ICandleMessage candle)
{
if (Position > 0 && _longStopLevel.HasValue)
{
if (candle.LowPrice <= _longStopLevel.Value)
{
SellMarket(Math.Abs(Position));
_longStopLevel = null;
_consecutiveLosses++;
return;
}
}
if (Position < 0 && _shortStopLevel.HasValue)
{
if (candle.HighPrice >= _shortStopLevel.Value)
{
BuyMarket(Math.Abs(Position));
_shortStopLevel = null;
_consecutiveLosses++;
return;
}
}
}
private void UpdateTrailingStops()
{
if (Position > 0 && _downYoungest.HasValue)
{
if (!_longStopLevel.HasValue || _downYoungest.Value > _longStopLevel.Value)
_longStopLevel = _downYoungest.Value;
}
else if (Position <= 0)
{
_longStopLevel = null;
}
if (Position < 0 && _upYoungest.HasValue)
{
if (!_shortStopLevel.HasValue || _upYoungest.Value < _shortStopLevel.Value)
_shortStopLevel = _upYoungest.Value;
}
else if (Position >= 0)
{
_shortStopLevel = null;
}
}
private void ValidatePendingLevels(DateTimeOffset currentTime)
{
if (_buyStopLevel.HasValue && _upYoungest.HasValue)
{
if (_upYoungest.Value < _buyStopLevel.Value && !AreEqual(_upYoungest, _buyStopLevel.Value))
{
_buyStopLevel = null;
_buyStopExpiry = null;
}
}
if (_sellStopLevel.HasValue && _downYoungest.HasValue)
{
if (_downYoungest.Value > _sellStopLevel.Value && !AreEqual(_downYoungest, _sellStopLevel.Value))
{
_sellStopLevel = null;
_sellStopExpiry = null;
}
}
if (_buyStopLevel.HasValue && _buyStopExpiry.HasValue && currentTime >= _buyStopExpiry.Value)
{
_buyStopLevel = null;
_buyStopExpiry = null;
}
if (_sellStopLevel.HasValue && _sellStopExpiry.HasValue && currentTime >= _sellStopExpiry.Value)
{
_sellStopLevel = null;
_sellStopExpiry = null;
}
if (Position != 0)
{
_buyStopLevel = null;
_sellStopLevel = null;
_buyStopExpiry = null;
_sellStopExpiry = null;
}
}
private void CheckPendingTriggers(ICandleMessage candle)
{
if (_buyStopLevel.HasValue && candle.HighPrice >= _buyStopLevel.Value && Position <= 0)
{
var buyLevel = _buyStopLevel.Value;
var vol = _buyStopVolume > 0m ? _buyStopVolume : Volume;
if (vol > 0m)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(vol);
_entryPrice = buyLevel;
_longStopLevel = _downYoungest;
}
_buyStopLevel = null;
_buyStopExpiry = null;
}
if (_sellStopLevel.HasValue && candle.LowPrice <= _sellStopLevel.Value && Position >= 0)
{
var sellLevel = _sellStopLevel.Value;
var vol = _sellStopVolume > 0m ? _sellStopVolume : Volume;
if (vol > 0m)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(vol);
_entryPrice = sellLevel;
_shortStopLevel = _upYoungest;
}
_sellStopLevel = null;
_sellStopExpiry = null;
}
}
private bool TrySetBuyStopLevel(DateTimeOffset time)
{
if (Position > 0 || _buyStopLevel.HasValue)
return false;
if (_upYoungest is not decimal up || _upMiddle is not decimal middle || _downYoungest is not decimal stop)
return false;
if (up <= middle || stop >= up)
return false;
var volume = CalculateOrderVolume(up, stop, Sides.Buy);
if (volume <= 0m)
return false;
_buyStopLevel = up;
_buyStopVolume = volume;
_buyStopExpiry = ExpirationHours > 0 ? time + TimeSpan.FromHours(ExpirationHours) : null;
return true;
}
private void TrySetSellStopLevel(DateTimeOffset time)
{
if (Position < 0 || _sellStopLevel.HasValue)
return;
if (_downYoungest is not decimal down || _downMiddle is not decimal middle || _upYoungest is not decimal stop)
return;
if (down >= middle || stop <= down)
return;
var volume = CalculateOrderVolume(down, stop, Sides.Sell);
if (volume <= 0m)
return;
_sellStopLevel = down;
_sellStopVolume = volume;
_sellStopExpiry = ExpirationHours > 0 ? time + TimeSpan.FromHours(ExpirationHours) : null;
}
private decimal CalculateOrderVolume(decimal entryPrice, decimal stopPrice, Sides direction)
{
var riskPerUnit = direction == Sides.Buy ? entryPrice - stopPrice : stopPrice - entryPrice;
if (riskPerUnit <= 0m)
return 0m;
var portfolioValue = Portfolio?.CurrentValue ?? 0m;
if (portfolioValue <= 0m)
portfolioValue = Volume > 0m ? Volume * entryPrice : 0m;
var riskAmount = portfolioValue * (MaximumRiskPercent / 100m);
if (riskAmount <= 0m)
return 0m;
var volume = riskAmount / riskPerUnit;
if (DecreaseFactor > 0m && _consecutiveLosses > 1)
volume -= volume * (_consecutiveLosses / DecreaseFactor);
if (volume <= 0m)
return 0m;
return Math.Max(volume, Volume > 0 ? Volume : 1m);
}
private bool AreEqual(decimal? first, decimal second)
{
if (first is not decimal value)
return false;
var step = Security?.PriceStep ?? 0.00000001m;
return Math.Abs(value - second) <= step / 2m;
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
if (trade?.Trade == null) return;
if (Position != 0m && _entryPrice == 0m)
_entryPrice = trade.Trade.Price;
if (Position == 0m)
_entryPrice = 0m;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class fractured_fractals_strategy(Strategy):
def __init__(self):
super(fractured_fractals_strategy, self).__init__()
self._maximum_risk_percent = self.Param("MaximumRiskPercent", 2.0)
self._decrease_factor = self.Param("DecreaseFactor", 10.0)
self._expiration_hours = self.Param("ExpirationHours", 1)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._high_buffer = []
self._low_buffer = []
self._last_up_fractal = None
self._last_down_fractal = None
self._up_youngest = None
self._up_middle = None
self._up_old = None
self._down_youngest = None
self._down_middle = None
self._down_old = None
self._buy_stop_level = None
self._sell_stop_level = None
self._long_stop_level = None
self._short_stop_level = None
self._buy_stop_expiry = None
self._sell_stop_expiry = None
self._buy_stop_volume = 0.0
self._sell_stop_volume = 0.0
self._entry_price = 0.0
self._consecutive_losses = 0
@property
def MaximumRiskPercent(self):
return self._maximum_risk_percent.Value
@property
def DecreaseFactor(self):
return self._decrease_factor.Value
@property
def ExpirationHours(self):
return self._expiration_hours.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(fractured_fractals_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def OnOwnTradeReceived(self, trade):
super(fractured_fractals_strategy, self).OnOwnTradeReceived(trade)
if trade is None or trade.Trade is None:
return
pos = float(self.Position)
if pos != 0 and self._entry_price == 0.0:
self._entry_price = float(trade.Trade.Price)
if pos == 0:
self._entry_price = 0.0
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._high_buffer.append(float(candle.HighPrice))
self._low_buffer.append(float(candle.LowPrice))
if len(self._high_buffer) > 5:
self._high_buffer.pop(0)
if len(self._low_buffer) > 5:
self._low_buffer.pop(0)
if len(self._high_buffer) < 5 or len(self._low_buffer) < 5:
return
self._detect_fractals()
self._check_protective_stops(candle)
self._validate_pending_levels(candle.CloseTime)
self._check_pending_triggers(candle)
self._update_trailing_stops()
if float(self.Position) == 0:
if not self._try_set_buy_stop_level(candle.CloseTime):
self._try_set_sell_stop_level(candle.CloseTime)
def _detect_fractals(self):
highs = list(self._high_buffer)
lows = list(self._low_buffer)
up_fractal = None
down_fractal = None
if highs[2] > highs[0] and highs[2] > highs[1] and highs[2] > highs[3] and highs[2] > highs[4]:
up_fractal = highs[2]
if lows[2] < lows[0] and lows[2] < lows[1] and lows[2] < lows[3] and lows[2] < lows[4]:
down_fractal = lows[2]
if up_fractal is not None and not self._are_equal(self._last_up_fractal, up_fractal):
self._last_up_fractal = up_fractal
self._up_old = self._up_middle
self._up_middle = self._up_youngest
self._up_youngest = up_fractal
if down_fractal is not None and not self._are_equal(self._last_down_fractal, down_fractal):
self._last_down_fractal = down_fractal
self._down_old = self._down_middle
self._down_middle = self._down_youngest
self._down_youngest = down_fractal
def _check_protective_stops(self, candle):
pos = float(self.Position)
if pos > 0 and self._long_stop_level is not None:
if float(candle.LowPrice) <= self._long_stop_level:
self.SellMarket(abs(pos))
self._long_stop_level = None
self._consecutive_losses += 1
return
if pos < 0 and self._short_stop_level is not None:
if float(candle.HighPrice) >= self._short_stop_level:
self.BuyMarket(abs(pos))
self._short_stop_level = None
self._consecutive_losses += 1
return
def _update_trailing_stops(self):
pos = float(self.Position)
if pos > 0 and self._down_youngest is not None:
if self._long_stop_level is None or self._down_youngest > self._long_stop_level:
self._long_stop_level = self._down_youngest
elif pos <= 0:
self._long_stop_level = None
if pos < 0 and self._up_youngest is not None:
if self._short_stop_level is None or self._up_youngest < self._short_stop_level:
self._short_stop_level = self._up_youngest
elif pos >= 0:
self._short_stop_level = None
def _validate_pending_levels(self, current_time):
if self._buy_stop_level is not None and self._up_youngest is not None:
if self._up_youngest < self._buy_stop_level and not self._are_equal(self._up_youngest, self._buy_stop_level):
self._buy_stop_level = None
self._buy_stop_expiry = None
if self._sell_stop_level is not None and self._down_youngest is not None:
if self._down_youngest > self._sell_stop_level and not self._are_equal(self._down_youngest, self._sell_stop_level):
self._sell_stop_level = None
self._sell_stop_expiry = None
if self._buy_stop_level is not None and self._buy_stop_expiry is not None and current_time >= self._buy_stop_expiry:
self._buy_stop_level = None
self._buy_stop_expiry = None
if self._sell_stop_level is not None and self._sell_stop_expiry is not None and current_time >= self._sell_stop_expiry:
self._sell_stop_level = None
self._sell_stop_expiry = None
if float(self.Position) != 0:
self._buy_stop_level = None
self._sell_stop_level = None
self._buy_stop_expiry = None
self._sell_stop_expiry = None
def _check_pending_triggers(self, candle):
pos = float(self.Position)
if self._buy_stop_level is not None and float(candle.HighPrice) >= self._buy_stop_level and pos <= 0:
buy_level = self._buy_stop_level
vol = self._buy_stop_volume if self._buy_stop_volume > 0 else float(self.Volume)
if vol > 0:
if pos < 0:
self.BuyMarket(abs(pos))
self.BuyMarket(vol)
self._entry_price = buy_level
self._long_stop_level = self._down_youngest
self._buy_stop_level = None
self._buy_stop_expiry = None
pos = float(self.Position)
if self._sell_stop_level is not None and float(candle.LowPrice) <= self._sell_stop_level and pos >= 0:
sell_level = self._sell_stop_level
vol = self._sell_stop_volume if self._sell_stop_volume > 0 else float(self.Volume)
if vol > 0:
if pos > 0:
self.SellMarket(abs(pos))
self.SellMarket(vol)
self._entry_price = sell_level
self._short_stop_level = self._up_youngest
self._sell_stop_level = None
self._sell_stop_expiry = None
def _try_set_buy_stop_level(self, time):
pos = float(self.Position)
if pos > 0 or self._buy_stop_level is not None:
return False
if self._up_youngest is None or self._up_middle is None or self._down_youngest is None:
return False
up = self._up_youngest
middle = self._up_middle
stop = self._down_youngest
if up <= middle or stop >= up:
return False
volume = self._calculate_order_volume(up, stop, True)
if volume <= 0:
return False
self._buy_stop_level = up
self._buy_stop_volume = volume
if self.ExpirationHours > 0:
self._buy_stop_expiry = time + TimeSpan.FromHours(self.ExpirationHours)
else:
self._buy_stop_expiry = None
return True
def _try_set_sell_stop_level(self, time):
pos = float(self.Position)
if pos < 0 or self._sell_stop_level is not None:
return
if self._down_youngest is None or self._down_middle is None or self._up_youngest is None:
return
down = self._down_youngest
middle = self._down_middle
stop = self._up_youngest
if down >= middle or stop <= down:
return
volume = self._calculate_order_volume(down, stop, False)
if volume <= 0:
return
self._sell_stop_level = down
self._sell_stop_volume = volume
if self.ExpirationHours > 0:
self._sell_stop_expiry = time + TimeSpan.FromHours(self.ExpirationHours)
else:
self._sell_stop_expiry = None
def _calculate_order_volume(self, entry_price, stop_price, is_buy):
if is_buy:
risk_per_unit = entry_price - stop_price
else:
risk_per_unit = stop_price - entry_price
if risk_per_unit <= 0:
return 0.0
portfolio = self.Portfolio
portfolio_value = 0.0
if portfolio is not None and portfolio.CurrentValue is not None:
portfolio_value = float(portfolio.CurrentValue)
if portfolio_value <= 0:
vol = float(self.Volume)
portfolio_value = vol * entry_price if vol > 0 else 0.0
risk_amount = portfolio_value * (float(self.MaximumRiskPercent) / 100.0)
if risk_amount <= 0:
return 0.0
volume = risk_amount / risk_per_unit
if float(self.DecreaseFactor) > 0 and self._consecutive_losses > 1:
volume -= volume * (self._consecutive_losses / float(self.DecreaseFactor))
if volume <= 0:
return 0.0
min_vol = float(self.Volume) if float(self.Volume) > 0 else 1.0
return max(volume, min_vol)
def _are_equal(self, first, second):
if first is None:
return False
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None else 0.00000001
return abs(first - second) <= step / 2.0
def OnReseted(self):
super(fractured_fractals_strategy, self).OnReseted()
self._high_buffer = []
self._low_buffer = []
self._last_up_fractal = None
self._last_down_fractal = None
self._up_youngest = None
self._up_middle = None
self._up_old = None
self._down_youngest = None
self._down_middle = None
self._down_old = None
self._buy_stop_level = None
self._sell_stop_level = None
self._long_stop_level = None
self._short_stop_level = None
self._buy_stop_expiry = None
self._sell_stop_expiry = None
self._buy_stop_volume = 0.0
self._sell_stop_volume = 0.0
self._entry_price = 0.0
self._consecutive_losses = 0
def CreateClone(self):
return fractured_fractals_strategy()