All projects
०२Active research · Data and evaluation2026
  • Python
  • pandas
  • FastAPI

Trading Lab

A Python workbench that tests trading rules against a buy-and-hold benchmark, with execution timing and costs built into the evaluation.

Active research. This page covers two stages. The first is a daily-bar engine, described in an engineering snapshot dated August 2026. The second is a later opening-range breakout protocol, which was still a draft in the most recent record.

The question is whether the idea beats holding

A trading rule can make money in a rising market and still lose to buying the asset and waiting. Trading Lab asks that question first. The engine computes the buy-and-hold benchmark in the same function that scores the strategy, over the same dates and with the same starting money, so a strategy return never appears without its comparison. The leaderboard sorts by margin over the benchmark instead of raw return, because a strong stock makes almost any rule’s raw return look impressive.

The engine loads prices with pandas, adds moving-average indicators, turns them into a position that is either fully invested or in cash, simulates the trades, and scores the result. A FastAPI server and a one-page web interface sit on top for running experiments. Runs from the web interface are appended to a leaderboard, so a failed idea stays on the record.

Keeping the decision apart from the fill

The simulator is a bar-by-bar loop instead of a vectorized expression. That’s slower, but it makes the gap between deciding and trading part of the structure rather than something to remember. The position used on any day is the one computed from the previous day’s close, and the order fills at the next open. Deciding on a close and filling at that same close is the easiest way for a backtest to flatter itself.

The engine charges a cost on every fill. It uses one blended figure of 0.10% per side for brokerage, charges, and slippage. That’s cruder than an itemized model, but it doesn’t claim precision the data can’t support. Stops are checked at the close and filled at the next open, so an overnight gap stays in the result. Positions are all in or all out because the thing under test is timing, and position sizing would blur the answer.

One evaluator for different ideas

The scoring code doesn’t know what produced a position. A moving-average crossover and a random-forest direction model both write a position column and go through the same evaluator, so the model gets no softer treatment than the crossover.

The machine-learning experiment trains once on the first 70% of the history and tests on the last 30%. That’s a chronological holdout. It’s a reasonable first step, but it isn’t walk-forward validation, and the snapshot points out that the README and the script’s docstring both use that term for it. The snapshot reports that the model’s accuracy barely cleared a naive always-up baseline and that the resulting trades lost to buy and hold. It takes that result from the README and does not re-run it. If the result holds, it shows that a small edge in predicting direction doesn’t survive a cost on every round trip.

What the August snapshot found

The strategy the lab rated best was an EMA 20/50 crossover on ten years of Nifty 50 data. Its margin over buy and hold was small and positive, and it turned negative when the assumed cost rose from 0.10% to 0.15% per side. The snapshot interprets this sensitivity as a reason to distrust the small measured edge. The result depends on the cost assumption.

A parameter sweep made the same point from another side. Of 36 fast and slow average combinations, four beat the benchmark. One of the highest-scoring pairs, 25/50, sat between two pairs that lost badly. That uneven pattern raises an overfitting concern; it does not prove that the strategy was overfit. The lab reported the standard 20/50 pair rather than the best cell in the grid. That was the right call, though nothing in the code enforces it.

A trailing-stop test went against intuition. A 5% trailing stop cut the return roughly in half and made the worst drawdown slightly worse, because it sold good trades during ordinary pullbacks.

The snapshot also lists problems in the measurement itself. The benchmark is invested while a slow indicator is still warming up and the strategy is forced to sit in cash, which penalizes slow indicators for a mechanical reason. Two scripts write the leaderboard under different rules, so running the batch script erases results added through the web interface. And some real-world effects sit outside the model. Dividends and taxes are left out, which favors the strategy. Interest on idle cash is also left out, which favors the benchmark.

The paper-trading bot is the piece closest to real money, and it’s the least proven. In the snapshot it had run once, written a single diary line, and filled no orders. It could also execute a stale pending order after a missed run. Its existence says nothing about a forward record yet.

The later research protocol

The October 2026 project record moves from the daily engine toward a more formal study of opening-range breakouts on Indian equities. It adds a dataset registry that records checksums, retrieval dates, time zones, and price basis, along with methodology, reproducibility, and research-log documents. The universe is frozen to the Nifty 50 constituents, and the data now includes 15-minute and hourly bars.

The protocol is careful about what it hasn’t decided. It lists candidate exit rules without choosing one, because the choice affects every result and shouldn’t be made before the protocol is validated. A holdout boundary at 1 October 2026 is meant to stay out of any tuning. The record notes missing late-session bars in its main price source, which matters when the exit time is the question, and it says the intraday cost model hadn’t had all its rates filled in at the last audit. It also considers null-centred statistical tests, such as a block bootstrap or a sign-flip test, along with power calculations and a correction for testing many variants.

ORB v1 is still marked as a draft. The next stage hasn’t started in that record, and no return from it counts as a result.

What would settle it

A reproducible experiment tied to a dataset version, a code revision, a complete cost model, and a fixed protocol would turn a number into a result. A longer paper-trading record would answer a different question, which is whether the process behaves as expected as new data arrives.

Next project: Pancreatic CT research · All projects

Next case studyPancreatic CT researchHow much of a high accuracy score comes from the model, and how much from the split?