Swing India — Self-Evolving Pipeline
End-to-end India cash-equity swing trade workflow. Combines fundamental sanity check (Anthropic finance skills) with technical setup + risk sizing (Bhala NSE skills). Every run is logged. Learnings compile from full history and tighten future runs.
Modes
| Trigger | Mode |
|---|---|
/swing-india (NO args) / swing-india / "suggest stocks" / "what to buy" |
dashboard — DEFAULT mode. Runs DAILY (paper-trading progress report) THEN SCAN (new picks) THEN STATS (closed history) in single combined output. One invocation = full summary. |
/swing-india scan |
scan-only — skip daily report, just scan Nifty100 + rank top 10 |
/swing-india daily / progress / report / "how are my swings doing" |
daily-only — paper-trading progress report: live CMP per open, unrealized PnL, R-multiple, signal (EXIT/TRAIL/WATCH/HOLD), portfolio totals, prioritized action commands |
swing analyze <ticker> / /swing-india <ticker> |
analyze — single-ticker deep dive |
/swing-india review |
review — check open positions (quick, no portfolio aggregation) |
/swing-india close <ticker> <exit_price> <reason> |
close — log outcome |
/swing-india compile |
compile — rewrite learnings from raw log |
/swing-india stats |
stats — print W/L, R-multiple, etc. |
Hard Rules (NEVER violate)
- Cash equity ONLY. Refuse F&O / options / futures. SEBI FY25: 91% retail F&O traders lost.
- Min market cap ₹5,000 cr. No microcaps.
- Min ADV ₹5 cr. Skip illiquid.
- Skip if quarterly result < 7 days away.
- Skip if stock in ASM/GSM/T2T surveillance.
- Max 3 concurrent open positions.
- Halt new entries if
month_pnl_pct < -3. - Per-trade risk ≤ 1% of capital.
- R:R minimum 1.5:1 to T1.
DASHBOARD Mode — DEFAULT (bare invocation)
Triggered: /swing-india, swing-india, "suggest stocks", "what to buy", "give me summary", "swing india", or any bare invocation with NO sub-command.
Purpose: Single invocation = full one-screen summary covering everything. No need to run separate commands for daily progress + scan + stats. User runs ONE thing, gets ALL info.
Pipeline (run in order, single combined output)
- DAILY block — run
python3 ~/.swing-india/daily_summary.py:- Live CMP per
open[]position - Per-position card: signal (EXIT/TRAIL/WATCH/HOLD+/HOLD), R-multiple, unrealized PnL ₹ + %, days held, distance to T1/stop
- PORTFOLIO totals: invested, market value, unrealized PnL ₹ + %, % capital deployed
- CLOSED HISTORY: trade count, W/L/BE breakdown, win rate, avg R, realized PnL
- Live CMP per
- SCAN block — run
python3 ~/.swing-india/scanner.py 10 <capital> <risk_pct>:- Top 10 ranked BUY picks from Nifty100 universe
- Each: entry, stop, T1, T2, qty, %cap, RSI, ADX, DMA+%
- Auto-persist new picks to
proposed[]+ log
- SUGGESTED ACTIONS block — copy-paste commands:
swing close <ticker> <exit> <reason>for any EXIT signalswing analyze <ticker>for top scan pick to deepen- Risk caveats if month_pnl_pct breached, slots full, etc.
Output structure (one combined output)
═══ DAILY PROGRESS (live MTM on open paper trades) ═══
[per-position cards sorted by signal priority]
[portfolio totals]
[closed history stats]
═══ NEW SCAN (Nifty100 top picks) ═══
[ranked table of survivor candidates]
═══ ACTIONS ═══
[prioritized copy-paste commands]
Skip Toggle
User can opt out of either block:
/swing-india scan→ SCAN only (skip daily)/swing-india daily→ DAILY only (skip scan)- Bare
/swing-india→ both
SCAN Mode — Suggest Stocks From Universe
Triggered: "swing scan", "/swing-india scan", "find swing setups", "scan for swings", "which stocks today". (Bare /swing-india runs DASHBOARD, which calls this internally.)
User does NOT specify ticker. Skill scans pre-defined Nifty100 universe + applies all swing-india filters + learnings + ranks survivors.
Pipeline
Run
python3 ~/.swing-india/scanner.py [top_n=10] [capital=500000] [risk_pct=1.0]Scanner internally:
- Loads
~/wiki/swing-india-learnings.md(skip list, thresholds) - Iterates 50 Nifty tickers (extensible to Nifty100/200)
- Fetches yfinance 2y OHLCV per ticker (parallel safe)
- Computes DMA50, DMA200, RSI14, MACD hist, ATR14, ADX14
- Hard rejects: skip-list, below 200DMA, ADX < min, RSI > cap, barely above DMA, MACD fading
- Sizes: entry = CMP + 0.5×ATR, stop = CMP - 1.5×ATR, qty = (capital × risk%) / risk_per_share
- Filters R:R T2 ≥ 2.5, position ≤ 35% capital
- Scores each survivor 0-100:
- RSI 40-50 = +30 (best per backtest), 50-60 = +22, 60-65 = +12
- ADX 35-50 = +25 (strong trend), 25-35 = +18, 50+ = +12
- 2-5% above 200DMA = +20 (sweet spot), 5-10% = +12, 10-20% = +5
- MACD accelerating = +15, just above 0 = +8
- Favor-list ticker = +10 bonus
- Sorts by score desc, returns top N
- Loads
Print ranked table + JSON.
Append summary YAML to
~/wiki/raw/swing-india-log.md(type: scan).Do NOT auto-add to proposed[]. User picks which to deepen via
analyze <ticker>.
Output Card
SCAN — 2026-MM-DD HH:MM IST
Universe: 50 Nifty100 | Capital: ₹X | Risk: Y%
Learnings active: yes (N=Z trades) | Skip list: 4 auto-rejected
Scanned: 50 | Filtered: 43 | Candidates: 7
# TICKER SCORE CMP RSI ADX DMA+% ENTRY STOP T1 T2 QTY %CAP
1 ASIANPAINT.NS 72 2599 61.7 46.6 3.0 2628 2514 2799 2970 43 22.6
2 SHRIRAMFIN.NS 65 1007 40.9 30.5 20.1 1022 963 1110 1198 85 17.4
...
NEXT: pick 1-3, run "swing analyze <ticker>" for deep card + add to proposed[]
When to Use Scan vs Analyze
- Scan = "what should I trade today?" — discovery
- Analyze = "is THIS ticker a setup?" — validation of your idea
ANALYZE Mode — Full Pipeline
Stage 0 — Read Learnings + Pre-flight
- Read
~/wiki/swing-india-learnings.md. If file empty / doesn't exist, fall back to defaults. - Apply learned overrides ONLY IF
closed_trade_count >= 10(else log "learnings active=false: insufficient sample (N=/10)"). - Read
~/.swing-india/positions.json. Refuse if:len(open) >= 3→ list current positionsmonth_pnl_pct < -3→ advise pause- ticker already in
open→ suggest review
Stage 1 — Fundamental Filter
Invoke skills:
comps-analysis— P/E, EV/EBITDA, P/B, ROCE vs sector peers.dcf-model— intrinsic value. Capture upside %.
Reject (SKIP):
- Comps premium > 30% AND DCF upside < 5%
- ROCE < 8% AND debt/equity > 1.5
- DCF intrinsic < CMP by > 15%
Pass: DCF upside ≥ 8% OR (comps fair ±15% AND ROCE > sector median).
Save dcf_intrinsic for T2 target.
Stage 2 — Technical Setup
Invoke Bhala skills:
nse-multi-timeframe-analysis— weekly bias, daily setup, 4H/1H entry timing.nse-rsi-divergence— bearish divergence check.nse-fibonacci-trading— support/resistance Fib levels.nse-technical-analysis— 50DMA, 200DMA, MACD, ADX.
Reject:
- Weekly trend down (below 200-week MA)
- Daily below 200DMA
- Bearish RSI divergence on daily
- ADX < 20
Pass outputs: entry_trigger, stop_loss, t1 (1.5R), t2 (DCF intrinsic capped at next major resistance).
Stage 3 — Risk + Sizing
nse-position-sizing— fixed-fractional. Reject if position > 35% of capital.nse-risk-reward-ratio— verify R:R T1 ≥ 1.5, T2 ≥ 2.5.nse-trailing-stops— trail logic post-T1 (default ATR(14) × 1.5).nse-stop-loss-strategies— hard invalidation rule.
Stage 4 — Output Card
TICKER: <SYMBOL>.NS
VERDICT: BUY | WAIT | SKIP
REASON: <one-line>
LEARNINGS APPLIED: <yes (active rules: 1,3,7) | no (N=4/10 sample)>
FUNDAMENTAL
CMP: ₹<x>
DCF intrinsic: ₹<y> (upside +<z>%)
Comps: <fair|premium|discount>
ROCE: <a>%
TECHNICAL
Trend (W/D/4H): <up|sideways|down>
Trigger: <entry condition>
Stop: ₹<s>
T1 (1.5R): ₹<t1>
T2 (DCF): ₹<t2>
POSITION
Capital: ₹<cap>
Risk: ₹<r> (<x>% of capital)
Quantity: <n> shares
Value: ₹<v> (<%> of capital)
R:R T1 / T2: <a> / <b>
INVALIDATION
- Hard stop hit
- Below <invalidation_level> on close
- Quarterly miss > 10%
HORIZON: 3-15 days (or learning override)
TRAIL: ATR(14) × 1.5 post T1
Stage 5 — Persist + Log
A. Update positions.json: append to proposed[] (NOT open[] until user confirms entry filled).
B. Append run YAML to ~/wiki/raw/swing-india-log.md:
---
type: run
timestamp: <ISO-8601 UTC>
ticker: <SYMBOL>.NS
mode: analyze
verdict: BUY|WAIT|SKIP
reason: <one-line>
capital: <inr>
fundamentals:
cmp: <num>
dcf_intrinsic: <num>
upside_pct: <num>
comps_premium_pct: <num>
roce_pct: <num>
debt_equity: <num>
technical:
weekly_trend: up|sideways|down
daily_above_200dma: true|false
rsi_daily: <num>
macd: bull_cross|bear_cross|neutral
adx: <num>
fib_support: <num>
fib_resistance: <num>
setup: <pattern_name>
position:
entry: <num>
stop: <num>
t1: <num>
t2: <num>
qty: <num>
position_pct_capital: <num>
rr_t1: <num>
rr_t2: <num>
rules_applied:
- <rule_id_or_name>
notes: <free text>
---
End BUY verdict with reminder:
Paper trade 2 months minimum before real capital. SEBI FY25: 91% retail derivatives traders lost. Cash equity edge requires discipline + 50+ trades for statistical significance.
REVIEW Mode
Triggered: "review swings" / "/swing-india review".
- Read
open[]from positions.json. - For each: fetch CMP (Groww MCP > Live-NSE-BSE MCP > yfinance fallback > WebFetch NSE).
- Flag per position:
- Stop hit → suggest close
- T1 reached → suggest trail activation
- T2 reached → suggest exit
- Time stop (held >
max_holding_daysfrom learnings, default 15) → suggest exit - Drawdown > 50% of risk amount → flag
- Recommend trail-stop adjustments.
- NEVER auto-modify state. User must confirm any close.
CLOSE Mode
Triggered: /swing-india close <ticker> <exit_price> <reason>.
Reasons: t1_hit | t2_hit | stop_hit | trail_hit | time_stop | discretionary | invalidation.
- Find ticker in
open[]. Compute:r_multiple = (exit - entry) / (entry - stop)(negative = loss)days_held = today - openedpnl_inr = (exit - entry) * qtyoutcome = win|loss|breakeven(R > 0.2 / R < -0.2 / else)
- Move from
open[]toclosed[]. - Update
month_pnl_pctandmonth_pnl_inr. - Append outcome YAML to raw log:
---
type: close
close_timestamp: <ISO>
ticker: <SYMBOL>.NS
entry: <num>
exit: <num>
stop: <num>
qty: <num>
exit_reason: t1_hit|t2_hit|stop_hit|trail_hit|time_stop|discretionary|invalidation
r_multiple: <num>
days_held: <num>
pnl_inr: <num>
outcome: win|loss|breakeven
postmortem: <free text — what worked, what didn't>
---
- If new month, reset
month_pnl_pctandmonth_pnl_inrto 0, updatecurrent_month.
COMPILE Mode
Triggered: /swing-india compile. Manual only per user choice.
- Read full
~/wiki/raw/swing-india-log.md. - Count
closed[]entries. If< 10, output: "Insufficient sample (N=/10). Compile aborted. Returning current learnings unchanged." Exit. - Analyze closed trades. Compute per-pattern stats:
- Win rate by setup (flag, breakout, pullback, etc.)
- Win rate by RSI bucket (< 40, 40-50, 50-65, > 65)
- Win rate by sector
- Win rate by holding-day bucket
- Avg R-multiple per outcome
- Worst tickers (≥3 losses)
- Rewrite
~/wiki/swing-india-learnings.md(replace whole file):
# Swing-India Learnings — compiled <date>
**Sample:** N=<x> closed trades | Win rate: <y>% | Avg R: <z>
## Confidence
<low (10-29) | medium (30-99) | high (100+)>
## Patterns That Win (≥60% W, N≥5)
- ...
## Patterns That Lose (<40% W, N≥5)
- ...
## Threshold Adjustments (applied at Stage 0 of analyze mode)
- `min_rr_t1`: <new value> (default 1.5)
- `rsi_max_entry`: <new value or null>
- `max_holding_days`: <new value> (default 15)
- `min_dcf_upside_pct`: <new value> (default 8)
## Skip List
- <ticker>: <reason>, lost N times
## Sector Tilts
- favor: <list>
- avoid: <list>
## Active Rule Overrides
1. <rule statement> — basis: <evidence>
2. ...
## Notes
- <free-form observations>
## Changelog
- <date>: <what changed from prior compile>
- After write, summarize the diff vs prior learnings (which rules added/changed/removed).
STATS Mode
Triggered: /swing-india stats.
Print:
- Closed trades count
- Win rate, avg win R, avg loss R, expectancy
- Best / worst trade
- Avg holding days
- Month PnL %
- Open positions count + unrealized PnL
DAILY Mode — Paper-Trading Progress Report
Triggered: /swing-india daily / /swing-india progress / /swing-india report / "how are my swings doing" / "daily paper trade summary" / "paper trade progress".
Purpose: End-of-day check on every open paper position. Live CMP fetch, unrealized PnL, R-multiple, time-stop tracking, prioritized action commands. Use this every trading day to drive close decisions without manually inspecting positions.
Pipeline
- Run
python3 ~/.swing-india/daily_summary.py. - Script internally:
- Loads
~/.swing-india/positions.json - For each
open[]entry: fetch CMP via yfinance (fallback: cmp_at_scan with(stale)tag if quote fails) - Computes per-position:
days_held= today − opened_atunrealized_pnl_inr= (cmp − entry) × qtyunrealized_pnl_pct= (cmp / entry − 1) × 100r_multiple= (cmp − entry) / (entry − stop)dist_to_t1_pct,dist_to_stop_pct- signal:
EXITif cmp ≤ stop OR cmp ≥ t2 OR held ≥ horizonTRAILif cmp ≥ t1 (book 50%, raise stop to ATR×1.5 below CMP)WATCHif r_multiple < −0.5 (half-stop warning)HOLD+if r_multiple > +0.8 (approaching T1)HOLDotherwise
- Sorts rows by signal priority (EXIT → TRAIL → WATCH → HOLD+ → HOLD)
- Aggregates portfolio: invested, market value, unrealized PnL ₹ and %, % of capital deployed
- Aggregates closed history: count, win rate, avg R, total realized PnL
- Loads
- Print per-position card + portfolio totals + closed-history block + suggested-actions block with copy-paste
swing close <ticker> <exit> <reason>commands for any EXIT signal. - Do NOT auto-close any position. User must run
swing closemanually to log outcome.
Output Card
╔══════════════════════════════════════════════════════════════════════╗
║ SWING-INDIA — DAILY PAPER-TRADING REPORT ║
║ YYYY-MM-DD HH:MM IST | Capital: ₹X | Mode: paper ║
╚══════════════════════════════════════════════════════════════════════╝
Open: N | Closed: M | Proposed (queued): K
┌─ [SIGNAL] TICKER [SIG ] held Xd/Yd ─────────────────────────────
│ CMP: ₹... Entry: ₹... Qty: ...
│ Stop: ₹... (X%) T1: ₹... (X%)
│ T2: ₹... Unrealized: ₹... (X%) R=...
│ → <reason>
└────────────────────────────────────────────────────────────────────
┌─ PORTFOLIO ──────────────────────────────────────────────────┐
│ Invested: ₹... (X% of cap) │
│ Market val: ₹... │
│ Unrealized: ₹... (X% on inv / Y% on cap) │
└───────────────────────────────────────────────────────────────┘
┌─ CLOSED HISTORY ─────────────────────────────────────────────┐
│ Trades: N (W:x L:y BE:z) Win-rate: P% │
│ Avg R: ±R Realized PnL: ₹... │
└───────────────────────────────────────────────────────────────┘
┌─ SUGGESTED ACTIONS (priority order) ─────────────────────────┐
│ [EXIT ] TICKER → swing close TICKER <exit> <reason> │
│ [TRAIL] TICKER → # book 50%, raise stop ATR×1.5 below CMP │
│ [WATCH] TICKER → # half-stop, no new add │
└───────────────────────────────────────────────────────────────┘
When to Use Daily vs Review
- daily = full progress dashboard, every trading day, with portfolio totals + closed stats + action list
- review = quick scan of open positions for stop/T1/T2/time-stop hits, no portfolio aggregation
Optional Scheduling
If user wants automated daily run without manual trigger, suggest cron OR /schedule skill:
# cron (weekdays 10:30 IST, post-market-open)
30 10 * * 1-5 python3 ~/.swing-india/daily_summary.py >> ~/.swing-india/daily.log 2>&1
But default = manual trigger via skill phrase.
Data Source Order
- Groww MCP (if configured) — live NSE quote, ATR, depth
- Live-NSE-BSE MCP — quotes, news
- Python yfinance with
.NSsuffix — historical OHLCV - WebFetch NSE/BSE official pages — last resort
Refusal Triggers
Refuse + cite SEBI 91% loss stat for:
- F&O / options / futures swing
- Intraday scalping
- Penny stocks (< ₹5,000 cr)
- Tips / sure-shot calls / guaranteed returns
File Map
| Path | Purpose | Mutation |
|---|---|---|
~/.swing-india/positions.json |
Live state (proposed/open/closed/PnL) | mutated every analyze + close |
~/wiki/raw/swing-india-log.md |
Append-only run + close history | append-only (immutable) |
~/wiki/swing-india-learnings.md |
Distilled rules from log | rewritten on /swing-india compile |
~/.swing-india/scanner.py |
Universe scanner | read-only (called by scan mode) |
~/.swing-india/analyze.py |
Single-ticker deep-dive engine | read-only (called by analyze mode) |
~/.swing-india/daily_summary.py |
Daily paper-trading progress report (live CMP, MTM, signals, actions) | read-only (called by daily mode) |
~/.swing-india/dashboard.py |
DEFAULT dashboard wrapper — runs daily_summary.py + scanner.py back-to-back | read-only (called by bare /swing-india) |
~/.swing-india/compile.py |
Learnings rewrite engine | read-only (called by compile mode) |
Self-Evolution Loop Summary
analyze → reads learnings → applies rules → logs run YAML
↓ (later)
close → logs outcome YAML
↓ (after ≥10 closes)
compile → reads full log → rewrites learnings
↓ (next analyze)
analyze → reads UPDATED learnings → tighter rules → better filter
Compounds over time. 50+ trades = real edge or proof of no edge.