A position-size function is the most dangerous piece of code in many EAs: a single error can multiply risk by ten. Unit tests catch most such errors before going live.
Cases worth testing:
Normal case: EURUSD, 0.5% risk, 10-pip stop.
JPY pair: different pip value.
Cross pair in a non-USD account currency.
Gold and an index with unusual contract sizes.
Very small stop: result should be capped at maximum lot size.
Very large stop: result below minimum lot — should return zero, not minimum lot.
Lot step rounding: always down.
In MQL5, tests can run as a script that calls the function with fixed inputs and compares results with values calculated by hand. In Python, frameworks like pytest make it simple.
Running the tests after every code change, and after broker changes to symbol specifications, keeps the function trustworthy.
Do you test your sizing code, and which edge case caught a bug?
FXS Prop Desk wrote: Wed Oct 07, 2026 3:33 pm
A position-size function is the most dangerous piece of code in many EAs: a single error can multiply risk by ten. Unit tests catch most such errors before going live.
Cases worth testing:
Normal case: EURUSD, 0.5% risk, 10-pip stop.
JPY pair: different pip value.
Cross pair in a non-USD account currency.
Gold and an index with unusual contract sizes.
Very small stop: result should be capped at maximum lot size.
Very large stop: result below minimum lot — should return zero, not minimum lot.
Lot step rounding: always down.
In MQL5, tests can run as a script that calls the function with fixed inputs and compares results with values calculated by hand. In Python, frameworks like pytest make it simple.
Running the tests after every code change, and after broker changes to symbol specifications, keeps the function trustworthy.
Do you test your sizing code, and which edge case caught a bug?
Hi,
honestly, interesting topic. Im not friend of demo accounts, because i believe that in nutshell they will not teach you anything, because you will not feel
real emotions, like stress and hapiness, but on anothe hand if it is for EA purpose i got it.
You have hit the nail on the head. The position-sizing algorithm is the nuclear reactor core of any Expert Advisor. A flawed entry logic will slowly bleed an account over a hundred trades; a flawed position-sizing function will blow the account in one.
To answer your prompt in an extended way, let's break down exactly why those cases are so volatile, add a few more critical edge cases to your testing matrix, and address how these bugs actually manifest in practice.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Your baseline cases are spot on. Here is exactly what those tests are proving under the hood in environments like MQL5 or C# cAlgo:
The JPY Pair & Point Value: A standard pair uses 5 decimal places (Point = 0.00001), while JPY pairs use 3 (Point = 0.001). A hardcoded pip multiplier will calculate a JPY position size that is exactly 100 times too large.
Cross Pairs in Non-USD Accounts: If you are trading GBPJPY on a EUR-denominated account, the EA must calculate the tick value dynamically. If the function relies on a static $10 per standard lot assumption, the risk will drift wildly as the EURGBP and EURJPY exchange rates fluctuate.
Gold and Indices: Contract sizes (SYMBOL_TRADE_CONTRACT_SIZE) vary wildly. Gold might be 100 ounces per lot, but a DAX index CFD might be 1 or 25 depending on the broker. A robust sizing function must query the broker's specific contract size dynamically rather than hardcoding multipliers.
Extreme SL Capping (Max/Min):
Small SL: A 2-pip SL on a 1% risk could calculate a volume of 150 lots. If SYMBOL_VOLUME_MAX is 50, the EA must cap it, but it should also ideally flag that the risk profile has been fundamentally altered.
Large SL: A 500-pip SL might result in a calculated volume of 0.004 lots. Returning SYMBOL_VOLUME_MIN (0.01) means the EA would take 2.5x the intended risk. It must return 0 and reject the trade.
Lot Step Rounding: Brokers are merciless here. If the SYMBOL_VOLUME_STEP is 0.01, sending a volume of 0.115 will return an ERR_INVALID_VOLUME rejection. Rounding must always be a MathFloor equivalent (e.g., MathFloor(volume / step) * step), never a standard round up, to guarantee risk never exceeds the maximum allowable percentage.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
If you are building a unit test suite (whether a standalone MQL5 script or a Python/pytest suite for back-end calculations), you must include these silent killers:
The Division-by-Zero Trap: What happens if the entry price and SL are identical, or the SL isn't properly initialized? The distance is 0. Calculating RiskAmount / SL_Distance will crash the EA with a zero-divide exception, halting all trading.
Free Margin vs. Risk: The sizing function might calculate 5 lots perfectly to risk 1%. However, if the account leverage is low (e.g., 1:30 in the EU), 5 lots might require more Free Margin than the account holds. The function must cross-check the calculated volume against OrderCalcMargin() and reduce the size if it violates margin constraints.
Floating Point Precision Errors: 0.01 in floating-point math is often 0.010000000000000000208.... If this isn't strictly normalized using NormalizeDouble (in MQL) or Math.Round (in C#) before being sent to the broker API, strict trade servers will reject the order.
The Weekend/Illiquid Gap: Testing the function when the market is closed or during a massive news event. Sometimes SYMBOL_TRADE_TICK_VALUE returns 0 when liquidity vanishes or the market closes. If the EA doesn't catch the 0 tick value, it either divides by zero or calculates infinite lot sizes.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Here is what happens: A developer writes a flawless sizing function using SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE). It works perfectly in live forward-testing. But when they run it in the MQL5 Strategy Tester on custom dates, the history for the cross currency required to calculate the tick value isn't loaded yet on the very first tick of the simulation.
SYMBOL_TRADE_TICK_VALUE silently returns 0.0.
The equation PositionSize = Risk / (StopLossPoints * TickValue) becomes a division by zero. The backtester instantly terminates the run with a critical error on the very first trade.
The fix—and the unit test that now catches it—is to assert that TickValue > 0 before allowing the math to proceed, and forcing a fallback calculation using SYMBOL_TRADE_CONTRACT_SIZE and the current ask/bid if the built-in tick value function fails.
Trusting the broker's API to always return valid specification data is the vulnerability. The best unit tests assume the broker's data feed is actively trying to break your math.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Here is a production-ready Pine Script (v5) implementation of the robust position-sizing function we discussed.
Since TradingView's environment handles account currencies and cross-pair conversions differently than MQL5 (Pine abstracts much of this away via syminfo.pointvalue), the primary risks here are division-by-zero, precision rounding, and enforcing hard minimums/maximums.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
I have set this up as a strategy script so you can immediately backtest it against your daily or 15-minute price action models. The dummy SMA entry logic is just there to trigger the function—you can easily strip it out and plug in your own entry criteria.
syminfo.pointvalue vs SYMBOL_TRADE_TICK_VALUE: In MQL5, you have to query the tick value dynamically and account for cross-currency math. Pine Script handles that backend conversion automatically through syminfo.pointvalue, which represents the value of a full 1.0 point move mapped to your account currency.
Lack of Native SYMBOL_VOLUME_STEP: TradingView doesn't automatically query the broker for the instrument's exact volume step (like 0.01 for forex or 1.0 for indices). You have to feed that into the script via inputs (which I mapped to i_qtyStep), making the math.floor() logic critical for instruments with unusual lot increments.
Order Execution Integrity: Notice the if posSize > 0 wrapper around the strategy.entry() commands. If the sizing function catches an error (like a massive stop loss that yields a 0.004 lot size), it returns 0. Pine will completely ignore the strategy.entry block, silently protecting the equity curve from an oversized minimum-lot execution.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
To elevate this from a functional script to a professional-grade trading module, we need to introduce architectural patterns familiar to enterprise software engineering (which I know you appreciate from your C# and .NET background).
In Pine Script v5, this means moving away from procedural spaghetti code and utilizing User-Defined Types (UDTs) and Methods to encapsulate the risk logic.
A "pro" version must also include:
Multiple Risk Models: Institutional traders don't just use "% of Equity". They toggle between % Equity, % Balance (ignoring floating PnL), and Fixed Cash.
Margin Validation: Checking if the calculated position violates the account's leverage limits before execution.
Detailed Status Handlers: Returning exact error states (e.g., "Margin Exceeded", "Below Min Lot") rather than just silently failing.
Telemetry / HUD: A real-time on-chart dashboard that displays the exact math behind the last calculation so you can audit it visually.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.