Стратегия Fractured Fractals
Порт классического советника MetaTrader «Fractured Fractals». Стратегия отслеживает подтверждённые фракталы Вильямса, размещает стоп-заявки на свежих уровнях пробоя и сопровождает позицию стоп-заявкой на противоположном фрактале.
Детали
- Источник: конвертация
MQL/20127/Fractured Fractals.mq5. - Режим рынка: пробой продолжения на любом инструменте, поддерживаемом StockSharp.
- Типы заявок: стоп-заявки для входов и защитные стоп-заявки для выходов.
- Размер позиции: расчёт по риску с параметрами
MaximumRiskPercentи адаптивным коэффициентом серииDecreaseFactor. - Значения по умолчанию:
MaximumRiskPercent= 2%DecreaseFactor= 10ExpirationHours= 1 часCandleType= часовые свечи
- Ключевые индикаторы: встроенные пятисвечные фракталы Вильямса.
- Тип стратегии: пробойная торговля в обе стороны с динамическим стопом.
Логика стратегии
Отслеживание последовательности фракталов
- Поддерживаются очереди из пяти последних максимумов и минимумов свечей, что воспроизводит буфер
iFractalsв MT5. - Каждый подтверждённый фрактал сдвигает тройку значений: «самый новый», «средний» и «старый». Дубликаты игнорируются с учётом шага цены инструмента.
- Для покупки требуется, чтобы новый верхний фрактал был выше среднего; для продажи — новый нижний фрактал ниже предыдущего.
Заявки на вход и срок действия
- При отсутствии длинной позиции и активной заявки ставится buy-stop на последнем верхнем фрактале и защитный стоп на последнем нижнем фрактале.
- Для короткой позиции зеркально размещается sell-stop на последнем нижнем фрактале и защитный стоп на верхнем фрактале.
- Невыполненные заявки получают срок действия
ExpirationHours. Если время свечи превышает срок или иерархия фракталов нарушается (новый более низкий верхний фрактал для лонга, новый более высокий нижний для шорта), заявка отменяется. - После открытия позиции противоположные заявки сразу снимаются, чтобы не оставлять лишних ордеров в стакане.
Защитные трейлинг-стопы
- Каждый подтверждённый противоположный фрактал обновляет защитную стоп-заявку: лонги подтягиваются к новому нижнему фракталу, шорты — к новому верхнему.
- Стопы только ужесточаются: новое значение должно улучшать цену текущей заявки, прежде чем она будет заменена.
- После закрытия позиции все оставшиеся защитные заявки немедленно отменяются.
Управление риском и контроль серии
- Метод
CalculateOrderVolumeповторяет расчёт из MT5: риск на единицу = входная цена минус цена стопа (или наоборот для шорта). - Целевой денежный риск равен
Portfolio.CurrentValue * MaximumRiskPercent / 100с резервом в виде свойстваVolume, если стоимость портфеля недоступна. - Итоговый объём нормализуется по размеру лота, шагу объёма, минимальному и максимальному объёму из объекта
Security. - После убыточной сделки счётчик серии увеличивается; прибыльные и нулевые сделки сбрасывают его. При двух и более подряд убытках объём сокращается на долю
losses / DecreaseFactor.
Фиксация результатов сделок
- Переопределение
OnOwnTradeReceivedагрегирует исполнения, чтобы определить завершение цикла позиции и его итог — прибыль, убыток или ноль. - Счётчик серии и отметка времени последней прибыльной сделки сохраняют логику оригинального советника, позволяя расширять аналитику при необходимости.
Рекомендации по использованию
- Привяжите стратегию к нужному инструменту и портфелю, выставьте
CandleTypeи параметры риска под размер счёта. - Убедитесь, что брокер/адаптер поддерживает стоп-заявки; иначе замените защитные ордера на ручные выходы в методе
UpdateTrailingStops. - Поскольку обработка идёт только по закрытым свечам, внутрисвечные всплески меньше выбранного таймфрейма не будут обрабатываться так же, как в тиковом тестере 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()