StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00 | | | //+------------------------------------------------------------------+
|
| | | //| Symbol.mqh|
|
| | | //| Copyright 2026, bandersoft |
|
| | | //| https://www.mql5.com/en/users/fats |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
2026-10-16 21:48:01 +03:00 | | | //| Pip size, price/pip conversion, normalisation, broker checks. |
|
StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00 | | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | #ifndef STOPFIRST_SYMBOL_MQH
|
| | | #define STOPFIRST_SYMBOL_MQH
|
| | |
|
| | | #include <Trade\SymbolInfo.mqh>
|
| | | #include "Defs.mqh"
|
| | |
|
| | | //--- per-symbol pip size overrides, set through SetPipSize()
|
| | | string g_pipSymbols[];
|
| | | double g_pipValues[];
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Find a symbol's override slot, or -1 |
|
| | | //| |
|
| | | //| Returns: the index into the override arrays, or -1 if the symbol |
|
| | | //| has no override. |
|
| | | //+------------------------------------------------------------------+
|
| | | int PipOverrideIndex(const string symbol)
|
| | | {
|
| | | int total = ArraySize(g_pipSymbols);
|
| | | for(int i = 0; i < total; i++)
|
| | | if(g_pipSymbols[i] == symbol)
|
| | | return i;
|
| | |
|
| | | return -1;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Declare what one pip is worth for a symbol |
|
| | | //| |
|
| | | //| The built-in rule below is the forex convention and is wrong for |
|
| | | //| instruments where a pip means something else - by 10x on gold |
|
| | | //| quoted to 2 digits, by 100x on crypto. No symbol property |
|
| | | //| distinguishes those cases, so the caller has to say: |
|
| | | //| |
|
| | | //| SetPipSize("XAUUSD", 0.1); |
|
| | | //| |
|
| | | //| Pass pipSize <= 0 to drop the override again. |
|
| | | //| Returns: nothing. |
|
| | | //+------------------------------------------------------------------+
|
| | | void SetPipSize(string symbol, double pipSize)
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | int index = PipOverrideIndex(symbol);
|
| | |
|
| | | if(pipSize <= 0)
|
| | | {
|
| | | if(index < 0) return;
|
| | |
|
| | | //--- close the gap with the last entry, then shrink
|
| | | int last = ArraySize(g_pipSymbols) - 1;
|
| | | g_pipSymbols[index] = g_pipSymbols[last];
|
| | | g_pipValues[index] = g_pipValues[last];
|
| | | ArrayResize(g_pipSymbols, last);
|
| | | ArrayResize(g_pipValues, last);
|
| | | return;
|
| | | }
|
| | |
|
| | | if(index >= 0)
|
| | | {
|
| | | g_pipValues[index] = pipSize;
|
| | | return;
|
| | | }
|
| | |
|
| | | int size = ArraySize(g_pipSymbols);
|
| | | ArrayResize(g_pipSymbols, size + 1);
|
| | | ArrayResize(g_pipValues, size + 1);
|
| | | g_pipSymbols[size] = symbol;
|
| | | g_pipValues[size] = pipSize;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Forget every pip size override |
|
| | | //| Returns: nothing. |
|
| | | //+------------------------------------------------------------------+
|
| | | void ClearPipSizes(void)
|
| | | {
|
| | | ArrayResize(g_pipSymbols, 0);
|
| | | ArrayResize(g_pipValues, 0);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Get the size of one pip, in price units |
|
| | | //| |
|
| | | //| This is the single definition of a pip for the whole library. |
|
| | | //| Everything that needs one calls here - no local copies. |
|
| | | //| |
|
| | | //| Returns: the override if one was set, otherwise point * 10 on a |
|
| | | //| 3- or 5-digit symbol and point on any other. |
|
| | | //| 0 if the symbol is unknown. |
|
| | | //+------------------------------------------------------------------+
|
| | | double GetPipSize(string symbol = "")
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | int index = PipOverrideIndex(symbol);
|
| | | if(index >= 0) return g_pipValues[index];
|
| | |
|
| | | CSymbolInfo symbolInfo;
|
| | | if(!symbolInfo.Name(symbol)) return 0;
|
| | |
|
| | | double point = symbolInfo.Point();
|
| | | int digits = symbolInfo.Digits();
|
| | |
|
| | | return (digits == 3 || digits == 5) ? point * 10 : point;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Convert price difference to pips |
|
| | | //| |
|
2026-10-16 21:48:01 +03:00 | | | //| Returns: the difference in pips, not rounded - rounding to one |
|
| | | //| decimal would report a 0.04 pip spread as 0.0. 0 if the |
|
| | | //| symbol is unknown. |
|
StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00 | | | //+------------------------------------------------------------------+
|
| | | double PriceToPips(double priceDiff, string symbol = "")
|
| | | {
|
| | | double pipSize = GetPipSize(symbol);
|
| | | if(pipSize == 0) return 0;
|
| | |
|
2026-10-16 21:48:01 +03:00 | | | //--- a unit conversion does not round: one decimal would report a
|
| | | //--- 0.04 pip spread as 0.0
|
StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00 | | | return priceDiff / pipSize;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Convert pips to price difference |
|
| | | //| |
|
| | | //| Returns: the distance in price units. 0 if the symbol is unknown. |
|
| | | //+------------------------------------------------------------------+
|
| | | double PipsToPrice(double pips, string symbol = "")
|
| | | {
|
| | | double pipSize = GetPipSize(symbol);
|
| | | return pips * pipSize;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Get spread in pips |
|
| | | //| |
|
| | | //| Returns: Ask - Bid in pips. 0 if the symbol is unknown, which is |
|
2026-10-16 21:48:01 +03:00 | | | //| also a plausible real spread. |
|
StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00 | | | //+------------------------------------------------------------------+
|
| | | double GetSpreadPips(string symbol = "")
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | CSymbolInfo symbolInfo;
|
| | | if(!symbolInfo.Name(symbol)) return 0;
|
| | |
|
2026-10-16 21:48:01 +03:00 | | | //--- without fresh rates Ask and Bid can be stale or zero
|
StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00 | | | if(!symbolInfo.RefreshRates()) return 0;
|
| | |
|
| | | double spread = symbolInfo.Ask() - symbolInfo.Bid();
|
| | | return PriceToPips(spread, symbol);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Check if market is open for trading |
|
| | | //| |
|
| | | //| Returns: true if the server clock falls inside ANY of the symbol's|
|
| | | //| trading sessions for today. false if the symbol is |
|
| | | //| unknown, trading on it is disabled, or no session covers |
|
| | | //| the current time. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool IsMarketOpen(string symbol = "")
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | CSymbolInfo symbolInfo;
|
| | | if(!symbolInfo.Name(symbol)) return false;
|
| | |
|
| | | //--- a symbol can still be quoted while trading on it is switched off
|
| | | if(symbolInfo.TradeMode() == SYMBOL_TRADE_MODE_DISABLED) return false;
|
| | |
|
| | | MqlDateTime dt;
|
| | | //--- TimeTradeServer, not TimeCurrent: TimeCurrent is the time of the
|
2026-10-16 21:48:01 +03:00 | | | //--- last tick, which stops at Friday 23:59 all weekend and would read
|
| | | //--- as inside Friday's session on Saturday. TimeTradeServer keeps
|
| | | //--- running.
|
StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00 | | | TimeToStruct(TimeTradeServer(), dt);
|
| | |
|
| | | //--- SymbolInfoSessionTrade reports from/to as seconds since
|
| | | //--- midnight, so compare against the clock time of day
|
| | | int secondsOfDay = dt.hour * 3600 + dt.min * 60 + dt.sec;
|
| | |
|
| | | datetime from = 0, to = 0;
|
| | |
|
2026-10-16 21:48:01 +03:00 | | | //--- reading session 0 only would show every symbol with a midday
|
| | | //--- break - indices, metals, most CFDs - as closed during its second
|
| | | //--- session. Walk the sessions until the broker runs out.
|
StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00 | | | for(uint session = 0; session < 16; session++)
|
| | | {
|
| | | if(!SymbolInfoSessionTrade(symbol, (ENUM_DAY_OF_WEEK)dt.day_of_week,
|
| | | session, from, to))
|
| | | break;
|
| | |
|
| | | //--- the end is exclusive: a session to 23:00 is closed AT 23:00.
|
| | | //--- "<=" said open at 23:00:00 sharp - the moment a new H1 bar
|
| | | //--- opens - and US30 rejected a trade as "market closed" there
|
| | | //--- (tester, 2026-10-14)
|
| | | if(secondsOfDay >= (int)from && secondsOfDay < (int)to) return true;
|
| | | }
|
| | |
|
| | | return false;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Format price with correct digits |
|
| | | //| |
|
| | | //| Returns: the price as text at the symbol's digit count, or at 5 |
|
| | | //| decimals if the symbol is unknown. |
|
| | | //+------------------------------------------------------------------+
|
| | | string FormatPrice(double price, string symbol = "")
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | CSymbolInfo symbolInfo;
|
| | | if(!symbolInfo.Name(symbol)) return DoubleToString(price, 5);
|
| | |
|
| | | return DoubleToString(price, symbolInfo.Digits());
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Decimal places implied by a volume step (0.01 -> 2, 0.001 -> 3) |
|
| | | //| Returns: 0..8, defaulting to 2 for a nonsensical step. |
|
| | | //+------------------------------------------------------------------+
|
| | | int VolumeDigits(const double volumeStep)
|
| | | {
|
| | | if(volumeStep <= 0) return 2;
|
| | |
|
| | | double scaled = volumeStep;
|
| | | for(int digits = 0; digits < 8; digits++)
|
| | | {
|
| | | if(MathAbs(scaled - MathRound(scaled)) < 1e-9) return digits;
|
| | | scaled *= 10.0;
|
| | | }
|
| | |
|
| | | return 8;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Round a volume DOWN to the broker's step |
|
| | | //| |
|
2026-10-16 21:48:01 +03:00 | | | //| Rounding a position size up raises risk the caller never asked |
|
| | | //| for. The epsilon keeps 0.3 / 0.1 from landing on 2.9999... and |
|
| | | //| flooring to 0.2. |
|
StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00 | | | //| |
|
| | | //| Returns: the volume, floored to the step and rounded to the |
|
| | | //| step's own precision. |
|
| | | //+------------------------------------------------------------------+
|
| | | double FloorToVolumeStep(const double volume, const double volumeStep)
|
| | | {
|
| | | if(volumeStep <= 0) return volume;
|
| | |
|
| | | double steps = volume / volumeStep;
|
| | | double rounded = MathRound(steps);
|
| | |
|
| | | if(MathAbs(steps - rounded) < 1e-7)
|
| | | steps = rounded;
|
| | | else
|
| | | steps = MathFloor(steps);
|
| | |
|
| | | return NormalizeDouble(steps * volumeStep, VolumeDigits(volumeStep));
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Is this order type a buy of some kind? |
|
| | | //| Returns: true for BUY, BUY_LIMIT, BUY_STOP and BUY_STOP_LIMIT. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool IsBuySide(const ENUM_ORDER_TYPE orderType)
|
| | | {
|
| | | return orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_BUY_LIMIT ||
|
| | | orderType == ORDER_TYPE_BUY_STOP || orderType == ORDER_TYPE_BUY_STOP_LIMIT;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Minimum distance a stop or pending price must keep from market |
|
| | | //| |
|
| | | //| SYMBOL_TRADE_STOPS_LEVEL is what the server enforces; |
|
| | | //| SYMBOL_TRADE_FREEZE_LEVEL is how close an existing order may sit |
|
| | | //| before it can no longer be modified. The larger of the two is the |
|
| | | //| conservative reading. |
|
| | | //| |
|
| | | //| A broker reporting 0 is using a dynamic, spread-derived level |
|
| | | //| rather than declaring "no constraint", so fall back to the |
|
| | | //| current spread instead of assuming anything goes. |
|
| | | //| |
|
| | | //| Returns: the distance in PRICE units, or 0 if the symbol is |
|
| | | //| unknown. |
|
| | | //+------------------------------------------------------------------+
|
| | | double MinStopDistance(string symbol = "")
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | CSymbolInfo symbolInfo;
|
| | | if(!symbolInfo.Name(symbol)) return 0;
|
| | | symbolInfo.RefreshRates();
|
| | |
|
| | | double point = symbolInfo.Point();
|
| | | int levels = (int)MathMax(symbolInfo.StopsLevel(), symbolInfo.FreezeLevel());
|
| | | double distance = levels * point;
|
| | |
|
| | | if(distance <= 0)
|
| | | {
|
| | | double spread = symbolInfo.Ask() - symbolInfo.Bid();
|
| | | if(spread > 0) distance = spread;
|
| | | }
|
| | |
|
| | | return distance;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Would the server accept these stop levels? |
|
| | | //| |
|
| | | //| Validates rather than adjusts, deliberately. Silently widening a |
|
| | | //| stop to make it legal changes the risk without telling the |
|
| | | //| caller, which is worse than refusing the order. |
|
| | | //| |
|
| | | //| Parameters: |
|
| | | //| price - the price stops are measured from: the entry for a |
|
| | | //| pending order, the CLOSING side for an open position |
|
| | | //| (Bid for a long, Ask for a short) |
|
| | | //| sl, tp - pass 0 for "not set", which always passes |
|
| | | //| |
|
| | | //| Returns: true if both levels clear MinStopDistance and sit on the |
|
| | | //| correct side of price. false otherwise, or if the symbol |
|
| | | //| is unknown. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool CheckStopLevels(string symbol, const ENUM_ORDER_TYPE orderType,
|
| | | const double price, const double sl, const double tp)
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | CSymbolInfo symbolInfo;
|
| | | if(!symbolInfo.Name(symbol)) return false;
|
| | |
|
| | | double distance = MinStopDistance(symbol);
|
| | | double tolerance = symbolInfo.Point() * 0.5;
|
| | | bool isBuy = IsBuySide(orderType);
|
| | |
|
| | | if(sl > 0)
|
| | | {
|
| | | double gap = isBuy ? (price - sl) : (sl - price);
|
| | | if(gap < distance - tolerance) return false;
|
| | | }
|
| | |
|
| | | if(tp > 0)
|
| | | {
|
| | | double gap = isBuy ? (tp - price) : (price - tp);
|
| | | if(gap < distance - tolerance) return false;
|
| | | }
|
| | |
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Would the server accept this pending order price? |
|
| | | //| |
|
| | | //| A limit sits on the far side of the market, a stop on the near |
|
| | | //| side, and both must clear MinStopDistance. Getting the side wrong |
|
| | | //| is retcode 10015, and it is easy to do when a signal flips. |
|
| | | //| |
|
| | | //| Returns: true if the price is on the correct side at a legal |
|
| | | //| distance. false otherwise, for a non-pending type, and for |
|
| | | //| BUY_STOP_LIMIT and SELL_STOP_LIMIT, which are pending but |
|
| | | //| need a second (limit) price this function does not take. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool CheckPendingPrice(string symbol, const ENUM_ORDER_TYPE orderType, const double price)
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | CSymbolInfo symbolInfo;
|
| | | if(!symbolInfo.Name(symbol)) return false;
|
| | | if(!symbolInfo.RefreshRates()) return false;
|
| | |
|
| | | double distance = MinStopDistance(symbol);
|
| | | double tolerance = symbolInfo.Point() * 0.5;
|
| | | double gap = 0;
|
| | |
|
| | | switch(orderType)
|
| | | {
|
| | | case ORDER_TYPE_BUY_LIMIT: gap = symbolInfo.Ask() - price; break;
|
| | | case ORDER_TYPE_BUY_STOP: gap = price - symbolInfo.Ask(); break;
|
| | | case ORDER_TYPE_SELL_LIMIT: gap = price - symbolInfo.Bid(); break;
|
| | | case ORDER_TYPE_SELL_STOP: gap = symbolInfo.Bid() - price; break;
|
| | | default: return false;
|
| | | }
|
| | |
|
| | | return gap >= distance - tolerance;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Is this volume tradable on this symbol? |
|
| | | //| Returns: true if within [min, max] and sitting on the volume |
|
| | | //| step. false otherwise, or if the symbol is unknown. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool IsVolumeValid(string symbol, const double volume)
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | CSymbolInfo symbolInfo;
|
| | | if(!symbolInfo.Name(symbol)) return false;
|
| | |
|
| | | if(volume <= 0) return false;
|
| | | if(volume < symbolInfo.LotsMin() - 1e-9) return false;
|
| | | if(volume > symbolInfo.LotsMax() + 1e-9) return false;
|
| | |
|
| | | double step = symbolInfo.LotsStep();
|
| | | if(step <= 0) return false;
|
| | |
|
| | | return MathAbs(volume - FloorToVolumeStep(volume, step)) < 1e-9;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Can positions be opened on this symbol right now? |
|
| | | //| |
|
| | | //| Close-only, short-only and long-only are all states a broker uses |
|
| | | //| routinely - around rollover, or on an expiring contract. |
|
| | | //| |
|
| | | //| Returns: true only for SYMBOL_TRADE_MODE_FULL. false otherwise, |
|
| | | //| or if the symbol is unknown. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool CanOpenPositions(string symbol = "")
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | CSymbolInfo symbolInfo;
|
| | | if(!symbolInfo.Name(symbol)) return false;
|
| | |
|
| | | return symbolInfo.TradeMode() == SYMBOL_TRADE_MODE_FULL;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Normalize lot size to broker requirements |
|
| | | //| |
|
2026-10-16 21:48:01 +03:00 | | | //| Rounds DOWN to the volume step, then clamps into |
|
StopFirst 1.00, the read-only core
Fourteen headers, a header-only MQL5 library: lot size from risk, pips on
forex, gold and indices, broker checks, bars and candle patterns, cached ATR,
sessions in GMT, account risk, symbol suffixes, a calendar filter. Sends no
trade requests. Plus the LotByRisk example script.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:40:34 +03:00 | | | //| [min, max]. Clamping up to the minimum is this function's stated |
|
| | | //| contract - the caller has already chosen a size and is asking for |
|
| | | //| the nearest legal one. CalcLotSizeByRisk deliberately does not do |
|
| | | //| that, because there the minimum may exceed the risk budget. |
|
| | | //| |
|
| | | //| Returns: a tradable lot size. The input unchanged if the symbol |
|
| | | //| is unknown. |
|
| | | //+------------------------------------------------------------------+
|
| | | double NormalizeLots(double lots, string symbol = "")
|
| | | {
|
| | | if(symbol == "") symbol = _Symbol;
|
| | |
|
| | | CSymbolInfo symbolInfo;
|
| | | if(!symbolInfo.Name(symbol)) return lots;
|
| | |
|
| | | double minLot = symbolInfo.LotsMin();
|
| | | double maxLot = symbolInfo.LotsMax();
|
| | | double lotStep = symbolInfo.LotsStep();
|
| | | if(lotStep <= 0) return lots;
|
| | |
|
| | | //--- floor to the step at the step's own precision, not a
|
| | | //--- hardcoded 2 decimals, which broke 0.001-step symbols
|
| | | lots = FloorToVolumeStep(lots, lotStep);
|
| | | lots = MathMax(minLot, MathMin(maxLot, lots));
|
| | |
|
| | | return NormalizeDouble(lots, VolumeDigits(lotStep));
|
| | | }
|
| | |
|
| | | #endif // STOPFIRST_SYMBOL_MQH
|
| | | //+------------------------------------------------------------------+
|