Skip to content

feat: paper trading, backtesting module, and risk management overlays#4

Merged
sreerevanth merged 4 commits into
sreerevanth:mainfrom
samalbishnupriya06-stack:feature/paper-trading-backtesting-risk
Jun 7, 2026
Merged

feat: paper trading, backtesting module, and risk management overlays#4
sreerevanth merged 4 commits into
sreerevanth:mainfrom
samalbishnupriya06-stack:feature/paper-trading-backtesting-risk

Conversation

@samalbishnupriya06-stack

@samalbishnupriya06-stack samalbishnupriya06-stack commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds three major features to Tradex: Paper Trading mode, Historical Backtesting,
and Risk Management chart overlays — transforming it from a signal generator into
a complete trading environment.


Changes

New Files

  • paper_trader.py — Paper Trading Engine: logs simulated BUY/SELL entries to
    data/paper_trades.json, tracks hypothetical P&L without real capital
  • backtester.py — Backtesting Module: fetches historical intraday data via
    yfinance, replays ORB strategy day-by-day, generates a performance report

Modified Files

  • app.py
    • Live / Paper mode toggle in sidebar
    • Stop Loss % and Take Profit % percentage inputs in sidebar
    • Backtesting UI tab (select ticker + date range → run → view report)
    • SL/TP horizontal lines drawn on all candlestick charts
    • Paper trades table displayed separately from live trades
  • trader.py
    • stop_loss_pct and take_profit_pct params passed through signal logic

Features in Detail

Paper Trading

  • Toggle between LIVE and PAPER mode in the sidebar
  • In Paper mode, signals are logged to data/paper_trades.json instead of
    executing real trades
  • Simulated P&L tracked per trade (entry → target or stop hit)

Backtesting

  • Select any NSE/NYSE ticker + date range
  • Fetches intraday historical data (1m intervals via yfinance)
  • Simulates ORB signals for each trading day
  • Report includes: Win Rate, Total Return %, Max Drawdown, Trade Log table

Risk Management Overlays

  • SL and TP levels shown as horizontal lines on candlestick charts
  • Color-coded: red for stop loss, green for take profit
  • Configurable percentages persist per session

Testing Done

  • Paper mode toggle switches correctly, no live trade calls made
  • Paper trades persist across reruns (read from JSON)
  • Backtester runs on RELIANCE.NS and AAPL for a 5-day window
  • SL/TP lines appear correctly on charts
  • No breakage to existing Live mode or signal generation


Closes #1

Summary by CodeRabbit

  • New Features
    • Paper Trading mode with sidebar selector and "Clear Paper Trades" action
    • Configurable Stop Loss % and Take Profit % sliders for simulated trades
    • Paper Trading dashboard: mode-labeled timestamp, performance summary, and trade history table (with empty-state)
    • Charts display SL/TP lines and annotations for active BUY/SELL entries
    • Historical Backtester tab: run backtests, view summary metrics, trade log, and equity-curve chart
    • Two-tab layout: Dashboard and Backtester

- paper_trader.py: logs simulated trades to data/paper_trades.json,
  tracks hypothetical P&L, win rate, auto-closes on new signal
- backtester.py: replays ORB strategy on historical intraday data,
  computes win rate, total return, max drawdown, equity curve
- app.py: Live/Paper mode toggle in sidebar, SL/TP sliders,
  chart overlays for stop loss and take profit levels,
  backtester UI in a second tab with equity curve visualization
- Closes sreerevanth#1
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 523abacb-7c5c-4756-a26d-c4f0fb1d074b

📥 Commits

Reviewing files that changed from the base of the PR and between e56e24c and e3f729c.

📒 Files selected for processing (1)
  • app.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • app.py

📝 Walkthrough

Walkthrough

Adds file-backed paper trading (log, stats, clear), a daily ORB backtester returning trade logs and metrics, SL/TP risk inputs and chart annotations, PAPER-mode simulated trade logging and performance UI, and a Backtester tab with equity-curve visualization.

Changes

Paper Trading and Backtesting Platform

Layer / File(s) Summary
Paper Trading Persistence Module
paper_trader.py
New module with JSON-backed persistence: log_paper_trade() (auto-closes prior OPEN for same symbol), get_paper_trades(), get_paper_stats(), and clear_paper_trades() persisted to data/paper_trades.json.
Backtesting Engine (daily)
backtester.py
New BacktestResult dataclass and run_backtest() that downloads daily OHLC via yfinance, replays an ORB-style opening-range breakout per day, simulates SL/TP exits (one trade per breakout/day), returns trade_log and aggregate metrics (win rate, total P&L, total P&L%, max drawdown).
Risk Management Inputs & Chart Enhancements
app.py (imports, sidebar, _price_figure signature, SL/TP rendering)
Adds Trading Mode selector (LIVE/PAPER), SL/TP percentage sliders, updates _price_figure(..., sl_pct, tp_pct) and draws annotated SL/TP horizontal lines when a BUY/SELL signal has an entry price; passes SL/TP values into chart rendering.
PAPER Mode Dashboard & Visualization
app.py (main flow, paper rendering)
When PAPER mode is active, dashboard logs simulated trades via log_paper_trade(), shows mode-labeled timestamp, renders _render_paper_performance() metrics, and displays a paper trades table or an informational empty-state.
Backtester UI and Multi-Tab Navigation
app.py (_render_backtest_tab, _app_with_tabs)
Adds _render_backtest_tab() Streamlit UI that validates dates, runs run_backtest(), renders backtest metrics, trade log table, and equity curve; introduces _app_with_tabs() to host the Dashboard and Backtester tabs.

Sequence Diagram

sequenceDiagram
  participant User
  participant Dashboard
  participant PaperTrader
  participant JSONStorage
  participant Backtester
  participant yfinance

  User->>Dashboard: Select PAPER mode, set SL/TP %
  Note over Dashboard: Live signal detected
  Dashboard->>PaperTrader: log_paper_trade(signal, sl_pct, tp_pct)
  PaperTrader->>JSONStorage: write data/paper_trades.json (append/close)
  Dashboard->>PaperTrader: get_paper_stats()
  PaperTrader->>JSONStorage: read data/paper_trades.json
  PaperTrader->>Dashboard: return stats
  Dashboard->>User: Show paper stats, trades table, SL/TP on chart

  User->>Dashboard: Click Backtester tab, set ticker and date range
  Dashboard->>Backtester: run_backtest(ticker, dates, sl_pct, tp_pct)
  Backtester->>yfinance: download daily OHLC
  yfinance->>Backtester: return historical candles
  Backtester->>Backtester: replay ORB, simulate SL/TP exits, build trade_log
  Backtester->>Dashboard: return BacktestResult (metrics + trade_log)
  Dashboard->>User: Display backtest metrics, trade log, equity curve
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through charts and daily files,
I logged each trade with cautious smiles,
SL and TP lines I neatly drew,
Backtests traced the market through and through,
Paper trades hum — practice pays!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and concisely describes the three main features added: paper trading, backtesting module, and risk management overlays.
Linked Issues check ✅ Passed The PR successfully implements all core requirements from issue #1: paper trading with JSON persistence, backtesting module with performance metrics, and risk management overlays (SL/TP) on charts.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #1 objectives; no out-of-scope additions detected beyond the paper trading, backtesting, and risk management features requested.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@ecc-tools

ecc-tools Bot commented Jun 4, 2026

Copy link
Copy Markdown

Analyzing 200 commits...

@ecc-tools

ecc-tools Bot commented Jun 4, 2026

Copy link
Copy Markdown

Analysis Complete

Generated ECC bundle from 1 commits | Confidence: 50%

View Pull Request #5

Repository Profile
Attribute Value
Language Python
Framework Not detected
Commit Convention conventional
Test Directory separate
Changed Files (3)
Metric Value
Files changed 3
Additions 574
Deletions 9

Top hotspots

Path Status +/-
app.py modified +236 / -9
backtester.py added +223 / -0
paper_trader.py added +115 / -0

Top directories

Directory Files Total changes
. 3 583
Analysis Depth Readiness (commit-history, 7%)

ECC Tools uses this to decide whether recommendations should stay at commit-history/setup guidance or expand into CI, security, harness, reference-set, AI-routing, and team backlog work.

Area Status Evidence / Next Step
Commit history Partial 1 commits sampled
CI/CD signals Missing Add workflow files or CI troubleshooting evidence so ECC Tools can reason about pipeline setup.
Security evidence Missing Add AgentShield, audit, SARIF, SBOM, or security review evidence so recommendations can cover security posture.
Harness configuration Missing Add Claude, Codex, OpenCode, Zed, dmux, MCP, plugin, or cross-harness config evidence for harness-agnostic recommendations.
Reference/eval evidence Missing Add fixtures, golden traces, reference sets, or evaluator benchmarks so deeper recommendations have regression evidence.
AI routing and cost controls Missing Add model-routing, budget, usage, or cost-control files before relying on AI-heavy automation recommendations.
Team handoff and project tracking Missing Add roadmap, runbook, project, Linear, or follow-up tracking docs so generated work can land in a team queue.
Reference Set Readiness (0/7, 0%)
Area Status Evidence / Next Step
Deep analyzer corpus Missing Add analyzer fixture, golden, benchmark, or reference-set files that can catch analyzer regressions.
RAG/evaluator comparison Missing Add retrieval or evaluator reference-set comparison fixtures with expected ranking behavior.
PR salvage/review corpus Missing Add stale-PR, review-thread, reopen-flow, or salvage reference cases for queue cleanup automation.
Discussion triage corpus Missing Add public discussion triage fixtures, golden cases, or reference sets for informational, answered, and no-response classifications.
Harness compatibility Missing Add cross-harness, adapter-compliance, or harness-audit evidence for Claude, Codex, OpenCode, Zed, dmux, and agent surfaces.
Security evidence Missing Attach security evidence such as SBOMs, SARIF, audit reports, or AgentShield evidence packs.
CI failure-mode evidence Missing Add captured CI failure logs, dry-run fixtures, or troubleshooting docs for common workflow failure modes.
Likely Future Issues (1)
Severity Signal Why it may show up
HIGH Regression coverage may lag behind the diff 3 generic code paths changed; 0 test files changed
  • Regression coverage may lag behind the diff: The PR changes multiple code paths but does not touch any obvious test files.
Suggested Follow-up Work (1)
Type Suggested title Targets
PR test: add regression coverage for app.py + backtester.py app.py, backtester.py
  • test: add regression coverage for app.py + backtester.py: Backfill regression coverage before another change set lands on the touched code paths.

Copy-ready bodies

test: add regression coverage for app.py + backtester.py

## Summary
- Add regression coverage for the recently touched code paths before more changes stack on top.

## Why
- Backfill regression coverage before another change set lands on the touched code paths.

## Touched paths
- `app.py`
- `backtester.py`

## Validation
- Add or extend focused tests that exercise the touched paths.
- Run the affected test suite and verify the new coverage closes the gap.
Generated Instincts (14)
Domain Count
git 3
code-style 9
testing 2

After merging, import with:

/instinct-import .claude/homunculus/instincts/inherited/TRADEX-instincts.yaml

Files

  • .claude/ecc-tools.json
  • .claude/skills/TRADEX/SKILL.md
  • .agents/skills/TRADEX/SKILL.md
  • .agents/skills/TRADEX/agents/openai.yaml
  • .claude/identity.json
  • .codex/config.toml
  • .codex/AGENTS.md
  • .codex/agents/explorer.toml
  • .codex/agents/reviewer.toml
  • .codex/agents/docs-researcher.toml
  • .claude/homunculus/instincts/inherited/TRADEX-instincts.yaml

ECC Tools | Everything Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app.py (1)

105-106: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make sidebar SL/TP drive the same target/stop values used in generated signals (not just overlays/paper trades).

  • app.py sliders (sl_pct, tp_pct) are used for chart overlays (_price_figure(..., sl_pct=..., tp_pct=...)) and paper trades (log_paper_trade(..., sl_pct=..., tp_pct=...)).
  • The displayed signals (“SIGNALS” card + signal matrix) use TradeSignal.target / TradeSignal.stop_loss, which are hardcoded in strategy.calculate_orb_signal (_action_signal: BUY target=+1%, stop=-0.5%; SELL target=-1%, stop=+0.5%).
  • _analyze_cached is keyed only by symbols and trader.analyze_symbols() takes only symbols, so changing the sliders won’t regenerate signals—UI can diverge from the strategy risk implied by the overlays/paper-trade rows.

Plumb sl_pct/tp_pct through analyze_symbols -> calculate_orb_signal/_action_signal and include them in _analyze_cached’s cache key.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.py` around lines 105 - 106, The signals are generated with hardcoded
targets/stops while sliders (sl_pct, tp_pct) only affect overlays/paper trades;
to fix, add sl_pct and tp_pct parameters to _analyze_cached,
trader.analyze_symbols, strategy.calculate_orb_signal and its helper
_action_signal and thread them through so calculate_orb_signal uses the slider
values to set TradeSignal.target and TradeSignal.stop_loss; update the cache key
for _analyze_cached to include sl_pct and tp_pct and change the call site
(currently _analyze_cached(tuple(symbols))) to pass the current slider values so
UI signals, overlays and paper trades stay in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app.py`:
- Around line 113-117: The paper-trade logger currently calls log_paper_trade
for every cycle whenever trading_mode == "PAPER" and signal.signal is "BUY" or
"SELL", causing duplicate logs; fix by constructing a durable signal identity
(e.g. f"{signal.symbol}:{signal.signal}:{signal.timestamp}" or another unique
combination such as symbol+side+rounded-timestamp) and checking a persisted
store (in-memory cache with durable backing, DB table, or a file) for that id
before calling log_paper_trade; if the id is new, call log_paper_trade and
persist the id (update the store) so subsequent runs skip the same signal.
- Around line 220-221: The IndentationError at the st.markdown('<div
class="section-title">CHARTS</div>', unsafe_allow_html=True) line means the
statement's indentation doesn't match the surrounding block; fix it by aligning
this st.markdown call to the correct indentation level (remove stray leading
spaces/tabs or convert mixed tabs to spaces) so it matches the surrounding scope
(top-level or the enclosing function/block), and ensure there are no unclosed
blocks or multi-line strings before it that would change expected indentation.

In `@backtester.py`:
- Around line 194-208: The percent metrics are incorrectly normalized to the
first trade price (first_entry) — change the logic to normalize against an
explicit initial capital (e.g., initial_capital) derived from or passed into the
backtest and computed equity_curve instead: ensure equity_curve represents
per-period account equity based on initial_capital and trade P&L, compute
total_return_pct = (ending_equity - initial_capital) / initial_capital * 100 and
max_dd_pct = max_drawdown / initial_capital * 100 (use the existing max_dd calc
over equity_curve but divide by initial_capital), and keep total_return,
avg_trade_pnl, and trade_log computations intact but based on notional or
position sizing consistent with initial_capital.

In `@paper_trader.py`:
- Around line 46-66: The code currently loads, mutates and rewrites
PAPER_TRADES_FILE without synchronization and treats corrupted JSON as an empty
list; wrap all access to PAPER_TRADES_FILE (both reads in _load_trades() and
writes where existing.append(trade) and json.dump(...) are called) with a
process-wide file lock (e.g., filelock.FileLock or a module-level threading.Lock
used with a context manager) to serialize concurrent runs, and perform atomic
writes by writing JSON to a temporary file in the same directory and then
os.replace(temp_path, PAPER_TRADES_FILE) to atomically swap; also validate
parsed JSON in _load_trades() (raise/log and abort write or restore from a
backup rather than silently returning [] on truncation/corruption) so you don’t
silently drop trades.

---

Outside diff comments:
In `@app.py`:
- Around line 105-106: The signals are generated with hardcoded targets/stops
while sliders (sl_pct, tp_pct) only affect overlays/paper trades; to fix, add
sl_pct and tp_pct parameters to _analyze_cached, trader.analyze_symbols,
strategy.calculate_orb_signal and its helper _action_signal and thread them
through so calculate_orb_signal uses the slider values to set TradeSignal.target
and TradeSignal.stop_loss; update the cache key for _analyze_cached to include
sl_pct and tp_pct and change the call site (currently
_analyze_cached(tuple(symbols))) to pass the current slider values so UI
signals, overlays and paper trades stay in sync.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 77b26c55-5c93-4ccf-a751-3c98a6717359

📥 Commits

Reviewing files that changed from the base of the PR and between d9cdf40 and 661285f.

📒 Files selected for processing (3)
  • app.py
  • backtester.py
  • paper_trader.py

Comment thread app.py
Comment thread app.py Outdated
Comment thread backtester.py Outdated
Comment on lines +194 to +208
total_return = round(sum(t["pnl"] for t in trade_log), 2)
first_entry = trade_log[0]["entry"]
total_return_pct = round(total_return / first_entry * 100, 2) if first_entry else 0.0
avg_trade_pnl = round(total_return / len(trade_log), 2)

# Max drawdown from equity curve
peak = equity_curve[0]
max_dd = 0.0
for val in equity_curve:
if val > peak:
peak = val
dd = (peak - val)
if dd > max_dd:
max_dd = dd
max_dd_pct = round(max_dd / first_entry * 100, 2) if first_entry else 0.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Normalize return and drawdown against a real capital base.

Line 196 and Line 208 divide cumulative multi-trade P&L by first_entry, so the reported percentages are anchored to the first trade's price instead of the backtest equity. That makes total_return_pct and max_drawdown misleading across multiple days/tickers. Track an initial capital or per-trade notional and derive both metrics from that equity curve instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backtester.py` around lines 194 - 208, The percent metrics are incorrectly
normalized to the first trade price (first_entry) — change the logic to
normalize against an explicit initial capital (e.g., initial_capital) derived
from or passed into the backtest and computed equity_curve instead: ensure
equity_curve represents per-period account equity based on initial_capital and
trade P&L, compute total_return_pct = (ending_equity - initial_capital) /
initial_capital * 100 and max_dd_pct = max_drawdown / initial_capital * 100 (use
the existing max_dd calc over equity_curve but divide by initial_capital), and
keep total_return, avg_trade_pnl, and trade_log computations intact but based on
notional or position sizing consistent with initial_capital.

Comment thread paper_trader.py Outdated
Comment on lines +46 to +66
existing = _load_trades()

# Auto-close any existing OPEN trade for same symbol
for t in existing:
if t["symbol"] == signal.symbol and t["status"] == "OPEN":
current_price = signal.last_price or entry
t["status"] = "CLOSED"
t["exit_price"] = current_price
t["exit_time"] = datetime.now().isoformat(timespec="seconds")
t["exit_reason"] = "New signal"
if t["side"] == "BUY":
t["pnl"] = round(current_price - t["entry_price"], 2)
t["pnl_pct"] = round((current_price - t["entry_price"]) / t["entry_price"] * 100, 2)
else:
t["pnl"] = round(t["entry_price"] - current_price, 2)
t["pnl_pct"] = round((t["entry_price"] - current_price) / t["entry_price"] * 100, 2)

existing.append(trade)

with PAPER_TRADES_FILE.open("w", encoding="utf-8") as f:
json.dump(existing, f, indent=2)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Guard paper-trade persistence with locking and atomic writes.

Line 46 does a full load/mutate/rewrite with no synchronization, and Line 114 treats any truncated/corrupted JSON as []. Two Streamlit reruns or users hitting this path together can drop trades or wipe the history entirely. Use a lock around all read/write paths and write to a temp file before replacing the real JSON file.

Also applies to: 102-115

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@paper_trader.py` around lines 46 - 66, The code currently loads, mutates and
rewrites PAPER_TRADES_FILE without synchronization and treats corrupted JSON as
an empty list; wrap all access to PAPER_TRADES_FILE (both reads in
_load_trades() and writes where existing.append(trade) and json.dump(...) are
called) with a process-wide file lock (e.g., filelock.FileLock or a module-level
threading.Lock used with a context manager) to serialize concurrent runs, and
perform atomic writes by writing JSON to a temporary file in the same directory
and then os.replace(temp_path, PAPER_TRADES_FILE) to atomically swap; also
validate parsed JSON in _load_trades() (raise/log and abort write or restore
from a backup rather than silently returning [] on truncation/corruption) so you
don’t silently drop trades.

@sreerevanth sreerevanth left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution! The feature set looks great and covers the requested enhancement well. Before merging, please address the paper-trading logging issue. Currently every Streamlit rerun logs a new trade and closes the previous OPEN trade for the same symbol, which inflates trade count and distorts P&L statistics. Also please verify the CHARTS section indentation since CodeRabbit flagged a possible parse error. Once those are fixed I'll be happy to review again.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
paper_trader.py (1)

11-15: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing error handling for corrupted JSON file.

If paper_trades.json exists but contains invalid JSON (e.g., due to a previous partial write or manual edit), json.load() will raise JSONDecodeError and crash the application. Add exception handling to either recover gracefully or surface the error explicitly.

🛡️ Proposed fix
 def _load_trades() -> list[dict]:
     if not os.path.exists(TRADES_FILE):
         return []
-    with open(TRADES_FILE, "r") as f:
-        return json.load(f)
+    try:
+        with open(TRADES_FILE, "r") as f:
+            return json.load(f)
+    except (json.JSONDecodeError, ValueError):
+        # Log or notify user of corrupted file
+        return []
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@paper_trader.py` around lines 11 - 15, The _load_trades function currently
calls json.load(TRADES_FILE) without handling corrupt JSON; wrap the file
read/json.load in a try/except that catches json.JSONDecodeError (and optionally
OSError) and handle it: either log the error with context (including
TRADES_FILE) and return an empty list to recover, or re-raise a clearer
exception (e.g., ValueError with filename and original error) so callers can
surface it; update the code around _load_trades to implement this error handling
and reference TRADES_FILE and _load_trades when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backtester.py`:
- Around line 81-94: The current intraday exit logic (variables side, day_low,
day_high, sl_price, tp_price, day_close, exit_price, exit_reason) always treats
STOP_LOSS as occurring before TAKE_PROFIT when both levels are within the day's
range, creating pessimistic bias; change the tie-breaking behavior when both
sl_price and tp_price are reachable on the same candle by implementing one clear
policy: either (A) randomize selection between SL and TP, (B) pick the level
closer to the open price (use open_price to compute distance), or (C) explicitly
document and expose the current assumption to users; update the branch in the
BUY and SELL blocks to detect the "both-hit" condition (day_low <= sl_price AND
day_high >= tp_price for BUY, the analogous condition for SELL) and apply your
chosen tie-breaker consistently, setting exit_price and exit_reason accordingly
and adding a comment or config flag to record the policy.

---

Duplicate comments:
In `@paper_trader.py`:
- Around line 11-15: The _load_trades function currently calls
json.load(TRADES_FILE) without handling corrupt JSON; wrap the file
read/json.load in a try/except that catches json.JSONDecodeError (and optionally
OSError) and handle it: either log the error with context (including
TRADES_FILE) and return an empty list to recover, or re-raise a clearer
exception (e.g., ValueError with filename and original error) so callers can
surface it; update the code around _load_trades to implement this error handling
and reference TRADES_FILE and _load_trades when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f97e9502-e1ba-47f3-bcdc-03f30cc3491b

📥 Commits

Reviewing files that changed from the base of the PR and between 661285f and 8075086.

📒 Files selected for processing (2)
  • backtester.py
  • paper_trader.py

Comment thread backtester.py
Comment on lines +81 to +94
if side == "BUY":
if day_low <= sl_price:
exit_price, exit_reason = sl_price, "STOP_LOSS"
elif day_high >= tp_price:
exit_price, exit_reason = tp_price, "TAKE_PROFIT"
else:
exit_price, exit_reason = day_close, "EOD"
else:
if day_high >= sl_price:
exit_price, exit_reason = sl_price, "STOP_LOSS"
elif day_low <= tp_price:
exit_price, exit_reason = tp_price, "TAKE_PROFIT"
else:
exit_price, exit_reason = day_close, "EOD"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Intraday exit order creates pessimistic bias in backtest results.

On volatile days where both SL and TP levels could have been breached within the same candle, the code always assumes stop-loss was hit first (lines 82-83, 89-90). This introduces a systematic pessimistic bias that can significantly understate win rate and total returns.

With daily OHLC data, it's impossible to determine which level was hit first. Consider either:

  1. Using a random choice when both could have triggered
  2. Using the level closer to the open price
  3. Documenting this assumption clearly for users
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backtester.py` around lines 81 - 94, The current intraday exit logic
(variables side, day_low, day_high, sl_price, tp_price, day_close, exit_price,
exit_reason) always treats STOP_LOSS as occurring before TAKE_PROFIT when both
levels are within the day's range, creating pessimistic bias; change the
tie-breaking behavior when both sl_price and tp_price are reachable on the same
candle by implementing one clear policy: either (A) randomize selection between
SL and TP, (B) pick the level closer to the open price (use open_price to
compute distance), or (C) explicitly document and expose the current assumption
to users; update the branch in the BUY and SELL blocks to detect the "both-hit"
condition (day_low <= sl_price AND day_high >= tp_price for BUY, the analogous
condition for SELL) and apply your chosen tie-breaker consistently, setting
exit_price and exit_reason accordingly and adding a comment or config flag to
record the policy.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app.py`:
- Around line 115-130: When clear_paper_trades() wipes the JSON paper-trade
store it doesn't reset the in-memory dedupe cache paper_logged_signals, so
previously-seen BUY/SELL keys remain marked and new trades aren't recorded;
update clear_paper_trades() to also remove or reset
st.session_state["paper_logged_signals"] (e.g., delete the key or set it to an
empty set) so the session dedupe cache is cleared whenever the paper history is
cleared, and ensure any code paths that clear paper history call that same
reset.
- Around line 114-130: There is a duplicated/pasted block causing an
IndentationError after the if trading_mode == "PAPER": check; remove the
duplicated second block and properly indent the remaining block so the for-loop
and subsequent st.session_state assignment are inside the if. Specifically, keep
a single block that checks trading_mode, reads/updates
st.session_state["paper_logged_signals"], iterates signals, computes signal_key,
calls log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct) for new BUY/SELL
keys, adds keys to logged, and finally writes logged back to st.session_state;
ensure function/variable names referenced are trading_mode, signals,
log_paper_trade, sl_pct, tp_pct, and paper_logged_signals.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e814ee74-d729-44a5-8ac0-d879327b680f

📥 Commits

Reviewing files that changed from the base of the PR and between 8075086 and e56e24c.

📒 Files selected for processing (1)
  • app.py

Comment thread app.py
Comment on lines +114 to +130
if trading_mode == "PAPER":
logged = st.session_state.get("paper_logged_signals", set())
for signal in signals:
if signal.signal in {"BUY", "SELL"}:
signal_key = f"{signal.symbol}_{signal.signal}_{signal.entry_price}"
if signal_key not in logged:
log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct)
logged.add(signal_key)
st.session_state["paper_logged_signals"] = loggedif trading_mode == "PAPER":
logged = st.session_state.get("paper_logged_signals", set())
for signal in signals:
if signal.signal in {"BUY", "SELL"}:
signal_key = f"{signal.symbol}_{signal.signal}_{signal.entry_price}"
if signal_key not in logged:
log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct)
logged.add(signal_key)
st.session_state["paper_logged_signals"] = logged

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
import ast
from pathlib import Path

source = Path("app.py").read_text(encoding="utf-8")
ast.parse(source)
print("app.py parses successfully")
PY

Repository: sreerevanth/TRADEX

Length of output: 468


Fix release-blocking syntax error in app.py PAPER trade logging (remove duplicated block)

app.py fails to parse due to missing indentation after if trading_mode == "PAPER": (IndentationError at/around line 115) and a pasted/duplicated block that splices logged into the next if.

🐛 Proposed fix
     # ── Paper trade logging ───────────────────────────────
     if trading_mode == "PAPER":
-    logged = st.session_state.get("paper_logged_signals", set())
-    for signal in signals:
-        if signal.signal in {"BUY", "SELL"}:
-            signal_key = f"{signal.symbol}_{signal.signal}_{signal.entry_price}"
-            if signal_key not in logged:
-                log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct)
-                logged.add(signal_key)
-    st.session_state["paper_logged_signals"] = loggedif trading_mode == "PAPER":
-    logged = st.session_state.get("paper_logged_signals", set())
-    for signal in signals:
-        if signal.signal in {"BUY", "SELL"}:
-            signal_key = f"{signal.symbol}_{signal.signal}_{signal.entry_price}"
-            if signal_key not in logged:
-                log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct)
-                logged.add(signal_key)
-    st.session_state["paper_logged_signals"] = logged
+        logged = st.session_state.get("paper_logged_signals", set())
+        for signal in signals:
+            if signal.signal in {"BUY", "SELL"}:
+                signal_key = f"{signal.symbol}_{signal.signal}_{signal.entry_price}"
+                if signal_key not in logged:
+                    log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct)
+                    logged.add(signal_key)
+        st.session_state["paper_logged_signals"] = logged
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if trading_mode == "PAPER":
logged = st.session_state.get("paper_logged_signals", set())
for signal in signals:
if signal.signal in {"BUY", "SELL"}:
signal_key = f"{signal.symbol}_{signal.signal}_{signal.entry_price}"
if signal_key not in logged:
log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct)
logged.add(signal_key)
st.session_state["paper_logged_signals"] = loggedif trading_mode == "PAPER":
logged = st.session_state.get("paper_logged_signals", set())
for signal in signals:
if signal.signal in {"BUY", "SELL"}:
signal_key = f"{signal.symbol}_{signal.signal}_{signal.entry_price}"
if signal_key not in logged:
log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct)
logged.add(signal_key)
st.session_state["paper_logged_signals"] = logged
# ── Paper trade logging ───────────────────────────────
if trading_mode == "PAPER":
logged = st.session_state.get("paper_logged_signals", set())
for signal in signals:
if signal.signal in {"BUY", "SELL"}:
signal_key = f"{signal.symbol}_{signal.signal}_{signal.entry_price}"
if signal_key not in logged:
log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct)
logged.add(signal_key)
st.session_state["paper_logged_signals"] = logged
🧰 Tools
🪛 Ruff (0.15.15)

[warning] 115-115: Expected an indented block after if statement

(invalid-syntax)


[warning] 122-122: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


[warning] 122-123: Expected an expression

(invalid-syntax)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.py` around lines 114 - 130, There is a duplicated/pasted block causing an
IndentationError after the if trading_mode == "PAPER": check; remove the
duplicated second block and properly indent the remaining block so the for-loop
and subsequent st.session_state assignment are inside the if. Specifically, keep
a single block that checks trading_mode, reads/updates
st.session_state["paper_logged_signals"], iterates signals, computes signal_key,
calls log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct) for new BUY/SELL
keys, adds keys to logged, and finally writes logged back to st.session_state;
ensure function/variable names referenced are trading_mode, signals,
log_paper_trade, sl_pct, tp_pct, and paper_logged_signals.

Source: Linters/SAST tools

Comment thread app.py Outdated
Comment on lines +115 to +130
logged = st.session_state.get("paper_logged_signals", set())
for signal in signals:
if signal.signal in {"BUY", "SELL"}:
signal_key = f"{signal.symbol}_{signal.signal}_{signal.entry_price}"
if signal_key not in logged:
log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct)
logged.add(signal_key)
st.session_state["paper_logged_signals"] = loggedif trading_mode == "PAPER":
logged = st.session_state.get("paper_logged_signals", set())
for signal in signals:
if signal.signal in {"BUY", "SELL"}:
signal_key = f"{signal.symbol}_{signal.signal}_{signal.entry_price}"
if signal_key not in logged:
log_paper_trade(signal, sl_pct=sl_pct, tp_pct=tp_pct)
logged.add(signal_key)
st.session_state["paper_logged_signals"] = logged

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear the session dedupe cache with the paper-trade store.

This cache survives clear_paper_trades(), so after the JSON file is wiped the same BUY/SELL key is still treated as “already logged” and the next scan can show an empty paper-trades table without recording a fresh trade. Reset paper_logged_signals when paper history is cleared.

🧰 Tools
🪛 Ruff (0.15.15)

[warning] 115-115: Expected an indented block after if statement

(invalid-syntax)


[warning] 122-122: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


[warning] 122-123: Expected an expression

(invalid-syntax)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.py` around lines 115 - 130, When clear_paper_trades() wipes the JSON
paper-trade store it doesn't reset the in-memory dedupe cache
paper_logged_signals, so previously-seen BUY/SELL keys remain marked and new
trades aren't recorded; update clear_paper_trades() to also remove or reset
st.session_state["paper_logged_signals"] (e.g., delete the key or set it to an
empty set) so the session dedupe cache is cleared whenever the paper history is
cleared, and ensure any code paths that clear paper history call that same
reset.

@samalbishnupriya06-stack

samalbishnupriya06-stack commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

@sreerevanth i have updated the codes please verify it

@sreerevanth sreerevanth merged commit fb1e639 into sreerevanth:main Jun 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhancement: Implement Paper Trading Mode and Strategy Backtesting

2 participants