Date: 1st May 2026
GTT Orders for Zerodha & Dhan, Telegram /StopPython Command, Historify Parquet Aggregation Fix, Codebase-Wide bare-except Sweep & Critical Docker Upgrade Hotfix
This release covers 6 commits since v2.0.0.7. The headline addition is end-to-end GTT (Good Till Triggered) order support for Zerodha and Dhan via four new endpoints — placegttorder, modifygttorder, cancelgttorder, gttorderbook — currently in pilot, with rollout to the remaining supported brokers planned for upcoming releases. Alongside the feature work, this release ships a critical hotfix for Docker upgrades from v2.0.0.5 and earlier: the auto-rotation of publicly-known sample APP_KEY introduced in v2.0.0.6 was crashing the gunicorn worker under Docker because the .env mount was read-only and the rotation could not write its atomic temp file. Any pre-v2.0.0.6 Docker install that left the sample keys in .env was hitting a restart loop on upgrade.
Highlights
GTT Order Implementation (Zerodha + Dhan) — Pilot rollout of /api/v1/placegttorder, /api/v1/modifygttorder, /api/v1/cancelgttorder, /api/v1/gttorderbook. Database migration is mandatory on upgrade. Rollout to all other supported brokers is planned. (#1322)
Telegram /StopPython command + /closeall enhancement — Lists running Python strategies inline; new Close all + Stop strategies button on /closeall flattens positions and stops every running strategy in one flow. (#1231)
Historify Parquet export now aggregates computed intervals — Selecting Parquet with 5m / 15m / 30m / 1h / W / M / Q / Y no longer silently downgrades to ZIP. The exporter now uses the same DuckDB time-bucket / daily-aggregation logic the ZIP path has used. (#917)
Codebase-wide bare-except sweep — 82 except: clauses across 45 files replaced with except Exception: (#1039). Restores correct shutdown-signal handling under Gunicorn / Docker / systemd; aligns with PEP 8 / Ruff E722.
🔴 Critical: Docker .env :ro mount removed; APP_KEY auto-rotation no longer crashes legacy Docker installs. Affects every Docker user upgrading from v2.0.0.5 or earlier.
Trading APIs
GTT Orders (#1322)
ce0e4d5 — Feat : Gtt Order Implementation. Updated for Dhan and Zerodha
Pilot APIs (currently Zerodha + Dhan; other brokers to follow):
POST /api/v1/placegttorder — place a GTT trigger.
POST /api/v1/modifygttorder — modify trigger / leg parameters.
POST /api/v1/cancelgttorder — cancel an active GTT.
POST /api/v1/gttorderbook — list active triggers.
Schema highlights: flat place/modify body with triggerprice_sl and triggerprice_tg, MIS rejected, last_price fetched server-side; Dhan SINGLE/OCO mapping with per-leg modify; Zerodha MARKET pricetype auto-converted to MPP-protected LIMIT (Kite GTTs cannot carry MARKET).
⚠️ Database migration is mandatory on upgrade. Run before starting the new build:
uv run python upgrade/migrate_all.py
or, just for the GTT tables:
uv run python upgrade/migrate_gtt.py
Adds sandbox_gtt and sandbox_gtt_legs tables and defaults. Sandbox / analyze-mode GTT execution is Phase 3 — analyze mode currently returns 501 for GTT calls until the in-progress sandbox integration ships.
API docs: https://docs.openalgo.in/api-documentation/v1/orders-api/placegttorder
Telegram Bot (#1231)
53334a0 — feat(telegram): add /stoppython and "Close all + Stop strategies" action
New /stoppython command snapshots RUNNING_STRATEGIES from blueprints/python_strategy.py and renders one inline button per running strategy plus a Stop All button and Cancel. Per-strategy and bulk actions both prompt for confirmation before terminating, then call stop_strategy_process() — the same code path the UI's Stop button uses (SIGTERM → SIGKILL on Linux/Mac, taskkill /F /T on Windows). The strategy-id ↔ button-index map is held in context.user_data["stoppy_list"] so callback_data stays under Telegram's 64-byte cap regardless of how long strategy IDs get. If nothing is running, the bot replies ℹ️ No Python strategies running. and exits cleanly.
/closeall confirmation now offers a third button:
Help text updated; docs/design/43-telegram-bot/README.md brought back in sync with the actually-registered command names (/orderbook, /tradebook, /chart, /mode, /menu, /link, /unlink).
Historify (#917)
ee89cf8 — fix(historify): aggregate computed intervals in Parquet export
export_to_parquet() previously ran a direct WHERE interval = ? query against market_data, which returns zero rows for any non-storage interval (only 1m and D are physically stored — 5m / 15m / 30m / 1h, custom intraday like 25m / 2h, and W / M / Q / Y are aggregated on the fly). To prevent empty downloads, the historify bulk-export blueprint silently overrode the user's Parquet selection to ZIP whenever any computed interval was requested — so picking Parquet + 5m produced a .zip of CSVs.
export_to_parquet() now mirrors export_to_zip()'s three-branch interval handling:
All symbols' rows go into a single Parquet file with the original schema preserved (symbol, exchange, interval, timestamp, open, high, low, close, volume, oi, datetime). Skipped symbols (missing source data) are surfaced in the response message rather than silently dropped. Compression codec (zstd / snappy / gzip / none) is honored as before.
The blueprint's silent ZIP override now applies only to multi-interval requests (legitimate — single-table formats can't carry per-interval files) and to single-computed CSV/TXT requests (those exporters still use direct queries and need the same treatment in a follow-up).
Code Quality / Stability (#1039)
b6419a9 — fix: replace bare except clauses with except Exception across codebase
82 occurrences across 45 production files in blueprints/, broker/ (all 30+ broker integrations), database/, sandbox/, services/, test/, utils/, and websocket_proxy/. Bare except: swallows BaseException, including SystemExit and KeyboardInterrupt — meaning Ctrl+C, SIGTERM from Docker / systemd, and other shutdown signals could be silently absorbed mid-iteration in long-running loops (websocket adapters, Telegram polling, sandbox engine).
Practical effects:
Cleaner shutdown under Gunicorn / Docker / systemd — SIGTERM propagates correctly.
MemoryError and other BaseException subclasses now propagate as expected.
database/historify_db.py:_safe_timestamp() additionally upgraded with logger.warning so timestamp conversion failures land in log/errors.jsonl instead of disappearing silently.
Ruff E722 / Flake8 E722 no longer fire across the codebase, making future CI lint enforcement viable.
No behaviour change for ordinary exception flows — call sites still catch Exception and below, exactly as before.
Security / Docker (Critical)
245403f — fix(security/docker): unblock APP_KEY/PEPPER auto-rotation under Docker (v2.0.0.8)
v2.0.0.6 introduced an auto-rotation in utils/env_check.py that detects the publicly-known sample APP_KEY / API_KEY_PEPPER (which shipped in .sample.env up to v2.0.0.5, and which install-docker.sh did not rewrite until commit 0162ce3) and replaces them with fresh secrets on first run. Under Docker, the rotation crashed the gunicorn worker:
[OpenAlgo security]
Detected publicly-known APP_KEY/API_KEY_PEPPER in .env, but
could not rewrite the file
([Errno 13] Permission denied: '/app/utils/../.env.tmp').
Two compounding causes:
docker-compose.yaml and the install-script compose templates mounted .env read-only (./.env:/app/.env:ro), so the rotation's atomic .env.tmp write failed with EACCES and sys.exit(1) killed the worker — gunicorn restart loop, container hard-down.
install-docker.sh and install-docker-multi-custom-ssl.sh kept .env at chmod 644 owned by host root because the previous :ro mount made chmod 600 + root ownership unreadable to the container's appuser (UID 1000) — see issue #960. With :ro removed, that workaround flipped into a problem: appuser still couldn't write a root-owned file even if the mount allowed it.
This affected every Docker user who installed before v2.0.0.6 and pulled the v2.0.0.6+ image — a meaningful slice of the existing Docker install base.
Fixes shipped:
Drop :ro from every .env mount so the rotation can write its temp file. Touched: docker-compose.yaml, install/install-docker.sh (compose template), install/install-docker-multi-custom-ssl.sh (compose template), install/docker-run.sh, install/docker-run.bat, docker-build.sh.
chown 1000:1000 .env && chmod 600 .env in install/install-docker.sh, install/install-docker-multi-custom-ssl.sh, install/docker-run.sh (with Linux/Mac branching). Tighter than the previous mode 644 and Docker-compatible at the same time — only appuser (UID 1000) on the host can read/write, instead of "anyone with read access to the install dir."
start.sh pre-flight check before gunicorn launches. If APP_KEY / API_KEY_PEPPER is in the publicly-known compromised set AND .env is not writable from inside the container, prints an unmissable banner with the safe recovery recipe and exits cleanly. Replaces the buried "Permission denied" stack trace with an actionable error.
Migration for users who hit the crash on Docker:
cd /path/to/openalgo
docker compose down
git pull
docker compose pull
Generate a fresh APP_KEY only — do NOT touch API_KEY_PEPPER on a populated install
APP_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
sed -i "s|^APP_KEY =.|APP_KEY = '$APP_KEY'|" .env
Make .env writable by the container's appuser (UID 1000)
sudo chown 1000:1000 .env
sudo chmod 600 .env
docker compose up -d
⚠️ Do NOT manually rotate API_KEY_PEPPER on a populated install. The auto-rotation in utils/env_check.py deliberately declines to touch the pepper when the database has users — rotating it would invalidate every Argon2 password hash and every Fernet-encrypted broker auth/feed token / TradingView API key, none of which can be recovered. If you genuinely need to rotate the pepper (rare), use the dedicated migration which handles re-encryption and the required password reset:
uv run python upgrade/rotate_pepper.py
After applying the APP_KEY-only migration, _generate_keys_on_first_run takes the silent fast path (APP_KEY no longer in COMPROMISED_APP_KEYS) and the container boots cleanly. Browser sessions need to log in again — by design, that's how APP_KEY rotation prevents anyone with the leaked sample key from forging your sessions.
Contributors
@marketcalls (Rajandran) — release management, GTT order implementation for Zerodha and Dhan (#1322), Telegram /stoppython command + /closeall enhancement (#1231), Historify Parquet aggregation fix (#917), codebase-wide bare-except sweep (#1039), critical Docker .env :ro hotfix.
Links
Repository: https://github.com/marketcalls/openalgo
Documentation: https://docs.openalgo.in
Discord: https://www.openalgo.in/discord
YouTube: https://www.youtube.com/@openalgo
Issue tracker: https://github.com/marketcalls/openalgo/issues