Invest System 4.5 ist ein MetaTrader 5-Expertenberater, der in die StockSharp High-Level-Strategie-API portiert wurde. Die Strategie handelt das EUR/USD-Paar, indem sie der Richtung der vorherigen abgeschlossenen 4-Stunden-Kerze folgt. Während der ersten Minuten der neuen 4-Stunden-Session ist ein einziger Trade erlaubt und die Positionsgröße passt sich an die realisierte Performance und das Kontowachstum an.
Der Code verlässt sich ausschließlich auf die High-Level-API: Automatische Kerzenabonnements werden verwendet, um sowohl den 4-Stunden-Richtungsbias als auch das Einstiegsfenster des niedrigeren Zeitrahmens zu überwachen, während der integrierte StartProtection-Helper statische Take-Profit- und Stop-Loss-Niveaus in Pips durchsetzt.
Handelslogik
Richtungsbias – beim Schließen jeder fertigen 4-Stunden-Kerze speichert die Strategie, ob die Kerze bullisch oder bärisch schloss. Eine bullische Kerze ermöglicht nur Long-Einstiege für die nächste Session, während eine bärische Kerze nur Shorts ermöglicht. Wenn die Kerze genau auf ihrem Öffnungspreis schließt, wird die vorherige Richtung beibehalten.
Einstiegs-Timing – wenn eine neue 4-Stunden-Kerze beginnt, öffnet sich ein Einstiegsfenster. Das Fenster bleibt für eine konfigurierbare Anzahl von Minuten gültig (standardmäßig 15). Die Strategie beobachtet Kerzen des niedrigeren Zeitrahmens (standardmäßig 1 Minute) und kann höchstens eine Marktorder senden, wenn alle Filter erfüllt sind, während das Fenster aktiv ist.
Einzelne Position – die Strategie pyramidisiert nie. Wenn bereits eine Position offen ist, werden keine neuen Signale bis zur nächsten 4-Stunden-Session verarbeitet. Sobald eine Order gesendet wird, schließt sich das Einstiegsfenster sofort, um das MetaTrader-Verhalten zu replizieren.
Gewinn- und Verlustnachverfolgung – wenn eine Position vollständig geschlossen wird, wird der realisierte PnL erfasst, um die unten beschriebene adaptive Lotlogik anzutreiben.
Positionsgrößen-Regeln
Der ursprüngliche Expertenberater verwendet zwei Schichten des Geldmanagements:
Eigenkapital-Meilensteine: der anfängliche Kontostand wird beim allerersten Update gespeichert. Wenn das Eigenkapital 2×, 3× … 6× den anfänglichen Saldo übersteigt, wird die Basis-Lotgröße proportional erhöht. Stufe 1 beginnt bei BaseLot, Stufe 2 verdoppelt ihn, Stufe 3 verdreifacht ihn, und so weiter. Sekundäre Lotgrößen (Lot2, Lot3, Lot4) werden mit den ursprünglichen Multiplikatoren (×2, ×7 und ×14 jeweils) abgeleitet.
Plan B-Eskalation: zwischen Trades wird ein einzelner globaler Volumenwert gehalten.
Nach einem verlierenden Trade mit dem Basis-Lot wird das Volumen auf das zweite Lot (Lot3) erhöht.
Wenn ein weiterer Verlust beim Handeln des zweiten Lots auftritt, aktiviert sich "Plan B". Plan B ordnet die internen Lot-Optionen neu zu, sodass das Basis-Lot zu Lot2 und das aggressive Lot zu Lot4 wird. Das aktuelle Volumen ändert sich nicht sofort, aber jeder nachfolgende Verlust schiebt die Strategie zum aggressiven Lot. Plan B wird automatisch abgebrochen, wenn das Konto ein neues Eigenkapital-Hoch erreicht.
Ein profitabler Trade setzt das aktuelle Volumen immer auf das Basis-Lot für die aktive Stufe zurück.
Diese Regeln reproduzieren die kaskadierende Lot-Eskalation der MetaTrader-Version genau, ohne manuell durch Orders zu iterieren oder Sammlungen zu verwenden.
Risikomanagement
StartProtection konfiguriert sowohl den Stop-Loss als auch den Take-Profit in absoluten Preiseinheiten, die aus der Pip-Größe abgeleitet werden. Stops und Ziele werden nur einmal registriert, wenn die Strategie gestartet wird, genau wie der ursprüngliche EA die Werte an jede Order anhängt.
Es werden nur Marktorders verwendet. Die Strategie selbst führt keine Hedge-Positionen, Skalierungen oder Teilausstiege durch; Ausstiege erfolgen über die konfigurierten Schutzorders.
Strategie-Parameter
Parameter
Beschreibung
Standard
Optimierungsbereich
StopLossPips
Stop-Loss-Abstand in Pips. 0 zum Deaktivieren des Stops verwenden.
240
120 – 360, Schritt 20
TakeProfitPips
Take-Profit-Abstand in Pips. 0 zum Deaktivieren des Ziels verwenden.
40
20 – 80, Schritt 10
EntryWindowMinutes
Länge des Einstiegsfensters nach jeder neuen 4-Stunden-Kerze.
15
5 – 30, Schritt 5
SignalCandleType
Für die Überwachung des Einstiegsfensters verwendete Kerzenreihe (standardmäßig 1 Minute).
1-Minuten-Zeitrahmen
–
TrendCandleType
Kerze des höheren Zeitrahmens zur Richtungsbestimmung (standardmäßig 4 Stunden).
4-Stunden-Zeitrahmen
–
BaseLot
Anfängliche Lotgröße für Stufe 1. Andere Lotgrößen werden automatisch abgeleitet.
Die Strategie erwartet, dass das angehängte Wertpapier sowohl die 4-Stunden-Kerzenreihe als auch die schnellere Zeitrahmen-Reihe bereitstellt. Diese Abonnements werden automatisch innerhalb von OnStarted erstellt.
Die Pip-Größe wird aus Security.PriceStep bestimmt und für Bruchquotierungen (3 oder 5 Dezimalstellen) angepasst, um der Behandlung von Pip-Werten durch MetaTrader zu entsprechen.
Da der ursprüngliche Roboter Kontostand-Schwellenwerte verwendet, liest die StockSharp-Implementierung Portfolio.CurrentValue bei jeder Einstiegskerzen-Aktualisierung. Bei der Ausführung in der Simulation stellen Sie sicher, dass das Portfoliomodell das aktuelle Eigenkapital aktualisiert, damit die Lot-Skalierung konsistent bleibt.
Die Python-Übersetzung wird wie gewünscht absichtlich weggelassen.
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>
/// Invest System 4.5 strategy converted from MetaTrader.
/// Trades in the direction of the previous 4-hour candle within the first minutes of the new session.
/// </summary>
public class InvestSystem45Strategy : Strategy
{
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<int> _entryWindowMinutes;
private readonly StrategyParam<DataType> _signalCandleType;
private readonly StrategyParam<DataType> _trendCandleType;
private readonly StrategyParam<decimal> _baseLot;
private decimal _pipSize;
private decimal _minBalance;
private decimal _maxBalance;
private int _lotStage;
private bool _planBActive;
private decimal _stageLot1;
private decimal _stageLot2;
private decimal _stageLot3;
private decimal _stageLot4;
private decimal _lotOption1;
private decimal _lotOption2;
private decimal _currentVolume;
private bool _needsPostTradeAdjustment;
private bool _hasOpenPosition;
private decimal _pnlAtEntry;
private decimal _lastTradePnL;
private int _trendDirection;
private DateTime? _entryWindowStart;
private DateTime? _entryWindowEnd;
private bool _entryWindowActive;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Stop loss distance expressed in pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take profit distance expressed in pips.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Minutes allowed for entries after a new trend candle opens.
/// </summary>
public int EntryWindowMinutes
{
get => _entryWindowMinutes.Value;
set => _entryWindowMinutes.Value = value;
}
/// <summary>
/// Candle type that drives entry timing.
/// </summary>
public DataType SignalCandleType
{
get => _signalCandleType.Value;
set => _signalCandleType.Value = value;
}
/// <summary>
/// Higher timeframe candle used to define trade direction.
/// </summary>
public DataType TrendCandleType
{
get => _trendCandleType.Value;
set => _trendCandleType.Value = value;
}
/// <summary>
/// Base lot size used to derive martingale steps.
/// </summary>
public decimal BaseLot
{
get => _baseLot.Value;
set => _baseLot.Value = value;
}
/// <summary>
/// Initialize <see cref="InvestSystem45Strategy"/>.
/// </summary>
public InvestSystem45Strategy()
{
_stopLossPips = Param(nameof(StopLossPips), 240)
.SetNotNegative()
.SetDisplay("Stop Loss (pips)", "Stop loss distance in pips", "Risk")
.SetOptimize(120, 360, 20);
_takeProfitPips = Param(nameof(TakeProfitPips), 40)
.SetNotNegative()
.SetDisplay("Take Profit (pips)", "Take profit distance in pips", "Risk")
.SetOptimize(20, 80, 10);
_entryWindowMinutes = Param(nameof(EntryWindowMinutes), 15)
.SetGreaterThanZero()
.SetDisplay("Entry Window", "Minutes after 4H open when entries are allowed", "Timing")
.SetOptimize(5, 30, 5);
_signalCandleType = Param(nameof(SignalCandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Signal Candles", "Candles used to time entries", "Timing");
_trendCandleType = Param(nameof(TrendCandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Trend Candles", "Higher timeframe candles for direction", "Timing");
_baseLot = Param(nameof(BaseLot), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Base Lot", "Starting lot size before scaling", "Risk")
.SetOptimize(0.05m, 0.3m, 0.05m);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
if (Security is null)
yield break;
yield return (Security, SignalCandleType);
if (!SignalCandleType.Equals(TrendCandleType))
yield return (Security, TrendCandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
_pipSize = CalculatePipSize();
// Recreate lot options according to current stage and plan mode.
RecalculateLotOptions();
var trendSubscription = SubscribeCandles(TrendCandleType);
trendSubscription.Bind(ProcessTrendCandle).Start();
var entrySubscription = SubscribeCandles(SignalCandleType);
entrySubscription.Bind(ProcessEntryCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, entrySubscription);
DrawOwnTrades(area);
}
}
/// <inheritdoc />
protected override void OnPositionReceived(Position position)
{
base.OnPositionReceived(position);
if (Position != 0m)
{
// Record entry state to compute realized PnL later.
if (!_hasOpenPosition)
{
_hasOpenPosition = true;
_needsPostTradeAdjustment = true;
_pnlAtEntry = PnL;
}
_entryWindowActive = false;
return;
}
if (!_hasOpenPosition)
return;
_hasOpenPosition = false;
_lastTradePnL = PnL - _pnlAtEntry;
// Mirror MetaTrader profit calculation for Plan B rules.
HandlePostTradeAdjustment();
}
private void ProcessTrendCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Store direction from the last completed 4H candle.
if (candle.ClosePrice > candle.OpenPrice)
{
_trendDirection = 1;
}
else if (candle.ClosePrice < candle.OpenPrice)
{
_trendDirection = -1;
}
_entryWindowStart = candle.CloseTime;
_entryWindowEnd = _entryWindowStart?.AddMinutes(EntryWindowMinutes);
// Open a new entry window immediately at the next candle open.
_entryWindowActive = true;
}
private void ProcessEntryCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Check SL/TP for open positions.
if (Position > 0m && _entryPrice > 0m)
{
if (_stopPrice > 0m && candle.LowPrice <= _stopPrice)
{
SellMarket(Position);
ResetTargets();
return;
}
if (_takePrice > 0m && candle.HighPrice >= _takePrice)
{
SellMarket(Position);
ResetTargets();
return;
}
}
else if (Position < 0m && _entryPrice > 0m)
{
if (_stopPrice > 0m && candle.HighPrice >= _stopPrice)
{
BuyMarket(Math.Abs(Position));
ResetTargets();
return;
}
if (_takePrice > 0m && candle.LowPrice <= _takePrice)
{
BuyMarket(Math.Abs(Position));
ResetTargets();
return;
}
}
// Update balance-dependent scaling before evaluating signals.
UpdateBalanceState();
if (!_entryWindowActive || !_entryWindowStart.HasValue || !_entryWindowEnd.HasValue)
return;
var openTime = candle.OpenTime;
if (openTime < _entryWindowStart.Value)
return;
if (openTime > _entryWindowEnd.Value)
{
_entryWindowActive = false;
return;
}
if (_trendDirection == 0)
return;
if (Position != 0m)
return;
// Lazy initialize volume when strategy is ready.
if (_currentVolume <= 0m)
_currentVolume = _lotOption1;
if (_currentVolume <= 0m)
return;
if (_trendDirection > 0)
{
BuyMarket(_currentVolume);
}
else
{
SellMarket(_currentVolume);
}
// Allow only one trade per 4H candle similar to MetaTrader logic.
_entryWindowActive = false;
}
private void HandlePostTradeAdjustment()
{
if (!_needsPostTradeAdjustment)
return;
_needsPostTradeAdjustment = false;
// Apply lot escalation rules after each closed trade.
UpdateBalanceState();
if (_lastTradePnL < 0m)
{
if (_currentVolume == _lotOption2 && !_planBActive)
{
_planBActive = true;
RecalculateLotOptions();
}
else if (_currentVolume == _lotOption1)
{
_currentVolume = _lotOption2;
}
else
{
_currentVolume = _lotOption2;
}
}
else if (_lastTradePnL > 0m)
{
_currentVolume = _lotOption1;
}
}
private void UpdateBalanceState()
{
var balance = Portfolio?.CurrentValue;
if (balance is null || balance.Value <= 0m)
return;
if (_minBalance <= 0m)
{
_minBalance = balance.Value;
_maxBalance = balance.Value;
}
if (balance.Value > _maxBalance)
{
_maxBalance = balance.Value;
if (_planBActive)
{
_planBActive = false;
RecalculateLotOptions();
}
}
var newStage = 1;
if (_minBalance > 0m)
{
// Check for equity milestones to scale base lots.
for (var stage = 6; stage >= 2; stage--)
{
if (balance.Value > _minBalance * stage)
{
newStage = stage;
break;
}
}
}
if (newStage != _lotStage)
{
_lotStage = newStage;
RecalculateLotOptions();
}
}
private decimal CalculatePipSize()
{
var step = Security?.PriceStep ?? 1m;
var decimals = Security?.Decimals ?? 0;
if (decimals == 3 || decimals == 5)
step *= 10m;
return step;
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
if (trade?.Trade == null) return;
if (Position != 0m && _entryPrice == 0m)
{
_entryPrice = trade.Trade.Price;
var slDist = StopLossPips * _pipSize;
var tpDist = TakeProfitPips * _pipSize;
if (Position > 0m)
{
_stopPrice = slDist > 0m ? _entryPrice - slDist : 0m;
_takePrice = tpDist > 0m ? _entryPrice + tpDist : 0m;
}
else
{
_stopPrice = slDist > 0m ? _entryPrice + slDist : 0m;
_takePrice = tpDist > 0m ? _entryPrice - tpDist : 0m;
}
}
if (Position == 0m)
ResetTargets();
}
private void ResetTargets()
{
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
}
private void ResetState()
{
_pipSize = 0m;
_minBalance = 0m;
_maxBalance = 0m;
_lotStage = 1;
_planBActive = false;
_stageLot1 = 0m;
_stageLot2 = 0m;
_stageLot3 = 0m;
_stageLot4 = 0m;
_lotOption1 = 0m;
_lotOption2 = 0m;
_currentVolume = 0m;
_needsPostTradeAdjustment = false;
_hasOpenPosition = false;
_pnlAtEntry = 0m;
_lastTradePnL = 0m;
_trendDirection = 0;
_entryWindowStart = null;
_entryWindowEnd = null;
_entryWindowActive = false;
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
}
private void RecalculateLotOptions()
{
var baseLot = BaseLot * _lotStage;
_stageLot1 = baseLot;
_stageLot2 = baseLot * 2m;
_stageLot3 = baseLot * 7m;
_stageLot4 = baseLot * 14m;
// Stage-specific lot multipliers replicate the original configuration.
if (_planBActive)
{
_lotOption1 = _stageLot2;
_lotOption2 = _stageLot4;
}
else
{
_lotOption1 = _stageLot1;
_lotOption2 = _stageLot3;
}
if (_currentVolume <= 0m)
_currentVolume = _lotOption1;
}
}
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 invest_system45_strategy(Strategy):
def __init__(self):
super(invest_system45_strategy, self).__init__()
self._stop_loss_pips = self.Param("StopLossPips", 240)
self._take_profit_pips = self.Param("TakeProfitPips", 40)
self._entry_window_minutes = self.Param("EntryWindowMinutes", 15)
self._signal_candle_type = self.Param("SignalCandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._trend_candle_type = self.Param("TrendCandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._base_lot = self.Param("BaseLot", 0.1)
self._pip_size = 0.0
self._min_balance = 0.0
self._max_balance = 0.0
self._lot_stage = 1
self._plan_b_active = False
self._stage_lot1 = 0.0
self._stage_lot2 = 0.0
self._stage_lot3 = 0.0
self._stage_lot4 = 0.0
self._lot_option1 = 0.0
self._lot_option2 = 0.0
self._current_volume = 0.0
self._needs_post_trade_adjustment = False
self._has_open_position = False
self._pnl_at_entry = 0.0
self._last_trade_pnl = 0.0
self._trend_direction = 0
self._entry_window_start = None
self._entry_window_end = None
self._entry_window_active = False
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
@property
def StopLossPips(self):
return self._stop_loss_pips.Value
@property
def TakeProfitPips(self):
return self._take_profit_pips.Value
@property
def EntryWindowMinutes(self):
return self._entry_window_minutes.Value
@property
def SignalCandleType(self):
return self._signal_candle_type.Value
@property
def TrendCandleType(self):
return self._trend_candle_type.Value
@property
def BaseLot(self):
return self._base_lot.Value
def OnStarted2(self, time):
super(invest_system45_strategy, self).OnStarted2(time)
self._reset_state()
self._pip_size = self._calculate_pip_size()
self._recalculate_lot_options()
trend_sub = self.SubscribeCandles(self.TrendCandleType)
trend_sub.Bind(self._process_trend_candle).Start()
entry_sub = self.SubscribeCandles(self.SignalCandleType)
entry_sub.Bind(self._process_entry_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, entry_sub)
self.DrawOwnTrades(area)
def _process_trend_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if float(candle.ClosePrice) > float(candle.OpenPrice):
self._trend_direction = 1
elif float(candle.ClosePrice) < float(candle.OpenPrice):
self._trend_direction = -1
self._entry_window_start = candle.CloseTime
self._entry_window_end = candle.CloseTime.AddMinutes(self.EntryWindowMinutes)
self._entry_window_active = True
def _process_entry_candle(self, candle):
if candle.State != CandleStates.Finished:
return
pos = float(self.Position)
# SL/TP management
if pos > 0 and self._entry_price > 0:
if self._stop_price > 0 and float(candle.LowPrice) <= self._stop_price:
self.SellMarket(pos)
self._on_position_closed()
return
if self._take_price > 0 and float(candle.HighPrice) >= self._take_price:
self.SellMarket(pos)
self._on_position_closed()
return
elif pos < 0 and self._entry_price > 0:
if self._stop_price > 0 and float(candle.HighPrice) >= self._stop_price:
self.BuyMarket(abs(pos))
self._on_position_closed()
return
if self._take_price > 0 and float(candle.LowPrice) <= self._take_price:
self.BuyMarket(abs(pos))
self._on_position_closed()
return
self._update_balance_state()
if not self._entry_window_active or self._entry_window_start is None or self._entry_window_end is None:
return
open_time = candle.OpenTime
if open_time < self._entry_window_start:
return
if open_time > self._entry_window_end:
self._entry_window_active = False
return
if self._trend_direction == 0:
return
if float(self.Position) != 0:
return
if self._current_volume <= 0:
self._current_volume = self._lot_option1
if self._current_volume <= 0:
return
close = float(candle.ClosePrice)
if self._trend_direction > 0:
self.BuyMarket(self._current_volume)
self._entry_price = close
sl_dist = self.StopLossPips * self._pip_size
tp_dist = self.TakeProfitPips * self._pip_size
self._stop_price = close - sl_dist if sl_dist > 0 else 0.0
self._take_price = close + tp_dist if tp_dist > 0 else 0.0
else:
self.SellMarket(self._current_volume)
self._entry_price = close
sl_dist = self.StopLossPips * self._pip_size
tp_dist = self.TakeProfitPips * self._pip_size
self._stop_price = close + sl_dist if sl_dist > 0 else 0.0
self._take_price = close - tp_dist if tp_dist > 0 else 0.0
self._has_open_position = True
self._needs_post_trade_adjustment = True
self._pnl_at_entry = float(self.PnL)
self._entry_window_active = False
def _update_balance_state(self):
portfolio = self.Portfolio
if portfolio is None:
return
balance = portfolio.CurrentValue
if balance is None or float(balance) <= 0:
return
bal = float(balance)
if self._min_balance <= 0:
self._min_balance = bal
self._max_balance = bal
if bal > self._max_balance:
self._max_balance = bal
if self._plan_b_active:
self._plan_b_active = False
self._recalculate_lot_options()
new_stage = 1
if self._min_balance > 0:
for stage in range(6, 1, -1):
if bal > self._min_balance * stage:
new_stage = stage
break
if new_stage != self._lot_stage:
self._lot_stage = new_stage
self._recalculate_lot_options()
def _calculate_pip_size(self):
sec = self.Security
if sec is None:
return 1.0
step = float(sec.PriceStep) if sec.PriceStep is not None else 1.0
decimals = int(sec.Decimals) if sec.Decimals is not None else 0
if decimals == 3 or decimals == 5:
step *= 10.0
return step
def _recalculate_lot_options(self):
base_lot = float(self.BaseLot) * self._lot_stage
self._stage_lot1 = base_lot
self._stage_lot2 = base_lot * 2.0
self._stage_lot3 = base_lot * 7.0
self._stage_lot4 = base_lot * 14.0
if self._plan_b_active:
self._lot_option1 = self._stage_lot2
self._lot_option2 = self._stage_lot4
else:
self._lot_option1 = self._stage_lot1
self._lot_option2 = self._stage_lot3
if self._current_volume <= 0:
self._current_volume = self._lot_option1
def _handle_post_trade_adjustment(self):
if not self._needs_post_trade_adjustment:
return
self._needs_post_trade_adjustment = False
self._update_balance_state()
if self._last_trade_pnl < 0:
if self._current_volume == self._lot_option2 and not self._plan_b_active:
self._plan_b_active = True
self._recalculate_lot_options()
else:
self._current_volume = self._lot_option2
elif self._last_trade_pnl > 0:
self._current_volume = self._lot_option1
def OnOwnTradeReceived(self, trade):
super(invest_system45_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:
self._entry_price = float(trade.Trade.Price)
sl_dist = self.StopLossPips * self._pip_size
tp_dist = self.TakeProfitPips * self._pip_size
if pos > 0:
self._stop_price = self._entry_price - sl_dist if sl_dist > 0 else 0.0
self._take_price = self._entry_price + tp_dist if tp_dist > 0 else 0.0
else:
self._stop_price = self._entry_price + sl_dist if sl_dist > 0 else 0.0
self._take_price = self._entry_price - tp_dist if tp_dist > 0 else 0.0
if pos == 0:
self._reset_targets()
def OnPositionReceived(self, position):
super(invest_system45_strategy, self).OnPositionReceived(position)
pos = float(self.Position)
if pos != 0:
if not self._has_open_position:
self._has_open_position = True
self._needs_post_trade_adjustment = True
self._pnl_at_entry = float(self.PnL)
self._entry_window_active = False
return
if not self._has_open_position:
return
self._has_open_position = False
self._last_trade_pnl = float(self.PnL) - self._pnl_at_entry
self._handle_post_trade_adjustment()
def _on_position_closed(self):
self._reset_targets()
if self._has_open_position:
self._has_open_position = False
self._last_trade_pnl = float(self.PnL) - self._pnl_at_entry
self._handle_post_trade_adjustment()
def _reset_targets(self):
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
def _reset_state(self):
self._pip_size = 0.0
self._min_balance = 0.0
self._max_balance = 0.0
self._lot_stage = 1
self._plan_b_active = False
self._stage_lot1 = 0.0
self._stage_lot2 = 0.0
self._stage_lot3 = 0.0
self._stage_lot4 = 0.0
self._lot_option1 = 0.0
self._lot_option2 = 0.0
self._current_volume = 0.0
self._needs_post_trade_adjustment = False
self._has_open_position = False
self._pnl_at_entry = 0.0
self._last_trade_pnl = 0.0
self._trend_direction = 0
self._entry_window_start = None
self._entry_window_end = None
self._entry_window_active = False
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
def OnReseted(self):
super(invest_system45_strategy, self).OnReseted()
self._reset_state()
def CreateClone(self):
return invest_system45_strategy()