Page 1 of 3
Unit tests for trading code: boring and invaluable
Posted: Wed Oct 28, 2026 7:10 am
by Shadow Trader
Code that handles money deserves tests. Unit tests check small pieces of logic in isolation, and a handful of them can catch the bugs that cost real money.
Candidates for testing: position sizing calculations, including rounding to lot steps and handling of minimum and maximum volumes. Pip and point conversions across digit counts. Stop and target price calculations for buys and sells. Time filters, including daylight saving transitions. Spread filters. Risk limit checks.
Each test provides inputs and expected outputs. For example, a risk of 100 dollars, a stop of 10 pips on EURUSD, and a standard pip value should yield a position of about 0.10 lots. If a later change breaks the function, the test fails immediately.
Tests also document intended behaviour. They make refactoring safer, since changes can be verified in seconds.
Edge cases matter most: zero distances, extreme prices, rejected orders, empty data. Testing those prevents crashes at bad moments.
Testing frameworks exist for every language, though MQL users often write simple scripts that run checks in the tester.
A neat practice is writing a test for every bug found. Once a bug is fixed with a test, it can never quietly return. Over a year, the test suite becomes a record of past mistakes and a guard against repeating them.
Which functions in trading code are covered by tests, and which bugs would they have caught?
Re: Unit tests for trading code: boring and invaluable
Posted: Thu Oct 29, 2026 7:07 pm
by PTScalper
Shadow Trader wrote: Wed Oct 28, 2026 7:10 am
Code that handles money deserves tests. Unit tests check small pieces of logic in isolation, and a handful of them can catch the bugs that cost real money.
Candidates for testing: position sizing calculations, including rounding to lot steps and handling of minimum and maximum volumes. Pip and point conversions across digit counts. Stop and target price calculations for buys and sells. Time filters, including daylight saving transitions. Spread filters. Risk limit checks.
Each test provides inputs and expected outputs. For example, a risk of 100 dollars, a stop of 10 pips on EURUSD, and a standard pip value should yield a position of about 0.10 lots. If a later change breaks the function, the test fails immediately.
Tests also document intended behaviour. They make refactoring safer, since changes can be verified in seconds.
Edge cases matter most: zero distances, extreme prices, rejected orders, empty data. Testing those prevents crashes at bad moments.
Testing frameworks exist for every language, though MQL users often write simple scripts that run checks in the tester.
A neat practice is writing a test for every bug found. Once a bug is fixed with a test, it can never quietly return. Over a year, the test suite becomes a record of past mistakes and a guard against repeating them.
Which functions in trading code are covered by tests, and which bugs would they have caught?
Hi Shadow Trader,
In algorithmic trading, testing is the only barrier between a logic flaw and sudden capital drawdown. Because execution happens in milliseconds—often completely headless on a VPS—a single unhandled exception, state mismatch, or rounding error can be fatal.
When transitioning from discretionary trading to automated execution, isolating pure mathematical logic from stateful broker API calls is the foundational step for testability. Here is a breakdown of the critical functions that demand rigorous unit testing, and the specific market-killing bugs they prevent.
Re: Unit tests for trading code: boring and invaluable
Posted: Thu Oct 29, 2026 7:07 pm
by PTScalper
The Architecture of a Resilient Test Suite
Writing testable trading code requires separating the "decider" from the "executor." If your position sizing logic is tangled inside your OnTick() or OnBar() execution block, it is nearly impossible to unit test.
By abstracting calculations into pure functions (e.g., a C# class library in a cTrader architecture), you can feed thousands of simulated edge cases into the logic in milliseconds.
Re: Unit tests for trading code: boring and invaluable
Posted: Thu Oct 29, 2026 7:07 pm
by PTScalper
High-Value Edge Cases to Mock:
Zero distances: What happens if the entry and the stop loss are identical due to a flat market structure? (Prevents divide-by-zero crashes).
Empty arrays: What if the historical data feed drops and the SMA array returns null? (Prevents NullReferenceException during a live trade).
Weekend gaps: Simulating a massive price gap that skips over your limit order completely.
Re: Unit tests for trading code: boring and invaluable
Posted: Thu Oct 29, 2026 7:08 pm
by PTScalper
Regression Testing as a Ledger of Mistakes
The concept of writing a test for every bug is known as regression testing. If an algorithm erroneously opens a long position instead of a short one because of a flipped inequality operator during a specific market regime, you fix the code and write a test that inputs that exact market state. Over years of development, this suite becomes an immutable ledger of past mistakes, ensuring that structural refactoring never quietly reintroduces a historical flaw.
Re: Unit tests for trading code: boring and invaluable
Posted: Thu Oct 29, 2026 7:08 pm
by PTScalper
How do you architect a mocking layer for broker APIs in C# to simulate network drops and slippage during unit tests?
Architecting a mocking layer for broker APIs in C# requires isolating the trading logic from the broker's proprietary SDK (such as cTrader's Automate API or a FIX protocol wrapper) using the Dependency Injection (DI) and Adapter patterns.
When scalping on Raw ECN environments, the execution layer cannot assume a MarketOrder command will synchronously return a clean fill. The mock must simulate the asynchronous, hostile reality of the order book: variable latency, asymmetric slippage, and transient socket disconnects.
Code: Select all
public interface IExecutionProvider
{
Task<ExecutionResult> SendMarketOrderAsync(string symbol, TradeDirection direction, double volume, double requestedPrice);
Task<ExecutionResult> ClosePositionAsync(string positionId);
event EventHandler<ConnectionStatusEventArgs> OnConnectionChanged;
}
public record ExecutionResult(bool IsSuccess, string PositionId, double FilledPrice, double Volume, string ErrorCode);
public enum TradeDirection { Buy, Sell }
Re: Unit tests for trading code: boring and invaluable
Posted: Thu Oct 29, 2026 7:09 pm
by PTScalper
2. The Mock ECN Provider
The mock implementation implements IExecutionProvider and injects controlled chaos. You configure it with probabilities for network drops and maximum slippage pips to stress-test your algorithm's state machine.
Code: Select all
public class MockEcnProvider : IExecutionProvider
{
private readonly double _simulateLatencyMs;
private readonly double _dropProbability;
private readonly double _maxSlippagePips;
private readonly Random _rng = new();
public MockEcnProvider(double simulateLatencyMs = 50, double dropProbability = 0.05, double maxSlippagePips = 1.5)
{
_simulateLatencyMs = simulateLatencyMs;
_dropProbability = dropProbability;
_maxSlippagePips = maxSlippagePips;
}
public async Task<ExecutionResult> SendMarketOrderAsync(string symbol, TradeDirection direction, double volume, double requestedPrice)
{
// 1. Simulate Network Latency
await Task.Delay(TimeSpan.FromMilliseconds(_simulateLatencyMs + _rng.Next(-10, 20)));
// 2. Simulate Transient Network Drop
if (_rng.NextDouble() < _dropProbability)
{
throw new TimeoutException("Broker API failed to respond within the timeout period.");
}
// 3. Simulate Slippage (Worse price on entry)
double slippageApplied = _rng.NextDouble() * _maxSlippagePips;
double pipValue = 0.0001; // Assuming 5-digit broker standard pair
double filledPrice = direction == TradeDirection.Buy
? requestedPrice + (slippageApplied * pipValue)
: requestedPrice - (slippageApplied * pipValue);
// 4. Simulate Rejections (e.g., Liquidity provider rejected)
if (volume > 10.0 && _rng.NextDouble() < 0.1)
{
return new ExecutionResult(false, string.Empty, 0, 0, "ERR_LIQUIDITY_REJECTED");
}
string mockPositionId = Guid.NewGuid().ToString("N");
return new ExecutionResult(true, mockPositionId, Math.Round(filledPrice, 5), volume, string.Empty);
}
public Task<ExecutionResult> ClosePositionAsync(string positionId)
{
// Implementation mirrors SendMarketOrderAsync for exit slippage and drops
throw new NotImplementedException();
}
public event EventHandler<ConnectionStatusEventArgs>? OnConnectionChanged;
}
Re: Unit tests for trading code: boring and invaluable
Posted: Thu Oct 29, 2026 7:09 pm
by PTScalper
3. Stress-Testing the Algorithm's State Machine
With the MockEcnProvider injected into your unit tests, you can validate how your algorithm handles the worst-case scenarios.
Testing the Ghost Order Scenario (Network Drop):
If SendMarketOrderAsync throws a TimeoutException, your algorithm does not know if the order reached the broker matching engine before the socket dropped, or if it failed locally.
A unit test can assert that your algorithm gracefully catches the exception, shifts its internal state to PendingReconciliation, and queries the broker's open positions list upon reconnection before attempting to resend the order.
Testing the Anti-Martingale / Position Sizing with Slippage:
By injecting positive and negative slippage into the mock, you can write assertions to ensure your exponential risk scaling logic correctly calculates the next position size based on the actual filled equity, rather than the theoretical requested equity.
Re: Unit tests for trading code: boring and invaluable
Posted: Thu Oct 29, 2026 7:10 pm
by PTScalper
The foundational mechanic for solving ghost orders in a distributed trading system relies on idempotency—specifically, generating a unique ClientOrderId (often mapped to the Label, Comment, or ClOrdID in FIX/Raw ECN environments) before the order leaves your local machine.
If a network exception severs the connection during routing, the local state machine immediately locks down to prevent duplicate entries, transitions into a reconciliation loop, and queries the broker using that exact ID upon reconnection.
Here is the C# architecture for this pattern using an asynchronous state machine.
Re: Unit tests for trading code: boring and invaluable
Posted: Thu Oct 29, 2026 7:10 pm
by PTScalper
1. The State Machine and Execution Lock
First, define the states and use a SemaphoreSlim to ensure no other tick events or timers can trigger new orders while the system is in an uncertain state.
Code: Select all
public enum ExecutionState
{
Idle,
Routing,
Reconciling,
InPosition
}
public class OrderManager
{
private ExecutionState _currentState = ExecutionState.Idle;
private readonly SemaphoreSlim _executionLock = new(1, 1);
private readonly IExecutionProvider _brokerApi;
// Stored locally before network transmission
private string _pendingClientOrderId;
public OrderManager(IExecutionProvider brokerApi)
{
_brokerApi = brokerApi;
_brokerApi.OnConnectionRestored += async (s, e) => await ReconcileStateAsync();
}
}