Fractured Fractals Strategy
Port of the classic MetaTrader "Fractured Fractals" expert advisor. The strategy tracks confirmed Williams fractals, places stop orders on fresh breakout levels, and trails a protective stop on the opposite fractal.
Details
- Source: Converted from
MQL/20127/Fractured Fractals.mq5. - Market Regime: Breakout continuation on any instrument supported by StockSharp.
- Order Types: Uses stop orders for entries and protective stop orders for exits.
- Position Sizing: Risk-based, controlled by
MaximumRiskPercentand the adaptiveDecreaseFactorstreak logic. - Default Parameters:
MaximumRiskPercent= 2%DecreaseFactor= 10ExpirationHours= 1 hourCandleType= 1-hour time frame
- Core Indicators: Native five-bar Williams fractals calculated on the fly.
- Strategy Type: Long/short breakout with dynamic stop management.
Strategy Logic
Fractal sequence tracking
- Maintains queues of the last five candle highs and lows to mimic the
iFractalsbuffer in MT5. - Each confirmed fractal shifts three rolling slots: youngest, middle, and old. Duplicate values are ignored using the instrument price step for tolerance.
- Long signals require the newest up fractal to exceed the middle fractal; short signals require the newest down fractal to be lower than the previous one.
Entry orders and expiration
- When no long position or pending buy stop exists, the strategy places a buy stop at the most recent up fractal with a stop loss at the latest down fractal.
- Symmetrically, short entries place a sell stop at the most recent down fractal with a protective stop at the latest up fractal.
- Pending orders inherit an expiration defined by
ExpirationHours. If the candle time surpasses the expiry or the fractal hierarchy invalidates the setup (new lower up fractal for longs or higher down fractal for shorts), the order is cancelled. - The bot keeps the book clean by cancelling opposite orders once a position opens.
Protective trailing stops
- Every confirmed opposite fractal updates the protective stop order: long positions trail the newest down fractal, short positions trail the newest up fractal.
- Stops are only tightened—new levels must improve over the existing order price before a replacement occurs.
- When the position is closed, any remaining stop orders are cancelled immediately.
Risk management and streak control
CalculateOrderVolumereplicates the MT5 risk calculation: risk per unit = entry price minus stop price (or vice versa for shorts).- Target monetary risk equals
Portfolio.CurrentValue * MaximumRiskPercent / 100with a fallback to theVolumeproperty when portfolio valuation is unavailable. - The resulting volume is normalised by lot size, volume step, minimum volume, and maximum volume constraints exposed by
Security. - After a losing trade the streak counter increments; profitable or flat trades reset the counter. If more than one consecutive loss occurs, the size is scaled down by
losses / DecreaseFactor.
Trade outcome tracking
OnOwnTradeReceivedaggregates fills to determine when a position cycle completes and whether it ended positive, negative, or flat.- The streak counter and the last profitable time stamp mirror the original logic, allowing further extensions (e.g., analytics) if desired.
Usage Notes
- Attach the strategy to any security/portfolio pair, adjust
CandleTypeto the desired resolution, and set the risk parameters according to account size. - Ensure the adapter/broker supports stop orders; otherwise replace the protective orders with manual exits in
UpdateTrailingStops. - Because the implementation processes only finished candles, intra-bar spikes smaller than the candle resolution will not trigger orders exactly as in tick-based MT5 tests.
- Consider enabling logging to review comment messages produced by the C# port, mirroring the original expert's feedback.
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()