Invest System 4.5 戦略 (C#)
概要
Invest System 4.5 は、StockSharp 高レベル戦略 API に移植された MetaTrader 5 のエキスパートアドバイザーです。戦略は以前に完成した 4 時間足の方向に従って EUR/USD ペアを取引します。新しい 4 時間セッションの最初の数分間に 1 回のトレードが許可され、ポジションサイジングは実現したパフォーマンスと口座の成長に適応します。
コードは高レベル API のみに依存します:自動ローソク足サブスクリプションは 4 時間方向バイアスと低い時間軸のエントリーウィンドウの両方を監視するために使用され、組み込みの StartProtection ヘルパーは pips で表された静的テイクプロフィットとストップロスレベルを適用します。
取引ロジック
- 方向バイアス – 完成した 4 時間足のクローズごとに、戦略はローソク足が強気または弱気でクローズしたかを記録します。強気のローソク足は次のセッションのロングエントリーのみを有効にし、弱気のローソク足はショートのみを有効にします。ローソク足が開始価格と全く同じ価格でクローズした場合、前の方向が維持されます。
- エントリータイミング – 新しい 4 時間足が始まると、エントリーウィンドウが開きます。ウィンドウは設定可能な分数(デフォルト 15 分)有効です。戦略は低い時間軸のローソク足(デフォルト 1 分)を監視し、ウィンドウがアクティブな間にすべてのフィルターが満たされた場合に最大 1 件の成行注文を送信できます。
- 単一ポジション – 戦略は決してピラミッドを組みません。ポジションがすでに開いている場合、次の 4 時間セッションまで新しいシグナルは処理されません。注文が送信されると、MetaTrader の動作を再現するためにエントリーウィンドウは即座に閉じます。
- 損益追跡 – ポジションが完全にクローズされると、以下に説明する適応的なロットロジックを駆動するために実現 PnL が取得されます。
ポジションサイジングルール
元のエキスパートアドバイザーは二つの資金管理レイヤーを使用します:
- 資本マイルストーン:初期口座残高は最初の更新で記録されます。資本が初期残高の 2×、3× … 6× を超えると、ベースロットサイズが比例して増加します。ステージ 1 は
BaseLotから始まり、ステージ 2 はそれを 2 倍にし、ステージ 3 はそれを 3 倍にするなど。二次ロットサイズ(Lot2、Lot3、Lot4)は元の乗数(それぞれ ×2、×7、×14)を使用して導出されます。 - プラン B エスカレーション:単一のグローバルボリューム値がトレード間で保持されます。
- ベースロットでの負けトレードの後、ボリュームは第 2 のロット(
Lot3)に引き上げられます。 - 第 2 のロットで取引中にさらに損失が発生した場合、「プラン B」が有効になります。プラン B はベースロットが
Lot2になりアグレッシブロットがLot4になるように内部ロットオプションを再マッピングします。現在のボリュームはすぐには変更されませんが、その後の損失はすべて戦略をアグレッシブロットに押し込みます。口座が新しい資本の高値に達すると、プラン B は自動的にキャンセルされます。 - 利益のあるトレードは常にアクティブなステージのベースロットに現在のボリュームをリセットします。 これらのルールは、注文を手動で反復したりコレクションを使用したりすることなく、MetaTrader バージョンのカスケードロットエスカレーションを忠実に再現します。
- ベースロットでの負けトレードの後、ボリュームは第 2 のロット(
リスク管理
StartProtectionは pip サイズから導出された絶対価格単位でストップロスとテイクプロフィットの両方を設定します。ストップとターゲットは戦略が開始されたときに一度だけ登録され、元の EA が各注文に値を添付するのと同様です。- 成行注文のみが使用されます。戦略自体はヘッジポジション、スケーリング、または部分的な出口を実行しません;出口は設定された保護注文を通じて発生します。
戦略パラメーター
| パラメーター | 説明 | デフォルト | 最適化範囲 |
|---|---|---|---|
StopLossPips |
pips 単位のストップロス距離。ストップを無効にするには 0 を使用。 |
240 | 120 – 360、ステップ 20 |
TakeProfitPips |
pips 単位のテイクプロフィット距離。ターゲットを無効にするには 0 を使用。 |
40 | 20 – 80、ステップ 10 |
EntryWindowMinutes |
新しい 4 時間足が開いた後のエントリーウィンドウの長さ。 | 15 | 5 – 30、ステップ 5 |
SignalCandleType |
エントリーウィンドウを監視するために使用されるローソク足シリーズ(デフォルト 1 分)。 | 1 分足の時間軸 | – |
TrendCandleType |
方向バイアスを構築するために使用される高い時間軸のローソク足(デフォルト 4 時間)。 | 4 時間足の時間軸 | – |
BaseLot |
ステージ 1 の初期ロットサイズ。他のロットサイズは自動的に導出されます。 | 0.1 | 0.05 – 0.3、ステップ 0.05 |
ファイル構造
2772_Invest_System_45/
├── CS/
│ └── InvestSystem45Strategy.cs
├── README.md
├── README_ru.md
└── README_zh.md
注意事項
- 戦略は添付された証券が 4 時間足シリーズとより速い時間軸シリーズの両方を提供することを期待します。これらのサブスクリプションは
OnStarted内で自動的に作成されます。 - pip サイズは
Security.PriceStepから決定され、MetaTrader の pip 値の処理に合わせるために小数点以下 3 桁または 5 桁の引用(分数引用)に調整されます。 - 元のロボットは口座残高の閾値を使用するため、StockSharp の実装は各エントリーローソク足の更新で
Portfolio.CurrentValueを読み取ります。シミュレーションで実行する場合、ロットスケーリングが一貫性を保つようにポートフォリオモデルが現在の資本を更新することを確認してください。 - Python 翻訳は要求通り意図的に省略されています。
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()