From 08f72b67ac0db98c78da4f9dd137a3e1cfe6f0c0 Mon Sep 17 00:00:00 2001 From: Ethansi2947E <126624443+Ethansi2947E@users.noreply.github.com> Date: Sat, 24 Oct 2026 12:16:03 +0100 Subject: [PATCH] fix: evaluate bar extremes and mark to close in open prices mode, calibrate broker spreads --- core/include/interactive_tester.hpp | 4 ++ core/include/order_book.hpp | 62 +++++++++++++++++++++++++++++ python/edge_engine/symbol_spec.py | 12 +++--- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/core/include/interactive_tester.hpp b/core/include/interactive_tester.hpp index 5a250f8..b0d3ab0 100644 --- a/core/include/interactive_tester.hpp +++ b/core/include/interactive_tester.hpp @@ -235,7 +235,9 @@ public: book_->on_price_update(tick, current_bar_idx_); strategy_->OnTick(); strategy_->OnBar(); + book_->evaluate_bar_extremes(bars_[current_bar_idx_], sp, current_bar_idx_); if (cfg_.enable_swaps) _apply_swaps(); + book_->mark_to_bar_close(bars_[current_bar_idx_], sp, current_bar_idx_); _record_bar_eob(sp); current_bar_idx_++; if (current_bar_idx_ >= n_bars_) is_done_ = true; @@ -284,7 +286,9 @@ public: book_->on_price_update(tick, current_bar_idx_); strategy_->OnTick(); strategy_->OnBar(); + book_->evaluate_bar_extremes(bars_[current_bar_idx_], sp, current_bar_idx_); if (cfg_.enable_swaps) _apply_swaps(); + book_->mark_to_bar_close(bars_[current_bar_idx_], sp, current_bar_idx_); _record_bar_eob(sp); current_bar_idx_++; if (current_bar_idx_ >= n_bars_) is_done_ = true; diff --git a/core/include/order_book.hpp b/core/include/order_book.hpp index e9a85b1..1595d5c 100644 --- a/core/include/order_book.hpp +++ b/core/include/order_book.hpp @@ -191,6 +191,68 @@ public: account_.update_state(active_positions_, symbol_); } + // Evaluate intrabar extremes (High / Low) for active positions in OPEN_PRICES mode + void evaluate_bar_extremes(const MqlRates& bar, double spread, int32_t bar_index) { + double ask_high = bar.high + spread; + double ask_low = bar.low + spread; + + for (auto it = active_positions_.begin(); it != active_positions_.end(); ) { + bool closed = false; + if (it->is_buy()) { + // Check SL first (conservative stop-first model) + if (it->sl > EPSILON_PRICE && bar.low <= it->sl) { + double exit_p = (bar.open <= it->sl) ? bar.open : it->sl; + _close_position_internal(it, exit_p, it->volume, DEAL_REASON_SL); + closed = true; + } else if (it->tp > EPSILON_PRICE && bar.high >= it->tp) { + double exit_p = (bar.open >= it->tp) ? bar.open : it->tp; + _close_position_internal(it, exit_p, it->volume, DEAL_REASON_TP); + closed = true; + } + } else { + // Sell position exit executed on Ask + if (it->sl > EPSILON_PRICE && ask_high >= it->sl) { + double ask_open = bar.open + spread; + double exit_p = (ask_open >= it->sl) ? ask_open : it->sl; + _close_position_internal(it, exit_p, it->volume, DEAL_REASON_SL); + closed = true; + } else if (it->tp > EPSILON_PRICE && ask_low <= it->tp) { + double ask_open = bar.open + spread; + double exit_p = (ask_open <= it->tp) ? ask_open : it->tp; + _close_position_internal(it, exit_p, it->volume, DEAL_REASON_TP); + closed = true; + } + } + + if (!closed) { + // Update MFE / MAE with bar extremes + if (it->is_buy()) { + it->highest_price = std::max(it->highest_price, bar.high); + it->lowest_price = std::min(it->lowest_price, bar.low); + it->mfe = std::max(it->mfe, symbol_.price_pnl(it->highest_price - it->price_open, it->volume)); + it->mae = std::min(it->mae, symbol_.price_pnl(it->lowest_price - it->price_open, it->volume)); + } else { + it->lowest_price = std::min(it->lowest_price, ask_low); + it->highest_price = std::max(it->highest_price, ask_high); + it->mfe = std::max(it->mfe, symbol_.price_pnl(it->price_open - it->lowest_price, it->volume)); + it->mae = std::min(it->mae, symbol_.price_pnl(it->price_open - it->highest_price, it->volume)); + } + ++it; + } + } + account_.update_state(active_positions_, symbol_); + } + + // Mark floating positions to bar close for accurate end-of-bar equity + void mark_to_bar_close(const MqlRates& bar, double spread, int32_t bar_index) { + current_tick_.bid = bar.close; + current_tick_.ask = bar.close + spread; + current_tick_.time = bar.time; + current_bar_index_ = bar_index; + _update_positions_pnl(bar_index); + account_.update_state(active_positions_, symbol_); + } + private: // ---------------------------------------------------- // Internal: Execute Instant Market Order diff --git a/python/edge_engine/symbol_spec.py b/python/edge_engine/symbol_spec.py index ad9c506..f170022 100644 --- a/python/edge_engine/symbol_spec.py +++ b/python/edge_engine/symbol_spec.py @@ -6,15 +6,17 @@ from __future__ import annotations from typing import Dict, Any, Optional FALLBACK_SPECS: Dict[str, Dict[str, Any]] = { - "Jump 75 Index": {"point": 0.01, "digits": 2, "spread_val": 2.99, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.01, "volume_max": 4.0, "volume_step": 0.01, "commission_per_lot": 0.0}, - "Jump 10 Index": {"point": 0.01, "digits": 2, "spread_val": 3.50, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.01, "volume_max": 10.0, "volume_step": 0.01, "commission_per_lot": 0.0}, + "Jump 75 Index": {"point": 0.01, "digits": 2, "spread_val": 2.47, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.01, "volume_max": 4.0, "volume_step": 0.01, "commission_per_lot": 0.0}, + "Jump 10 Index": {"point": 0.01, "digits": 2, "spread_val": 2.27, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.01, "volume_max": 10.0, "volume_step": 0.01, "commission_per_lot": 0.0}, "Jump 25 Index": {"point": 0.01, "digits": 2, "spread_val": 3.20, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.01, "volume_max": 8.0, "volume_step": 0.01, "commission_per_lot": 0.0}, "Jump 50 Index": {"point": 0.01, "digits": 2, "spread_val": 3.00, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.01, "volume_max": 6.0, "volume_step": 0.01, "commission_per_lot": 0.0}, - "Jump 100 Index": {"point": 0.01, "digits": 2, "spread_val": 2.80, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.01, "volume_max": 2.0, "volume_step": 0.01, "commission_per_lot": 0.0}, + "Jump 100 Index": {"point": 0.01, "digits": 2, "spread_val": 0.10, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.01, "volume_max": 2.0, "volume_step": 0.01, "commission_per_lot": 0.0}, "Volatility 75 Index": {"point": 0.01, "digits": 2, "spread_val": 5.00, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.001,"volume_max": 10.0, "volume_step": 0.001, "commission_per_lot": 0.0}, "Volatility 75 (1s) Index":{"point": 0.01,"digits": 2, "spread_val": 4.50, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.001,"volume_max": 10.0, "volume_step": 0.001, "commission_per_lot": 0.0}, - "Volatility 100 Index": {"point": 0.01, "digits": 2, "spread_val": 3.00, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.2, "volume_max": 50.0, "volume_step": 0.01, "commission_per_lot": 0.0}, - "Volatility 10 Index": {"point": 0.001,"digits": 3, "spread_val": 0.20, "trade_tick_size": 0.001,"trade_tick_value": 0.001,"volume_min": 0.1, "volume_max": 100.0, "volume_step": 0.01, "commission_per_lot": 0.0}, + "Volatility 100 Index": {"point": 0.01, "digits": 2, "spread_val": 0.25, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.2, "volume_max": 50.0, "volume_step": 0.01, "commission_per_lot": 0.0}, + "Volatility 50 Index": {"point": 0.01, "digits": 2, "spread_val": 1.70, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.01, "volume_max": 10.0, "volume_step": 0.01, "commission_per_lot": 0.0}, + "Volatility 25 Index": {"point": 0.01, "digits": 2, "spread_val": 1.98, "trade_tick_size": 0.01, "trade_tick_value": 0.01, "volume_min": 0.01, "volume_max": 10.0, "volume_step": 0.01, "commission_per_lot": 0.0}, + "Volatility 10 Index": {"point": 0.001,"digits": 3, "spread_val": 0.158, "trade_tick_size": 0.001,"trade_tick_value": 0.001,"volume_min": 0.1, "volume_max": 100.0, "volume_step": 0.01, "commission_per_lot": 0.0}, "Step Index": {"point": 0.1, "digits": 1, "spread_val": 1.00, "trade_tick_size": 0.1, "trade_tick_value": 0.1, "volume_min": 0.1, "volume_max": 50.0, "volume_step": 0.01, "commission_per_lot": 0.0}, }