Nifty 50 Reinforcement-Learning Trader: a plain-English explainer (v2)

Written for Satya, 5 Oct 2026. Status: final for v2. The v2 run (full 2M-step training, then a 4-fold walk-forward) started at 10:49 IST and finished at 11:16 IST. Every number here comes from the project's code and result files (results/full_training_2000k_results.json, results/walk_forward_results.json), or from the v2 change notes. No numbers are made up. Full v2 results with equity charts: v2 results page.


0. The short version


1. Overview: what the project is and where it came from

Origin. The code is adapted from Ziad Francis's open-source project Reinforcement_Trading_Part_2. That project was built for forex-style data. This project points it at Nifty 50 1-minute data and adds:

Reinforcement learning in one paragraph. You never tell the agent "this is a good trade." It simply tries things inside a market simulator. After each step, the simulator gives back a reward: a score that goes up when equity goes up and down when equity falls. Over millions of steps, the agent adjusts itself to collect more reward. Think of a new trader paper-trading years of charts at high speed, with only the P&L for feedback.

The algorithm: PPO. The learning method is Proximal Policy Optimization (PPO) from the standard stable-baselines3 library. "Proximal" means each update may change the agent's behaviour only a little (clip_range = 0.1), which keeps learning stable. The agent's "brain" is a small neural network called an MLP (multi-layer perceptron): a few layers of numbers that turn the market snapshot into action probabilities. The winning setup uses two hidden layers of 256 neurons each.

The pipeline at a glance

1. Hyperparameter search
8 configs × 2 folds × 50k
DONE winner: large_batch
→
2. v2 full training 2M
~1,900–2,200 steps/s
DONE 10:49–11:07 IST
→
3a. Sealed test
best-val checkpoint
2025-07-10 to 2026-10-01
DONE −19.11% vs B&H −11.69%
→
3b. Walk-forward
4 folds × 200k, large_batch
DONE 1 of 4 folds positive
→
Real edge?
see section 9

2. The data

Item Value
Instrument Nifty 50 index (spot), 1-minute OHLC bars
Source Upstox historical backfill, stored as parquet (data/nifty50.parquet) with an S3 backup
Size 440,167 one-minute bars
Range 2022-01-03 09:15 IST to 2026-10-01 15:29 IST. These are bar-open times; the loader relabels each bar by its close time, so the log shows 09:16 to 15:30.
Decision bars Resampled to 1-hour (H1) bars: 7,967 usable after warm-up
Execution bars The original 1-minute bars, used inside each hour to see whether the stop or the target was hit first

How the hourly bars are built. Each bar is labelled by its close time on clock-hour boundaries. That gives seven bars a day: 10:00, 11:00, 12:00, 13:00, 14:00, 15:00, and a final bar labelled 16:00. The 10:00 bar covers only 09:15–10:00 (45 minutes), and the last bar covers only 15:00–15:30 (30 minutes).

The first ~250 hourly bars (about Jan–Feb 2022) are dropped as warm-up. Indicators like the 200-period EMA need history before they mean anything. That's why every training window starts on 2022-02-22.

Why check stops and targets on 1-minute bars? An hourly candle can show that both your stop and your target were touched, but it can't say which came first. So the simulator walks through the 60 one-minute candles inside the hour, in order. If a single 1-minute candle touches both the stop and the target, the simulator assumes the stop was hit first. This rule is deliberately pessimistic (conservative), so it cannot flatter the results.

What is NOT in the data. The agent sees only Nifty's own price history. Specifically, there is:

Nothing was invented to fill these gaps. The index "volume" column isn't used by any feature.


3. The features: what the agent "sees"

A feature is one number the agent looks at when deciding. All features are causal: each uses only data up to and including the current bar's close, never anything later.

Most are ATR-normalised, meaning divided by the Average True Range (ATR, the typical bar range over the last 14 bars). "Close is 2 ATRs above the 20 EMA" then means the same thing at Nifty 17,000 and at Nifty 26,000, which lets one model work across years.

23 price/volatility features (from repo/features.py):

Group Features Plain meaning
Trend distance close_ema20_atr, close_ema50_atr, close_ema200_atr, ema20_ema50_atr How far price is from its 20/50/200-hour EMAs, and how far the 20 EMA is from the 50 EMA, in ATRs
Momentum macd_hist_atr, roc20_atr MACD histogram, and price change over 20 bars, in ATRs
Volatility atr_close, atr_fast_slow, bb_width_close ATR as a % of price; whether short-term ATR is above long-term ATR (vol expanding or contracting); Bollinger-band width as a % of price
Candle shape range_atr, upper_wick_ratio, lower_wick_ratio This bar's range in ATRs, and how much of it is upper or lower wick
Recent moves ret1_atr … ret5_atr Price change over the last 1, 2, 3, 4 and 5 bars, in ATRs
Calendar tod_sin, tod_cos, dow_sin, dow_cos Time of day and day of week, encoded as smooth circles so that 15:00 sits "next to" 10:00 the next day
Session (morning/afternoon) session_asia, session_london Inherited from the forex original. On NSE hours they effectively split the day at about 12:30 IST.

What was removed:

4 regime flags (from regime_detection.py), each 0 or 1:

That makes 27 market features. Missing values (NaN) are filled with 0.

The simulator adds 6 "my position" numbers:

So each decision uses 33 numbers. Before they reach the network, they are rescaled to roughly mean 0 and spread 1, then clipped at ±10. A wrapper called VecNormalize does this using running averages from the training data, often just called the "vecnorm" stats.


4. How the agent decides and trades

4.1 The choices (the "action space")

Every hour, the agent outputs three choices at once. Technically, this is a Gymnasium MultiDiscrete([3, 3, 4]) action.

Choice Options Meaning
Direction 0 = flat/close, 1 = long, 2 = short What position to hold
Stop-loss width 1.0, 1.5 or 2.0 × ATR How far away the stop sits
Target 1, 1.5, 2 or 3 × the stop distance ("R-multiple") Reward-to-risk of the bracket

R means "one unit of risk": the amount you lose if the stop is hit. A 2R target pays twice what the stop costs.

Position sizing. Each trade risks 0.5% of current equity. Starting equity is 10,000, an abstract account unit rather than literally rupees, so the first trade risks 50. Position size is risk ÷ stop distance: a wider stop means a smaller position. The simulator allows fractional sizes rather than whole Nifty lots.

4.2 A worked example: one hour

The prices are illustrative, to show the mechanics. They are not from a real trade log.

  1. 11:00 IST bar closes. Nifty closes at 25,000 and the hourly ATR(14) is 50 points. Equity is 10,000 and the agent is flat. The 33 numbers above are computed from data up to 11:00, and nothing later.
  2. The agent picks [long, SL 1.0×ATR, TP 2R].
  3. Entry. The simulator buys at the close plus half the spread plus slippage, for example 25,000 + 0.25 + 0.5 = 25,000.75.
    • Stop distance = 1.0 × 50 = 50 points, so SL = 24,950.75.
    • Target = 2 × 50 = 100 points, so TP = 25,100.75.
    • Risk = 0.5% × 10,000 = 50, so size = 50 ÷ 50 = 1.0 unit.
  4. 11:01 to 12:00. The simulator checks each 1-minute candle in order:
    • If a candle's low reaches the SL, the trade closes at the stop: −1R before costs.
    • If a candle's high reaches the TP, the trade closes at the target: +2R before costs.
    • If one candle touches both, it counts as a stop (the pessimistic rule).
  5. Costs. Slippage and spread worsen the fills. Charges such as STT, exchange fees, stamp duty and GST are deducted using the rates in force on that trade's date (§5).
  6. Reward for the hour = change in equity ÷ the 0.5% risk unit, which is roughly "R gained or lost". While a trade is open, there are two tiny extra terms:
    • plus 1% of the open P&L in R, marked at the bar close the agent already saw
    • minus 0.00002 for holding
  7. 12:00 bar closes. If the trade is still open, the agent decides again:
    • Same direction: the bracket stays.
    • Flat: the trade closes at the 12:00 close.
    • Opposite direction: the trade closes and reverses.

4.3 What "one step" and "2 million steps" mean

One step is one hourly decision. During training, the agent plays episodes of 2,048 hours (roughly 1.2 years), each starting from a random point in the training data. v2 runs 8 of these simulators in parallel.

The full run's training window has 4,877 hourly bars. So 2,000,000 steps is about 410 passes over the same history, which is a lot. That's why overfitting is the main risk (§9).


5. Trading costs: date-aware model, Zerodha/Upstox style

broker_costs.py now charges each trade at the rates in force on its date, taken from NSE circulars. These are still assumptions for a typical discount broker, not exact broker bills.

Rates that change by date

Item Period Value Source
STT on futures sells up to 2024-09-30 0.0125%
2024-10-01 to 2026-03-31 0.02% NSE/FATAX/63809
from 2026-04-01 0.05% NSE/FATAX/73524 (Finance Act 2026)
Nifty lot size up to 2024-04-25 50
2024-04-26 to 2025-01-01 25 NSE/FAOP/61415
2025-01-02 to 2025-12-30 75 NSE/FAOP/64625
from 2025-12-31 65 NSE/FAOP/70616

Rates held constant

Item Value Note
Exchange transaction charge 0.00183% of turnover ⚠ The historical path of this rate is unconfirmed. The current rate is applied to all dates.
SEBI fee 0.0001% of turnover
Stamp duty 0.002% on the buy side (Maharashtra)
GST 18% on brokerage + exchange + SEBI
Brokerage min(₹20, 0.03%) per order
Slippage 0.5 point per side

Worked examples. Each is 1 lot and a +100-point round trip, including slippage:

Trade date Entry price Lot STT rate Total cost In Nifty points
2023-06-15 19,500 50 0.0125% ₹283.72 5.67 pts
2025-02-15 23,500 75 0.02% ₹591.90 7.89 pts
2026-06-15 25,000 65 0.05% ₹1,034.61 15.92 pts

The cost of a round trip has nearly tripled in points over the data period, mostly because of STT. Much of the sealed test window (2025-07-10 to 2026-10-01) is in the most expensive regime. That makes the test harder, and more realistic, than before.

For comparison:

Note: I haven't re-read the v2 simulator code line by line. This section describes the cost schedule and the worked examples from broker_costs.py, not every detail of how the simulator applies them.


6. How the training was run

6.1 Words used in this section

6.2 Stage 1: hyperparameter search (DONE)

Fold Train Validation Fold's own test
1 2022-02-22 to 2023-07-03 2023-07-12 to 2023-12-15 sealed, not used
2 2024-06-14 to 2025-10-20 2025-10-31 to 2026-04-08 sealed, not used

How the winner was chosen (hyperparam_search.py, get_best_config):

  1. Score each config by its average validation excess return vs buy-and-hold across the two folds, in percentage points (pp). Excess return is the agent's return minus what simply holding Nifty returned over the same window.
  2. Pick the highest average.

There is no extra eligibility filter at this stage. Drawdown and trade count are recorded but don't affect the ranking.

Winner: large_batch. Learning rate 0.0003, batch 256, network [256, 256], rollout 2,048 steps. It averaged +6.39 pp of excess, against +5.80 pp for second-placed shallow_net. The full table is in §8.2.

The search was run with the earlier code. Costs were a flat ~5.9 points, and each run's validation score came from its last-step 50k model. It was not re-run for v2. v2 reuses its winner (--use_best_hyperparams).

6.3 Checkpoint choice inside every run (the "consistency score")

During each run, the code regularly pauses to score the agent on two slices:

For each slice it computes quality = cumulative reward − 1.0 × max drawdown %. The checkpoint's score is the weaker of the two (min(q_train, q_val)).

A checkpoint can become "best" only if it is eligible: positive reward on both slices and at least 5 trades on each. This stops a do-nothing agent (zero trades, zero drawdown) from winning. The best eligible checkpoint is saved to best_model/ with its own vecnorm stats.

v2 change: the sealed test (and each walk-forward fold's test) now scores this best-validation checkpoint with its matching vecnorm. The model from the very last training step is evaluated separately, so the gap between the two shows how much overfitting happened late in training.

6.4 Stage 2: full training, 2,000,000 steps (v2, DONE)

Item Value
Settings large_batch: lr 0.0003 constant, batch 256, net [256, 256]. Also: entropy bonus 0.03, 5 epochs per update, clip 0.1, target-KL 0.025, weight decay 1e-5, gamma 0.99.
Hardware NVIDIA RTX 5090 Laptop GPU (24 GB), CUDA
Parallelism 8 environments (n_envs=8, SubprocVecEnv)
Speed ~1,900–2,200 steps/second, up from ~323 with one environment (about 6×)
Train 2022-02-22 to 2024-12-19 (4,877 bars)
Validation 2025-01-10 to 2025-06-19 (761 bars)
Sealed test 2025-07-10 to 2026-10-01 (2,129 bars). Same window as the 80k baseline, so the two can be compared.
Scored model Best-validation checkpoint + its vecnorm. The last-step model is reported separately.
Results Best-val checkpoint −19.11% vs buy-and-hold −11.69% (−7.43 pp). Last-step model −22.77%. Details in §8.3 and on the v2 results page.

What overfitting looked like in the earlier run. These are checkpoint scores from the stopped old-cost run. This is not a result; it is shown only to explain the pattern. Values are cumulative reward in R units: 1 R ≈ 0.5% of equity, so +20 R is very roughly +10%.

Steps Train-slice reward Val reward Score Eligible?
100k +3.2 −7.7 −13.5 no
200k +11.6 +22.0 +6.2 yes ← best
400k +30.0 +10.3 +4.5 yes
800k +47.3 −8.2 −17.1 no
1.2M +90.9 −27.8 −42.4 no

The training score kept climbing while validation fell. That's textbook overfitting: the agent memorises 2022–2024 instead of learning something that carries over to 2025. It's exactly why v2 now tests the best-validation checkpoint rather than the last step.

6.5 Stage 3: walk-forward test (v2, DONE)

Walk-forward means repeating train → validate → test on several stretches of history instead of just one. It shows whether results hold up across different market conditions.

6.6 Timeline (all times IST)

When (IST) What
4 Oct 80k-step CPU baseline delivered (rough 3.5-point cost model). Its page is at /rl-trading/.
5 Oct ~00:10 Project staged on the laptop; quick 4k-step smoke test
5 Oct ~00:17 – ~08:26 Hyperparameter search: 16 runs × 50k steps, earlier flat cost model
5 Oct ~08:26 First 2M attempt starts; dies at ~215k steps when the laptop goes to sleep
5 Oct 09:29 2M run restarted (old costs, one environment, ~323 steps/s)
5 Oct, before 10:49 That run is stopped at about 1.46M steps. Its partial model is kept as models/full_training_2000k_partial_oldcosts. It is not a result.
5 Oct 10:49 v2 run starts: date-aware costs, 27 features, 8 parallel environments, best-val checkpoint scoring. Full 2M with large_batch, then walk-forward 4 × 200k with --use_best_hyperparams.
5 Oct 11:07 v2 full 2M training finishes; sealed-test result written to results/full_training_2000k_results.json.
5 Oct 11:16 v2 walk-forward finishes (results/walk_forward_results.json) and the job prints ALL_DONE. Results published at v2/.

6.7 GPU use and parallel environments

PPO with a small MLP doesn't need much GPU. Each cycle has two phases:

  1. Collect experience. The simulator steps through hours and scans 1-minute candles on the CPU.
  2. Update the network. The GPU does a few quick passes over the collected samples, in a short burst.

With one simulator, the GPU sat idle most of the time (~323 steps/s). v2 runs 8 simulators in parallel (SubprocVecEnv, one process each). This lifted speed to 1,900–2,200 steps/s (about 6×), so a 2M-step run now takes roughly 15–20 minutes of training instead of about 1¾ hours. The GPU is still not the bottleneck. That's expected for this kind of model.


7. Anti-leakage design: how the project avoids cheating

Leakage, or look-ahead bias, is when a backtest accidentally uses information that wouldn't have been available at the time. It's the most common reason backtests look great and live trading doesn't.

What the project does about it:

  1. Causal features. Every indicator uses only past and current bars: EMAs, rolling windows, and shift(k) for past returns. repo/leakage_checks.py has a guard that recomputes features after appending future rows and fails if any past value changes.
  2. Decide at the bar close, execute afterwards. The agent decides at the hourly close it has already seen, and the trade plays out on the following 1-minute candles. The open-P&L part of the reward uses only that known close.
  3. Pessimistic intrabar rule. If the stop and target are touched in the same minute, the stop counts first.
  4. Chronological splits with embargoes. Train comes before validation, which comes before test, with 50-bar gaps between them. Nothing is shuffled across time.
  5. Sealed test, one model chosen without looking at it. The test window is evaluated once, at the end. The model scored on it is chosen by validation only (the best-val checkpoint), and the choice is fixed before the test is opened. The search folds' own test windows were never touched.
  6. Normalisation travels with the model. Each checkpoint is saved with its own vecnorm stats, frozen during evaluation, so validation and test data never influence them.

Still open: the search overlaps the test window. The hyperparameter search's fold 2 used 2024-06 to 2025-10 for training and 2025-10-31 to 2026-04-08 for validation. Both overlap the full run's sealed test window. No agent traded the test window while the final model was being trained. But the choice of large_batch was partly informed by how configs did in months that fall inside that test window. That's mild selection leakage. The walk-forward, and later live paper-trading, are the cleaner checks.


8. Results so far

8.1 Baseline: 80k steps on CPU (delivered 4 Oct)

Agent Nifty buy-and-hold
Test window 2025-07-10 to 2026-10-01 same
Total return −6.40% −11.69%
Max drawdown −13.27% −15.72%
Trades 554 1
Win rate 42.78% n/a
Profit factor 0.95 n/a
Sharpe-like ratio −0.41 n/a
Final equity (from 10,000) 9,360 8,831
Excess vs buy-and-hold +5.29 pp

What the measures mean:

Reading it. The agent lost less than the market in a falling year, but it still lost money. With PF 0.95 over 554 trades, the 42.8% win rate didn't cover costs.

This run used the old rough 3.5-point cost model. Under v2's date-aware costs (about 8–16 points per trade in the test window), the same trading would have done worse. So the v2 numbers will be compared against a tougher bar.

8.2 Hyperparameter search results (DONE, earlier code)

Source: results/hyperparam_search_results.json. These are validation numbers only; the test windows stayed sealed. Configs are sorted by score.

Rank Config LR Batch Network Fold 1 val return % Fold 1 excess vs B&H (pp) Fold 2 val return % Fold 2 excess vs B&H (pp) Avg val return % Avg max DD % Avg Sharpe-like Avg trades Score: avg excess (pp)
1 large_batch (winner) 3e-4 256 [256, 256] +11.11 +0.77 +4.98 +12.02 +8.05 -4.01 +1.78 105.5 +6.39
2 shallow_net 3e-4 128 [128, 128] +4.63 -5.72 +10.28 +17.31 +7.45 -3.38 +1.77 83.5 +5.80
3 wide_net 3e-4 128 [512, 512] +5.63 -4.71 -0.34 +6.70 +2.64 -5.48 +0.69 89.0 +0.99
4 baseline 3e-4 128 [256, 256] -1.06 -11.40 +3.75 +10.78 +1.34 -3.83 +0.42 87.0 -0.31
5 small_batch 3e-4 64 [256, 256] -2.82 -13.17 +2.55 +9.59 -0.14 -5.20 -0.05 89.0 -1.79
6 high_lr 1e-3 128 [256, 256] -4.47 -14.82 +2.52 +9.56 -0.98 -7.16 -0.40 120.0 -2.63
7 low_lr 1e-4 128 [256, 256] -6.48 -16.82 +2.73 +9.77 -1.87 -6.43 -0.40 89.5 -3.52
8 deep_net 3e-4 128 [256, 256, 128] -5.70 -16.04 -0.75 +6.29 -3.22 -5.90 -0.87 82.0 -4.88

Buy-and-hold over the validation windows returned +10.34% in fold 1 (a rising market) and −7.04% in fold 2 (a falling market). Each run was 50,000 steps, seed 42, with the earlier flat cost model and 29 features.

What to take from it:

8.3 The v2 full run and walk-forward

Sealed test, 2025-07-10 to 2026-10-01 (2,129 hourly bars), date-aware costs:

Model Return Max DD Trades Win rate Profit factor Sharpe Excess vs B&H
Best-val checkpoint (official) −19.11% −21.39% 567 41.45% 0.80 −1.82 −7.43 pp
Last-step model (2M steps) −22.77% −24.58% 952 40.76% 0.87 −1.40 −11.08 pp
Buy-and-hold Nifty −11.69% −15.72%

Walk-forward, 4 folds × 200k steps, large_batch, each fold's best-val checkpoint:

Fold Test window Val return Test return Test max DD Trades B&H return Excess (pp)
1 2023-01-23 to 2023-04-20 +2.39% +1.12% −3.69% 85 −2.73% +3.85
2 2024-03-19 to 2024-06-14 +4.77% −0.43% −3.11% 93 +7.13% −7.56
3 2025-05-19 to 2025-08-06 +6.91% −4.04% −5.12% 81 −1.73% −2.32
4 2026-07-13 to 2026-10-01 +4.55% −4.27% −6.59% 70 −7.39% +3.12

Against the §9 checklist:

  1. Positive sealed-test return: no (−19.11%).
  2. Consistent across folds: no. Only fold 1 made money, and only folds 1 and 4 beat buy-and-hold. Every fold looked good on validation (+2.4% to +6.9%) but fell back on test, which is the same overfitting pattern seen in training.
  3. Profit factor above 1.1: no (0.80).
  4. Drawdown no worse than buy-and-hold: no (−21.39% vs −15.72%).
  5. Enough trades: yes (567).
  6. Robustness across seeds: not tested (one seed).

The best-val checkpoint did better than the last-step model (−19.11% vs −22.77%), so picking by validation helped a bit, but it didn't turn the result positive. The 80k baseline's −6.40% used a rough flat 3.5-point cost and isn't directly comparable; the v2 costs are higher (about 6 to 14 points per round trip depending on date).

9. How to read the final numbers when they arrive

Why train, validation and test are kept separate.

Best-val checkpoint vs last-step model. v2 reports both.

If the last-step model is much worse on the test, the run overfit late, as the earlier logs suggested it would. If the two are similar, training was more stable than feared.

Overfitting is when a model fits noise in the training years rather than repeatable behaviour. The warning sign is a growing gap between train and validation: train returns climbing while validation stays flat or falls.

Why buy-and-hold is the benchmark. If an agent can't beat simply holding Nifty, there's no reason to run it. But in a falling year, "beating" buy-and-hold can just mean staying flat a lot. So look at both the absolute return (did it make money?) and the excess.

What would count as a real edge. All of these together, after the date-aware costs:

  1. A positive sealed-test return, not just "lost less than Nifty".
  2. Consistency across walk-forward folds. The agent should be positive (or at least beat buy-and-hold) in most folds, including up, down and sideways markets. One great fold and three bad ones is not an edge.
  3. Profit factor > 1.1 on test. A PF between 1.0 and 1.1 is too thin to survive real-world execution.
  4. Acceptable drawdown: for example, no worse than buy-and-hold over the same window, and tolerable for a system risking 0.5% per trade.
  5. Enough trades (hundreds) for the win rate and PF to mean something statistically.
  6. Robustness: similar results across a few different random seeds.

Nothing is proven. Both the 80k baseline and the v2 2M run lost money on the sealed test, and v2 lost more than buy-and-hold.


10. Limitations and missing data


11. Open issues

What's left from the code review after the v2 fixes:

  1. The search overlaps the sealed test window. Search fold 2 (training 2024-06 to 2025-10, validation 2025-10-31 to 2026-04-08) overlaps the full run's test window (2025-07-10 to 2026-10-01). See §7.
  2. The GitHub PR code differs from the laptop code. The PR branch is cursor/nifty-rl-trading-results-b2f0, under aiprojects/rl-trading-nifty/ (PRs 13 and 14). It doesn't match the v2 code actually running on the laptop. The PR should be updated so the repo reflects what produced the results.

Fixed in v2:


12. What's done and next steps

Done:

Suggested next steps (for Satya to decide):

  1. Read the results using the checklist in §9. Compare the best-val checkpoint with the last-step model, and look at every walk-forward fold, not just the average.
  2. Fight overfitting if the gap is large:
    • fewer steps or early stopping
    • a smaller network or stronger regularisation
    • several random seeds, averaged
  3. Re-run the hyperparameter search under v2: date-aware costs, 27 features, best-val scoring. Use folds that don't touch the sealed test window, to close the selection-leakage gap. With 8 environments, the search is now cheap.
  4. Better inputs, probably the biggest lever: OI, India VIX, futures basis, breadth and options-derived signals, all strictly causal.
  5. Fairer walk-forward: anchored or expanding training windows, and more folds.
  6. Sync the GitHub PR with the laptop v2 code.
  7. Only after a positive, consistent, cost-robust result: paper-trade live for a few weeks before any real money.

Appendix: file map (laptop, /home/satya/rl-trading-nifty/)

File What it does
run_extended_training.py Driver: --mode full, walk_forward or hyperparam_search; Nifty config
broker_costs.py Date-aware Indian F&O cost model (STT and lot-size schedules from NSE circulars)
nifty_data.py Loads the Upstox parquet, converts to IST, relabels bars by close time
regime_detection.py Trend/range and high/low-vol flags
hyperparam_search.py The 8 configs and the "best average excess" rule
walk_forward.py Cuts history into consecutive train/val/test blocks
repo/features.py The 23 causal price/volatility features
repo/env_bracket.py The trading simulator: actions, brackets, 1-minute TP/SL check, reward
repo/train_ppo.py PPO training, consistency checkpointing, evaluation rollouts
repo/leakage_checks.py Look-ahead guard for features
results/hyperparam_search_results.json Search results (table in §8.2)
results/full_training_2000k_results.json v2 sealed-test results, best-val checkpoint and last-step model (§8.3)
results/walk_forward_results.json v2 walk-forward fold results (§8.3)
models/full_training_2000k_partial_oldcosts/ Stopped old-cost run (~1.46M steps). Not a result.
models/*/run_info.json, models/*/eval_logs/consistency_evals.csv Per-run metadata and checkpoint scores

Self-contained page (no external assets). Same content as EXPLAINER.md. Version 2 (final), updated 5 Oct 2026 after the v2 run finished at 11:16 IST. v2 results page · 80k baseline results page.