A live, always-on trading dashboard + algorithmic SIP / swing / F.I.R.E. engine for the Indian stock market — built for a Euro-resident investor.
"Earn in Euros, invest in Rupees, watch it from a Pi on the wall."
One Python package, three deliverables, one DuckDB file:
┌───────────────────────────┐
│ pure strategy core │
│ (no I/O, no Rich, async- │
│ agnostic — fully tested) │
└────────────┬──────────────┘
│
┌────────────────────────┴───────────────────────┐
│ │
┌───────▼────────┐ ┌──────────▼──────────┐
│ imbot daemon │ writes ┌─────────────┐ │ imbot ui │
│ (asyncio + ├─────────►│ state.duck │◄──┤ (Textual TUI; │
│ APScheduler) │ │ db │ │ read-only; │
│ │ └─────────────┘ │ refreshes 2-10s) │
└────────────────┘ ▲ ▲ ▲ └─────────────────────┘
│ │ │
│ │ └──── imbot fire / dividend / swing / project
│ └────── imbot backtest dividend / swing
└──────── imbot optimize --param atr-mult=...
imbot daemon runs cron-style jobs: refresh prices every 5min during NSE hours, scan for swing setups at 15:25 IST, regenerate the dividend list at 16:00 IST, deposit your monthly SIP on the last NSE trading day. imbot ui is a Textual dashboard with live positions, equity curve, bot brain, and event log — read-only DuckDB so it never blocks the daemon.
# 1. Clone + venv
git clone https://github.com/GhostRoboticsLab/IndianMarketBot.git
cd IndianMarketBot
python3.11 -m venv venv
source venv/bin/activate
# 2. Install (editable, with dev extras)
pip install -e ".[dev]"
# 3. Verify
pytest -q # 69 tests, ~1s
# 4. Pick your entry point
imbot menu # interactive picker
imbot fire # one-shot: monthly F.I.R.E. allocation
imbot daemon # long-running scheduler (Ctrl+C to stop)
imbot ui # live dashboardThree console scripts ship: imbot (CLI), imbot-daemon (long-running scheduler), imbot-ui (Textual app). They all share one DuckDB file at ~/.imbot/state.duckdb (override via IMBOT_HOME=/var/lib/imbot).
Three F.I.R.E. routes, four bots, one regime-aware engine. Documented in Docs/FIRE_Strategies_40k_SIP.md.
| Route | Style | Vehicle | Wins when |
|---|---|---|---|
| 1. Snowball 🐌 | Income | High-yield stocks + DRIP | Retirement — live off dividends |
| 2. Indexing 📚 | Reliability | Nifty 50 / Next 50 ETFs | Always (given 15-year horizon) |
| 3. Momentum 🚀 | Alpha | Mid/Small caps, 200-DMA + 6M momo | Bull markets |
The daemon rebalances dynamically based on Nifty 50's 200-DMA:
BULL (Nifty > 200-DMA) BEAR (Nifty < 200-DMA)
┌────────────────────┐ ┌────────────────────┐
│ Core ETFs 40% ████░░░░░░░░░░░ │ Core ETFs 80% ████████████████░░
│ Alpha picks 60% ████████████░░░ │ Alpha picks 20% ████░░░░░░░░░░░░░░
└────────────────────┘ └────────────────────┘
The V4.0 "institutional math" stack running inside the daemon:
- ATR-based 1%-risk sizing — every position risks exactly 1% of equity if stopped out
- Relative Strength filter — only buy stocks outperforming Nifty's 20-DMA-of-RS
- Market breadth gate — refuse new entries unless >40% of universe is above 50-DMA
- Chandelier trailing stop —
Close − 2.5 × ATR, lets winners run - Hard time-stop — any position held ≥20 days exits, win or lose (fixes runaway-winners-stuck-open bug in the legacy bot)
# screening / allocation (live, one-shot)
imbot fire --budget 500
imbot dividend --budget 10000
imbot swing --top 5 --rr 2.5
# forward projection (pure math, no network)
imbot project --years 15 --sip 500 --expense 2500
# backtests (downloads history)
imbot backtest dividend --years 5 --sip 500
imbot backtest swing --years 2 --capital 1000000
# parameter grid search
imbot optimize --list
imbot optimize --param atr-mult=2.0,3.0,0.25 --param time-stop=15,30,5
# daemon + dashboard
imbot daemon # foreground; for systemd see Docs/operations.md
imbot ui # Textual dashboard
# DuckDB helpers
imbot db init
imbot db inspect
imbot db migrate-json legacy/virtual_account_state.json health_beat every 30 s (heartbeat row)
paper_tick every 60 s, NSE (trailing stops + exits)
prices_tick every 5 min, NSE (OHLCV cache refresh)
signals_swing daily 15:25 IST (V4.0 swing setups)
paper_eod daily 15:25 IST (new entries if regime + breadth bull)
signals_dividend daily 16:00 IST (snowball candidates)
signals_fire daily 16:30 IST (FIRE allocation incl. regime)
monthly_sip 09:30 IST · last NSE day of month (deposits EUR→INR)
Each job hits Yahoo Finance through data.yahoo.fetch_*_async — 10s timeout, 3 retries, exponential backoff. NSE-hours gated via @requires_market_open so decisions never run at 2am on stale data.
Knobs you might actually want to turn live in config/default.toml. Override via:
~/.imbot/config.toml(per-user file, same TOML layout)IMBOT_*env vars:IMBOT_RISK__MAX_POSITIONS=5,IMBOT_SIP__AMOUNT_EUR=750
| To change... | Edit... |
|---|---|
| Tradable universe | src/indianmarket/universe.py |
| Risk per trade / max positions / ATR multiplier | config.risk |
| Monthly SIP amount / deposit time | config.sip |
| Daemon cadence | config.daemon |
| NSE holidays | src/indianmarket/data/market_calendar.py |
| FX fallbacks | config.fx |
| Bull/bear allocation split | config.allocation |
The strategy core is fully pure — change a function in src/indianmarket/core/*.py and your update flows through the CLI, daemon, and dashboard with no extra wiring.
IndianMarketBot/
├── pyproject.toml # deps, console_scripts (imbot, imbot-daemon, imbot-ui)
├── config/
│ └── default.toml # canonical knobs (override via ~/.imbot or env)
├── src/indianmarket/
│ ├── config.py # TOML + env var loader
│ ├── universe.py # one source of truth for ticker lists
│ ├── constants.py # risk + execution defaults
│ ├── core/ # pure strategy: indicators, regime, sizing,
│ │ # momentum, dividend, swing, projection, backtest
│ │ # — 100% I/O-free, fully unit-tested
│ ├── data/
│ │ ├── yahoo.py # the ONLY module that imports yfinance
│ │ ├── cache.py # in-memory TTL cache
│ │ ├── store.py # DuckDB read/write + JSON migration
│ │ ├── schema.sql # prices / signals / positions / trades / ...
│ │ └── market_calendar.py # NSE hours + holidays + @requires_market_open
│ ├── daemon/ # asyncio event loop + APScheduler jobs
│ │ ├── runner.py # SIGTERM/SIGINT graceful shutdown
│ │ ├── scheduler.py # cron triggers
│ │ └── jobs/ # prices, paper_trader, signals_*, monthly_sip
│ ├── ui/ # Textual TUI (read-only DuckDB)
│ │ ├── app.py # 3×2 grid layout
│ │ └── widgets/ # header, positions, signals, brain, log,
│ │ # equity chart (textual-plotext), status bar
│ ├── cli/ # Typer subcommands
│ │ └── commands/ # fire, dividend, swing, project, backtest,
│ │ # optimize, daemon, ui, menu, db
│ ├── optimize/grid_search.py # cartesian-product param sweeps
│ └── tests/ # 69 unit + smoke tests (pytest)
├── Docs/
│ ├── FIRE_Strategies_40k_SIP.md
│ └── development_journey.md
├── CLAUDE.md # guidance for Claude Code agents
└── LICENSE # Apache 2.0
# On the Pi
sudo apt install -y python3.11-venv git
git clone https://github.com/GhostRoboticsLab/IndianMarketBot.git ~/imbot
cd ~/imbot && python3.11 -m venv venv && source venv/bin/activate
pip install -e .
# systemd: start daemon at boot
sudo cp scripts/imbot.service /etc/systemd/system/ # see Docs/operations.md (C15)
systemctl --user enable --now imbot
# From the Pi's HDMI-attached monitor (or SSH session)
imbot ui # full-screen Textual dashboardThe UI auto-falls-back to an ASCII sparkline if textual-plotext doesn't render correctly on the Pi terminal. Layout collapses to single-column below ~100 cols.
This is research/educational code, not financial advice.
- The bots produce signals. They do not place real orders. Execution is on you.
paper_traderis virtual money only — no broker API is wired in.- yfinance data can be delayed or wrong. Never trust a screen blindly.
- Past backtest performance does not predict future returns.
- Universe lists were curated knowing what works today → survivorship bias is real in the backtests.
- The author is not a SEBI-registered investment advisor.
By using this code you accept full responsibility for any decisions made with it.
- Textual + textual-plotext — the dashboard
- DuckDB — embedded columnar database, single-writer + MVCC reads
- APScheduler — async cron
- Typer — CLI
- Rich — terminal output
- pandas — time-series everything
- yfinance — free market data
- pyxirr — Rust-backed XIRR
Apache 2.0 — do what you want, just don't sue me.
Star ⭐ this repo if it sparked an idea. PRs welcome.