I explain my problem to Antigravity and it send me fixed Position Sizer.mq5 file. everything works fine now!
if there is any bug please let me know. I'm not a programmer.
I'm fan of EARNFOREX!
here is the changelogs:
---
### **Subject: Performance Optimization & Critical Chart Freeze Fix in Version 3.16b**
Hi everyone /
@Enivid,
First of all, thank you for developing and actively maintaining this indispensable tool.
I recently encountered a critical issue when running Position Sizer (v3.16b MT5) on live charts alongside resource-heavy indicators (such as Volume Profile, VWAP, and Market Profile). After a few seconds on a live chart, the chart would freeze:
* New incoming candles were severely delayed or stalled.
* Third-party indicators stopped updating completely.
* Dragging lines caused noticeable UI lag.
* As soon as Position Sizer was removed, the chart and all indicators immediately returned to normal smooth execution.
After profiling the code, we identified the exact bottlenecks causing this event-loop blockage and applied a series of clean, non-breaking performance optimizations. The chart now runs completely smooth with zero lag.
Below is the breakdown of the root causes found and the proposed changelog/patches:
---
### **Root Causes Identified**
1. **Redraw Storm in `DummyObjectSelect()` (Most Critical)**:
In MT5, `DummyObjectSelect()` was designed to work around an MT5 deselection glitch by calling `ObjectDelete()` followed immediately by `ObjectCreate()`. Deleting and recreating an object forces MT5 to trigger an internal chart redraw and recalculation cycle across all attached indicators.
2. **Frequency of `CheckAndRestoreLines()`**:
Inside `OnTimer()`, `CheckAndRestoreLines()` was guarded only by a 50ms check. Because of this, `DummyObjectSelect()` was deleting and recreating an object up to 20 times per second, effectively flooding the terminal's message queue.
3. **Unthrottled `OnTick()` Executions**:
During active market sessions or news events, `OnTick()` was executing `ExtDialog.RefreshValues()` on every single tick (10–20+ times per second). Each pass called full recalculation routines, chart object position updates, and account queries.
4. **Redundant Static Symbol Queries in `GetSymbolAndAccountData()`**:
Over 15 static symbol parameters (`SYMBOL_TRADE_TICK_SIZE`, `SYMBOL_VOLUME_MIN/MAX/STEP`, `SYMBOL_TRADE_CONTRACT_SIZE`, margin modes, currencies) were being re-queried via `SymbolInfoDouble`/`Integer`/`String` on every single tick, none of which change dynamically.
5. **Continuous `CalculateAutoCommission()` Calls**:
When `AutoCommission` was enabled, the broker deal history and tier specifications were being scanned and calculated repeatedly on every tick.
---
### **Proposed Changelog / Fixes**
#### 1. `Position Sizer.mqh` — `DummyObjectSelect()` Lazy-Initialization
Instead of deleting and recreating the dummy object on every call, the object is created only once (hidden from chart/timeframes) and merely toggled for selection:
```mql5
void CPositionSizeCalculator:
😀ummyObjectSelect(string dummy_name = "DummyObject")
{
string full_name = ObjectPrefix + dummy_name;
if (ObjectFind(ChartID(), full_name) < 0)
{
ObjectCreate(0, full_name, OBJ_HLINE, 0, TimeCurrent(), 0);
ObjectSetInteger(ChartID(), full_name, OBJPROP_COLOR, clrNONE);
ObjectSetInteger(ChartID(), full_name, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);
ObjectSetInteger(ChartID(), full_name, OBJPROP_HIDDEN, true);
ObjectSetInteger(ChartID(), full_name, OBJPROP_BACK, true);
}
ObjectSetInteger(ChartID(), full_name, OBJPROP_SELECTED, true);
ObjectSetInteger(ChartID(), full_name, OBJPROP_SELECTED, false);
}
```
#### 2. `Position Sizer.mq5` — Decoupled Timer Throttling in `OnTimer()`
Separated line restoration checks from UI recalculations. Line restoration now runs once every 5 seconds (more than fast enough if a line is accidentally deleted) instead of every 50ms:
```mql5
void OnTimer()
{
ulong now = GetTickCount64();
if (now - LastLineCheckTime >= 5000)
{
ExtDialog.CheckAndRestoreLines();
LastLineCheckTime = now;
}
if (now - LastRecalculationTime < 1000) return;
ExtDialog.RefreshValues();
ChartRedraw();
}
```
#### 3. `Position Sizer.mq5` — 250ms Throttle on `OnTick()`
Added a 250ms guard to `OnTick()` to cap tick-driven recalculations to max 4 times per second. This keeps calculations virtually instantaneous for the user while preventing tick-bursts from starving the terminal's thread:
```mql5
void OnTick()
{
if ((bool)MQLInfoInteger(MQL_VISUAL_MODE))
{
ListenToChartEvents(ExtDialog.Name());
ExtDialog.UpdateStrategyTesterTrades();
}
ulong now = GetTickCount64();
if (now - LastTickRefreshTime < 250) return;
LastTickRefreshTime = now;
ExtDialog.RefreshValues();
if (sets.TrailingStopPoints > 0) DoTrailingStop();
}
```
#### 4. `Position Sizer.mqh` — Static Symbol Specifications Cached (60s)
Cached immutable symbol properties inside `GetSymbolAndAccountData()` for 60 seconds or until the symbol changes, reducing ~15 API roundtrips per tick:
```mql5
ulong LastSymbolStaticDataTime = 0;
string LastSymbolForStaticData = "";
void ResetSymbolDataCache()
{
LastSymbolStaticDataTime = 0;
LastSymbolForStaticData = "";
}
void GetSymbolAndAccountData()
{
ulong now = GetTickCount64();
bool force_refresh = (SymbolForTrading != LastSymbolForStaticData) ||
(now - LastSymbolStaticDataTime > 60000);
if (force_refresh)
{
TickSize = SymbolInfoDouble(SymbolForTrading, SYMBOL_TRADE_TICK_SIZE);
MinLot = SymbolInfoDouble(SymbolForTrading, SYMBOL_VOLUME_MIN);
MaxLot = SymbolInfoDouble(SymbolForTrading, SYMBOL_VOLUME_MAX);
LotStep = SymbolInfoDouble(SymbolForTrading, SYMBOL_VOLUME_STEP);
if (!CalculateUnadjustedPositionSize) LotStep_digits = CountDecimalPlaces(LotStep);
else LotStep_digits = 8;
ContractSize = SymbolInfoDouble(SymbolForTrading, SYMBOL_TRADE_CONTRACT_SIZE);
CalcMode = (ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(SymbolForTrading, SYMBOL_TRADE_CALC_MODE);
MarginHedging = SymbolInfoDouble(SymbolForTrading, SYMBOL_MARGIN_HEDGED);
MarginCurrency = SymbolInfoString(SymbolForTrading, SYMBOL_CURRENCY_MARGIN);
if (MarginCurrency == "RUR") MarginCurrency = "RUB";
ProfitCurrency = SymbolInfoString(SymbolForTrading, SYMBOL_CURRENCY_PROFIT);
if (ProfitCurrency == "RUR") ProfitCurrency = "RUB";
BaseCurrency = SymbolInfoString(SymbolForTrading, SYMBOL_CURRENCY_BASE);
if (BaseCurrency == "RUR") BaseCurrency = "RUB";
InitialMargin = SymbolInfoDouble(SymbolForTrading, SYMBOL_MARGIN_INITIAL);
MaintenanceMargin = SymbolInfoDouble(SymbolForTrading, SYMBOL_MARGIN_MAINTENANCE);
if (MaintenanceMargin == 0) MaintenanceMargin = InitialMargin;
LastSymbolStaticDataTime = now;
LastSymbolForStaticData = SymbolForTrading;
}
AccountMarginMode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
AccountCurrency = AccountInfoString(ACCOUNT_CURRENCY);
AccountCurrencyDigits = (int)AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
if (AccountCurrency == "RUR") AccountCurrency = "RUB";
AccStopoutMode = AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
AccStopoutLevel = AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
TickValue = SymbolInfoDouble(SymbolForTrading, SYMBOL_TRADE_TICK_VALUE);
SwapsTripleDay = WeekdayToString((int)SymbolInfoInteger(SymbolForTrading, SYMBOL_SWAP_ROLLOVER3DAYS));
}
```
#### 5. `Position Sizer.mqh` — AutoCommission Cached (30s) with Trade Invalidation
Auto-commission calculations are cached for 30 seconds and automatically invalidated immediately whenever a trade is placed (`OnTrade()`) or the chart symbol changes:
```mql5
ulong LastAutoCommissionTime = 0;
double CachedAutoCommission = -1;
void ResetAutoCommissionCache()
{
LastAutoCommissionTime = 0;
CachedAutoCommission = -1;
}
double CalculateCommission()
{
if (AutoCommission)
{
ulong now = GetTickCount64();
if (CachedAutoCommission < 0 || (now - LastAutoCommissionTime > 30000))
{
CachedAutoCommission = CalculateAutoCommission();
LastAutoCommissionTime = now;
}
double auto_commission = CachedAutoCommission;
if (auto_commission >= 0)
{
if (auto_commission > 0 && NormalizeDouble(auto_commission, AccountCurrencyDigits) == 0) auto_commission = MathPow(0.1, AccountCurrencyDigits);
sets.CommissionType = COMMISSION_CURRENCY;
if (CommissionProfitOnly) sets.CommissionPerLot = CommissionReward;
else sets.CommissionPerLot = auto_commission;
return auto_commission;
}
}
double commission = sets.CommissionPerLot;
if (sets.CommissionType == COMMISSION_PERCENT)
commission = CalculateContractValue() * sets.CommissionPerLot / 100;
CommissionReward = commission;
CommissionProfitOnly = false;
return commission;
}
```
#### 6. Proper Deinit Cleanup
In `Position Sizer.mq5` under `OnDeinit()`, added explicit cleanup for the dummy objects (`DummyObject` and `DummyObject2`) so no hidden objects are left behind upon removal.
---
### **Result**
After applying these changes, CPU utilization dropped significantly, third-party indicators (Volume Profile, VWAP) remain perfectly responsive, and live candles render without any delay or stuttering.
Hopefully these optimizations can be considered for the next official release!