A robust, enterprise-grade intraday trading algorithm built with Node.js and TypeScript, specifically designed for automating Short Straddle strategies on NIFTY (Tuesday) and SENSEX (Thursday) weekly index options using Angel One's SmartAPI (BANKNIFTY is monthly expiry and strictly excluded).
- π Automated Smart Login: Hands-free authentication with automated 6-digit TOTP generation.
- π‘ Compliance Ready: Automatic resolution of Public IP, Local IP, and MAC addresses for secure API header requirements.
- π Real-time Risk Management: Active MTM tracking with automated stop-loss placement (125% factor: trigger = entry Γ 2.25, limit = trigger Γ 1.05) for all sell positions.
- π Local Positions Tracking: Persistent local state bookkeeping in
positions.jsonto track open legs and prevent duplicate trade entries; broker truth via SmartAPIGET_POSITIONSis queried for post-expiry reports and P&L verification, whilegetMtmfilters to the algo's own symbol whitelist. - π§ͺ Paper Trading Mode: High-fidelity simulation mode to test strategies against live market data without financial risk.
- π¬ Multi-Channel Remote Control: Dual support for Telegram and Slack interactive commands to monitor and control the algorithm remotely.
- π Datewise Logging & Retention: Dynamically logs application status and MTM metrics daily, with automated script-based log retention cleanup.
- π Weekly Expiry Reports: Track and analyze performance with detailed P&L and straddle breakdowns under expiry-reports/.
- π§© Developer Agent Customizations: Integrated workspace agent skills (
.agents/skills) to automate PR creation, git cleanup, and description validation. - ποΈ Modular Architecture: Clean, domain-driven design for high maintainability and testability.
- π‘οΈ High Test Coverage: Robust test suite with >= 80% branch coverage threshold ensuring reliable execution.
- π³ Docker Ready: Fully containerized for consistent deployment across environments.
- Runtime: Node.js v22+ (LTS)
- Language: TypeScript
- Framework: Express.js
- API Client: SmartAPI (via
krb-smart-api-module) - Testing: Jest
- Formatting/Linting: Prettier & ESLint
- Package Manager: pnpm
- Node.js v22+
- pnpm (recommended) or npm
- SmartAPI Credentials: API Key and 16-character TOTP Secret.
-
Clone the repository:
git clone https://github.com/kunalrbhatia/smart-api.git cd smart-api -
Install dependencies:
pnpm install
-
Configure Environment:
cp .env.example .env
Edit
.envand fill in yourAPI_KEY,CLIENT_CODE,CLIENT_PIN, andCLIENT_TOTP_PIN(16-character secret).Variable Default Description ENTRY_TIME09:15Session entry start time ( HH:mmformat) for market open checks and initial trade timing (deployed VPS setting:09:30).NO_ENTRY_AFTER15:10Session entry cutoff time ( HH:mmformat); no new straddles or rolls are entered after this time.EXIT_TIME15:17Session exit time ( HH:mmformat) when open sell positions are force-closed (deployed VPS setting:15:35).INDEXAuto ( day of week)Optional index override ( NIFTYorSENSEXonly). BANKNIFTY is ignored and falls back to day-of-week selection.
The algorithm automatically selects the index based on the day of the week (getAlgoIndex()):
- Tuesdays (
* * 2): NIFTY weekly option expiry (NSE /NFO) - Thursdays (
* * 4): SENSEX weekly option expiry (BSE /BFO) - Note: BANKNIFTY is strictly excluded (monthly expiry, out of scope).
Crontab slices for single-machine VPS execution:
# NIFTY expiry Tuesdays
16 09 * * 2 cd /home/ubuntu/smart-api && node dist/run-algo.js >> logs/algo_run.log 2>&1
20-55/5 09 * * 2 cd /home/ubuntu/smart-api && node dist/run-algo.js >> logs/algo_run.log 2>&1
*/5 10-14 * * 2 cd /home/ubuntu/smart-api && node dist/run-algo.js >> logs/algo_run.log 2>&1
0-40/5 15 * * 2 cd /home/ubuntu/smart-api && node dist/run-algo.js >> logs/algo_run.log 2>&1
# SENSEX expiry Thursdays
16 09 * * 4 cd /home/ubuntu/smart-api && node dist/run-algo.js >> logs/algo_run.log 2>&1
20-55/5 09 * * 4 cd /home/ubuntu/smart-api && node dist/run-algo.js >> logs/algo_run.log 2>&1
*/5 10-14 * * 4 cd /home/ubuntu/smart-api && node dist/run-algo.js >> logs/algo_run.log 2>&1
0-40/5 15 * * 4 cd /home/ubuntu/smart-api && node dist/run-algo.js >> logs/algo_run.log 2>&1Run with hot-reloading:
pnpm run devCompile and start:
pnpm run build
pnpm startRun the comprehensive test suite:
pnpm test
pnpm run test:coverage # Generate coverage reportRun live-fidelity option chain backtests for NIFTY or SENSEX:
# Default NIFTY backtest (Tuesdays, lot size 65, strike diff 50/100, hedge Β±500)
node scripts/backtest-straddle.mjs --expiry-days-only --from 2026-02-01 --to 2026-08-03
# SENSEX backtest (Thursdays, lot size 20, strike diff 200/300, hedge Β±1500)
node scripts/backtest-straddle.mjs --index sensex --expiry-days-only --data-dir ../nifty-optionchain-data/data/chains-sensexNote: The chains-sensex data directory must be populated by the option chain collector prior to running SENSEX backtests.
The algorithm generates daily date-wise log files (app-YYYY-MM-DD.log and mtm-YYYY-MM-DD.log). To delete logs older than 30 days, run:
node scripts/clean-logs.jsPerformance analyses and P&L breakdowns for each weekly expiry date are documented in the expiry-reports/ directory formatted as expiry-<INDEX>-YYYY-MM-DD.md (e.g. expiry-NIFTY-2026-08-18.md or expiry-SENSEX-2026-08-20.md).
Each report highlights:
- P&L Summary: Net Premium, Realised vs Unrealised P&L, and winner/loser counts.
- Position Breakdown: Status (LONG/SHORT/CLOSED) and individual P&L for each traded contract.
- Straddle Pair Analysis: Grouped CE/PE strike performance.
The repository includes an offline backtester for the Short Straddle at ATM strategy. It reads the unified option-chain snapshots produced by the nifty-optionchain-data pipeline (data/chains/YYYY-MM-DD/YYYY-MM-DD_HHmm.json) and replays each trading session.
For every (trading day, expiry) session, the backtester:
- Enters: at
>= ENTRY_TIMEIST (default09:15, deployed09:30), buys 5-lot hedges (ATM+500CE &ATM-500PE for NIFTY;ATM+1500CE &ATM-1500PE for SENSEX), and sells 1-lot ATM straddle. - Rolls: on subsequent ticks, if
|ATM - nearestTradedSellStrike| >= strikeDiff(NIFTY: 50 when India VIX < 14, 100 otherwise; SENSEX: 200 when India VIX < 14, 300 otherwise), sells a new ATM straddle (or missing leg if LTP > 5). - Exits:
- Per-leg 125% Stop Loss: trigger =
entry * 2.25. Checked per leg on every snapshot. Fills atmax(slLimitPrice, currentLtp) + slSlippage. - Close rule (
EXIT_TIME+): sell legs withLTP > 5are bought back at market; sell legs withLTP <= 5and all long hedges expire worthless.
- Per-leg 125% Stop Loss: trigger =
By default only the nearest expiry is traded per day (--expiries all to trade every expiry present in the data).
# Point at the option-chain data lake
pnpm backtest -- --data-dir ../nifty-optionchain-data/data/chains
# Or set it once via environment
export OPTIONCHAIN_DATA_DIR=../nifty-optionchain-data/data/chains
pnpm backtest| Flag | Default | Description |
|---|---|---|
--data-dir <path> |
auto-detected | Path to the data/chains directory. |
--index nifty|sensex |
nifty |
Index profile to select strategy parameters (lot size, hedge variance, strike differences, expiry day). |
--from YYYY-MM-DD / --to YYYY-MM-DD |
all | Restrict the backtest date range. |
--entry HHMM |
0915 |
Session entry time (IST); reads ENTRY_TIME environment variable if set. |
--close HHMM |
1517 |
Session close time (IST) when sell legs > 5 LTP are bought back; reads EXIT_TIME environment variable if set. |
--strike-diff N / --vix <14|>=14 |
profile default | Strike step for rolling ATM entries (NIFTY: 50/100; SENSEX: 200/300). |
--entry-slippage N |
0 |
Premium points deducted from sell entries / added to buy hedges for open execution slippage. |
--sl-slippage N |
0 |
Additional premium points added to SL limit fill price on stop loss triggers. |
--lot-size N |
profile default | Index lot size used for P&L in rupees (65 for NIFTY, 20 for SENSEX). |
--expiries nearest |
nearest |
nearest (one per day) or all (every expiry present). |
--json <file> |
β | Also write full per-session + position details to a JSON file. |
- Stop Loss Fills: SL trigger is
entry * 2.25; limit order is placed atentry * 2.25 * 1.05. On trigger, fills occur atmax(slLimitPrice, currentLtp) + slSlippageto model limit execution and gap-through slippage. - Expiry Settlement: At
EXIT_TIME+ (Phase A), sell legs withLTP > 5are bought back at market price. Any legs remaining open are settled at the final snapshot LTP of the day (~15:40, CAS market close) in Phase B (SETTLED_ITMifLTP > 5, elseEXPIRED_WORTHLESS). - Entry Execution: Initial entries use the
ENTRY_TIME(default09:15, deployed09:30) chain LTP. Live market fills at09:16or09:31can differ during volatile opens;--entry-slippageallows configuring realistic entry slippage.
Date Expiry ATM strike Traded Strikes Positions P&L (βΉ)
2026-05-04 2026-05-05 24150 24150,24200,24250,24300 10 -βΉ2,223.00
2026-07-01 2026-07-07 23950 23950 4 +βΉ0.00
Note: Non-expiry session rows in the example output above originate from a full-range backtest run without the --expiry-days-only filter.
The summary block reports total P&L, win rate, average per session, profit factor, and maximum drawdown. Trades are simulated in index points β no live brokerage fees are modeled.
The application supports remote monitoring and control via both Telegram and Slack.
In your .env file, configure the following:
- Telegram: Set
USE_TELEGRAM=true,TELEGRAM_BOT_TOKEN, andTELEGRAM_CHAT_ID. - Slack: Set
USE_SLACK=true,SLACK_WEBHOOK_URL(for outbound notifications), andSLACK_SIGNING_SECRET(to verify slash commands).
Point your Slack App's Slash Commands endpoint to: https://<your-domain>/api/slack/commands.
These commands can be sent as messages on Telegram or run as slash commands in Slack (e.g., /status):
| Command / Slack Slash | Description |
|---|---|
/status or /check |
Get current algo status (Running/Stopped) and trading mode. |
/paperon |
Enable Paper Trading Mode (trades are mocked locally). |
/paperoff |
Enable Live Trading Mode (trades execute on your broker account). |
/logs |
Retrieve the last 20 lines of application logs from local log files in logs/. |
/kill |
Sets the kill switch (.kill file) to stop the algo from placing new trades. |
/resume / /start |
Clear the kill switch to allow the algo to resume operations. |
The project is configured for automated deployment to Oracle Cloud via GitHub Actions.
The .github/workflows/ci.yml workflow triggers on every push to the development branch. It performs the following on the target server:
- Builds the project (
babeltranspilation) and runs lint, typecheck, and test checks. - Copies build artifacts to the Oracle Cloud instance via SCP.
- Generates the
.envfile from GitHub Secrets (including API credentials, notification tokens, andENTRY_TIME/EXIT_TIME/NO_ENTRY_AFTERstrategy timings). - Installs production dependencies.
Note: The trading algorithm runs via system crontab triggers (Tuesday for NIFTY, Thursday for SENSEX). PM2 on the server is used exclusively for hosting the MCP server (mcp-smart-api).
To use the deployment workflow, add the following secrets in your repository settings (Settings > Secrets and variables > Actions):
| Secret Name | Description |
|---|---|
ORACLE_HOST |
Public IP of your Oracle Cloud instance. |
ORACLE_USER |
SSH username (e.g., ubuntu). |
ORACLE_SSH_KEY |
Your private SSH key (.key or .pem content). |
PORT |
The port the app should run on (default: 3000). |
API_KEY |
Your SmartAPI Key. |
CLIENT_CODE |
Your SmartAPI Client Code. |
CLIENT_PIN |
Your SmartAPI Client Pin. |
CLIENT_TOTP_PIN |
Your 16-character TOTP Secret Key. |
USE_TELEGRAM |
Set to true to enable Telegram notifications. |
TELEGRAM_BOT_TOKEN |
Your Telegram Bot Token. |
TELEGRAM_CHAT_ID |
Your Telegram Chat ID. |
USE_SLACK |
Set to true to enable Slack notifications. |
SLACK_WEBHOOK_URL |
Your Slack Webhook URL. |
SLACK_SIGNING_SECRET |
Your Slack App Signing Secret. |
ENTRY_TIME |
Intraday strategy entry time (e.g. 09:15). |
EXIT_TIME |
Intraday strategy exit time (e.g. 15:17). |
NO_ENTRY_AFTER |
Intraday strategy no-entry cutoff time (e.g. 15:10). |
smart-api/
βββ src/
β βββ helpers/
β β βββ apiService/ # Domain-specific API logic (positions, orders, strategy)
β β βββ ... # Utility helpers (logger, notifier, etc.)
β βββ store/ # In-memory state management
β βββ routes/ # API Endpoints
β βββ app.ts # Express application configuration
βββ __tests__/ # High-coverage test suite
βββ jest.config.js # Testing configuration
For AI developers using AI agents (like Antigravity), workspace customization skills are configured under .agents/skills/:
- gh-pr-workflow: Automates branching, staging, committing (Conventional Commits), pushing, and opening GitHub Pull Requests.
- git-cleanup-sync: Cleans up local feature branches, switches back to
development, and pulls the latest changes. - pr-description-check: Validates PR descriptions to ensure paths, commands, and code snippets are wrapped in backticks (e.g., `src/app.ts`).
- readme-auto-update: Automates and verifies updating the
README.mdfile whenever core application changes are made. - verify-pr-status: Watches and verifies that all GitHub PR checks complete and pass successfully before concluding a PR lifecycle.
Detailed documentation for all endpoints (Algo control, Account info, Market data) can be found in ENDPOINTS.md.
Trading in the stock market involves significant risk. This algorithm is provided for educational and demonstration purposes. Kunal and the contributors are not responsible for any financial losses incurred through the use of this software. Always backtest thoroughly and trade responsibly.
Contributions are welcome! Please ensure that any new features include corresponding unit tests and maintain the existing branch coverage standards (>= 80%).
Developed with β€οΈ by Kunal
Full-stack Developer | Stock Market Trader