System Development and Backtesting Prop Guide

Algo & Quant Prop Trading By Alphaex Capital Updated

If you're researching system development and backtesting, this guide explains the essentials in plain language.

Key takeaways

  • A simple 50-SMA/200-SMA crossover on EUR/USD with 1% risk targets provides a fast way to validate trading ideas.
  • Designing a modular algorithm with separate data ingestion, signal generation, risk management, and execution layers ensures maintainability and adaptability.
  • Cleaning minute-level data in pandas and storing it as feather or parquet files enables rapid, reliable backtesting.
  • Modeling realistic slippage, latency, commissions, and using walk-forward validation prevents over-fitting and reveals true strategy performance.

Quick Validation Of A Trading Idea

If you're a beginner looking for quick validation, start with a plain-vanilla moving-average crossover on EUR/USD. The idea is simple: when the 50-period SMA crosses above the 200-period SMA you go long, when it flips below you go short. No fancy frameworks, just a handful of lines.

Step-by-step code

# assume df is a one-minute bar DataFrame with columns: ['time','bid','ask']
df['mid'] = (df['bid'] + df['ask']) / 2
df['sma50'] = df['mid'].rolling(50).mean()
df['sma200'] = df['mid'].rolling(200).mean()

# generate signals
df['signal'] = 0
df.loc[df['sma50'] > df['sma200'], 'signal'] = 1   # long
df.loc[df['sma50'] < df['sma200'], 'signal'] = -1  # short

Now add a profit target and stop-loss that each equal 1 % of your account equity. Grab the equity value once, then compute dollar thresholds:

account_eq = 10000  # example equity
risk = account_eq * 0.01

df['entry_price'] = df['mid'].where(df['signal'].diff()!=0)
df['target'] = df['entry_price'] + (risk / account_eq) * df['entry_price']
df['stop']   = df['entry_price'] - (risk / account_eq) * df['entry_price']

For backtesting basics, loop through the rows, close a trade when price hits either the target or stop. Each closed trade updates a running equity line - no class needed.

equity_curve = [account_eq]
for i,row in df.iterrows():
    if row['signal'] != 0 and not pd.isna(row['entry_price']):
        # pretend we exit at target for demo
        profit = risk if row['mid'] >= row['target'] else -risk
        equity_curve.append(equity_curve[-1] + profit)

The resulting equity_curve list is your quick validation equity plot. You can dump it to a CSV or chart it with any plotting library. This minimal setup gives you a fast way to do trading idea testing while you're still in the system development phase.

Designing The Core Algorithm Architecture

If you're building a modular trading system , think of it in layers you can pull apart and swap. First comes the data ingestion layer - you pull price feeds, economic calendars, or alternative data streams. In Python, pandas is your go-to for time-series frames, while numpy gives you fast numeric operations. Keep the ingest code thin, just enough to clean, timestamp and store the data in a consistent format.

Next up is the signal generation layer. This is where you plug in indicators like RSI, MACD, or your own custom models. Design each signal as an interchangeable module that accepts the cleaned DataFrame and spits out a buy/sell flag. Because the modules are independent, you can test a new momentum filter without touching the rest of the system.

After a signal fires, the risk management layer steps in. It should evaluate position size, stop-loss levels, and portfolio exposure. By isolating risk rules, you can tweak a volatility-based sizing rule without re-writing the signal code.

The execution layer talks to your broker . A thin wrapper around the broker's REST or FIX API abstracts order placement, order status checks, and error handling. This wrapper shields the rest of the system from API quirks, letting you swap brokers by swapping just the wrapper implementation.

  • Data ingestion - pandas / numpy for clean time series.
  • Signal generation - plug-in modules (RSI, MACD, custom ML).
  • Risk management - position sizing, limits, portfolio checks.
  • Execution - thin API wrapper, universal order interface.

By keeping these four layers distinct, you end up with a robust algorithm architecture that's easy to maintain, test, and evolve as market conditions change.

Acquiring And Cleaning Historical Market Data

1. Grab minute-level EUR/USD from a free feed

If you're a beginner, start with a public API like Alpha Vantage or a CSV archive on Investing.com . Request the “FX_INTRADAY” endpoint, set interval=1min , and save the reply as eurusd_min.csv . A quick curl or Python requests call does the trick.

2. Spot and fill missing candles

  • Load the CSV into a pandas.DataFrame and set the timestamp as the index.
  • Use df.asfreq('T') to create a full minute grid; gaps turn into NaN rows.
  • Forward-fill the price columns ( ffill() ) and set volume to zero for the empty minutes.

3. Adjust GBP/JPY for daylight-saving and holidays

When you pull GBP/JPY data, remember the London session shifts an hour forward in spring and back in autumn. Convert timestamps to UTC first, then apply the pytz library to localize to “Europe/London”. Holidays appear as long gaps - drop any period longer than a full trading day and mark it as a non-trading interval.

4. Resample raw tick data to 5-minute bars

  1. Read the tick file, keep columns for price, size and timestamp.
  2. Round timestamps down to the nearest 5-minute mark ( dt.floor('5T') ).
  3. Group by the rounded time and calculate open, high, low, close, and sum of volume.

5. Store the cleaned set for fast backtesting

After you've cleaned and resampled, write the DataFrame to a .feather or .parquet file. Both formats are column-oriented, compress well, and load in milliseconds - perfect for backtesting data pipelines.

Selecting Indicators And Parameter Sets

If you're a trader who swings between major pairs and exotic ones, the right technical indicators can feel like a secret weapon. Let's break down how you might pick and tweak them for EUR/USD's deep liquidity versus GBP/JPY's wild FX volatility.

  • EMA vs. VWAP on EUR/USD - The Exponential Moving Average reacts quickly to price changes, which is handy when the market is humming with tight spreads. VWAP (Volume-Weighted Average Price) shines when you need a reference that respects trade volume, but on a pair as liquid as EUR/USD it often mirrors the EMA with only a slight lag. For most day-traders, the EMA's speed gives a clearer entry signal.
  • EMA vs. VWAP on GBP/JPY - Here the volatility spikes, so VWAP can help you see where the bulk of trading happened during a chaotic session. The EMA may whisk you into a false breakout. Many FX volatility hunters stick with VWAP for GBP/JPY, pairing it with a momentum filter.

One combo that works well on the volatile GBP/JPY is a 14-period RSI paired with a 20-period Bollinger Band . The RSI flags overbought or oversold zones, while the Bollinger Band shows price expansion. When the price touches the lower band and the RSI dips below 30, you've got a potential bounce.

For risk control, set an ATR-based stop loss . Calculate the Average True Range over 14 periods, then place your stop about 1.5 x ATR away from the entry. This lets the stop breathe during high-impact news.

Don't lock yourself into one lookback. Test windows from 10 to 50 periods during backtesting - short windows catch fast moves , longer ones smooth out choppy legs. Parameter optimization across these ranges helps you adapt to shifting market regimes without over-fitting.

Implementing Precise Risk Management Rules

If you're a systematic trader, the moment you pull a strategy into a backtest you should lock in the same money-management discipline you'd use with real cash. That means coding a drawdown limit, a volatility-adjusted position size, a trailing stop, and a cap on how many trades can sit open at once.

  • Daily drawdown limit : before each new bar the engine checks the day's unrealized loss. If the loss hits 2 % of the current equity, the script flips a flag and aborts any further entries for that day. This simple rule keeps a single bad session from wiping out months of gains.
  • Position sizing : calculate risk per trade as 1 % of the account balance. Then use the instrument's recent volatility - often the Average True Range (ATR) - to turn that risk amount into a share count. For example, size = (0.01 * equity) / (ATR * multiplier) where the multiplier reflects the distance to your stop.
  • Trailing stop : attach a stop that trails the price by half of the current ATR (0.5 x ATR). As the market moves in your favor the stop automatically tightens, protecting profit while still giving the trade room to breathe.
  • Concurrency rule : enforce a hard ceiling of three open positions across all symbols. The backtest logic checks the active position count before allowing a new entry; if three are already live, the next signal is ignored until one closes.

Embedding these trading risk rules directly into the backtest engine means every simulated trade respects the same constraints you'd apply on a live account, giving you a realistic picture of strategy durability.

Building A Realistic Backtest Engine

If you're a beginner looking to test a strategy, you want a backtest engine that feels like the real market, not a textbook exercise. The first step is slippage modeling - set a fixed cost of 0.5 pip for every EUR/USD trade and 1 pip for each GBP/JPY trade. It sounds simple, but it forces the engine to respect the thin spreads you see in live trading.

Next, think about execution latency. Most traders notice a delay between signal and fill, so we model order execution as two bars later than the trigger. That two-bar lag captures the reality of market orders slipping past the bid-ask spread while you wait for the next price bar.

Don't forget commission. We charge 0.1 basis points per side, which adds up when you flip positions frequently. By including this tiny cost, the simulation realism improves and you can see if a high-turnover strategy still makes money after fees.

  • Fixed slippage: 0.5 pip (EUR/USD), 1 pip (GBP/JPY) per trade
  • Execution latency: order filled two bars after signal
  • Commission: 0.1 bp per side, applied on entry and exit
  • Toggle mode: switch between tick-level and bar-level simulation to compare speed and accuracy

When you toggle between tick-level and bar-level simulation, you can see how much detail you really need. Tick data gives you the finest granularity, but it can slow the backtest down. Bar data runs faster, yet still respects the execution latency you built in. Play with both and let the results tell you which level of detail matches your trading style.

Analyzing Performance Metrics And Trade Statistics

If you want to gauge whether a strategy can survive the market's ups and downs, you need a handful of performance metrics that speak the truth. Below is a quick “cheat sheet” you can follow after every back-test or live run.

Core profit calculations

  • Net profit: sum of all closed-trade P&L, positive and negative.
  • Gross profit: total of only the winning trades.
  • Gross loss: total of only the losing trades (use absolute values).
  • Profit factor: divide gross profit by gross loss; a value above 1.5 usually hints at a robust edge.

Risk-adjusted return - Sharpe ratio

Take your daily returns series, subtract a risk-free rate of zero, . The Sharpe ratio is the average daily return divided by that deviation, annualised if you prefer. A higher Sharpe tells you you're earning more per unit of volatility.

Win rate and trade expectancy

Split the win rate by currency pair to see where the strategy shines. For EUR/USD and GBP/JPY you'll want two numbers: % of winning trades versus % of losing trades. Combine those with average win size and average loss size to get trade expectancy - the dollar value you can expect per trade on average.

Monte Carlo equity path simulation

Run thousands of randomised trade orderings on . The simulation builds a distribution of possible outcomes, letting you spot tail risk - those rare but severe drawdowns that a simple back-test might hide. If the 5th percentile equity line stays above a comfortable buffer, you've got a strategy that can handle rough seas.

Optimizing And Forward Testing With Walk-Forward Validation

If you're a trader who likes to tweak indicators, start with a solid parameter optimization routine. Run a grid search on EMA periods ranging from 10 to 30 and RSI thresholds from 30 to 70. Do this on a six-month in-sample window so you have enough data for a reliable fit, but not so much that the market conditions become stale.

  • Set up the grid, let the back-tester loop through each EMA/RSI combo, record profit factor, win rate and Sharpe ratio.
  • Pick the top-performing sets - don't fall for the single best result, look for a cluster of stable scores.

Next, apply walk forward validation. Split the next three months as an out-of-sample period, run the strategy with the chosen parameters, then re-calibrate using the most recent data before moving forward another three months. This forward testing loop mimics real-world trading and is a key overfitting prevention tool.

While you step through each walk-forward segment, keep an eye on how the system behaves under different market regimes. Compare performance during GBP/JPY volatility spikes with the steadier EUR/USD liquidity environment. If the Sharpe ratio starts to slip, note the amount of degradation after each forward step.

Document those drops - a gradual decline signals you're chasing noise, a sudden plunge may mean the market structure has shifted. When the Sharpe ratio consistently falls beyond a tolerable threshold, it's time to stop tuning and lock in the current settings for live deployment.

FAQ

Frequently Asked Questions

What is trading system development and backtesting?

System development involves creating, testing, and refining trading strategies with objective rules for entries, exits, and position sizing. Backtesting applies these rules to historical data to evaluate how the strategy would have performed in the past. This process is essential for prop traders because it provides evidence that a strategy has an edge before risking real capital, helping avoid approaches that look promising but actually lose money over time.

What topics are covered in system development and backtesting?

This section covers adapting systems to daily loss limits, backtesting strategies for prop challenges, building trading systems for prop firms, customizing for maximum drawdown rules, documentation requirements, expectancy and edge calculations, fixed fractional and ratio position sizing, forward testing strategies, Kelly criterion considerations, live tracking versus backtest performance comparisons, Monte Carlo analysis, optimization techniques, performance metrics, position sizing model development, refining systems after challenges, robustness testing, sample size determination, scenario analysis, and walk-forward analysis.

How do I backtest a trading strategy properly?

Use high-quality historical data that includes realistic transaction costs like spreads, slippage, and commissions. Test over sufficiently long periods covering various market conditions. Avoid overfitting by using out-of-sample data to validate results. Walk-forward analysis helps ensure strategies work across different time periods. Always backtest with the same risk management rules you'll use in live trading to get accurate performance expectations.

What's the difference between backtesting and forward testing?

Backtesting applies your strategy to historical data to evaluate past performance, while forward testing trades in real-time with a demo account or small position sizes. Backtesting helps quickly identify promising strategies, but forward testing validates they work in current market conditions with realistic execution. Always forward test before going live with real capital, as backtest results can be misleading due to overfitting or changing market dynamics.

Continue Learning

Explore more guides and enhance your trading knowledge.