Read the unified strategy source code at /home/ubuntu/bots/Claude_bot/unified_strategy.py and extract the full entry/exit logic. I need to understand:
1. What SMMA crossover conditions trigger an entry (BUY/SELL)?
2. What RSI gap filter conditions are applied?
3. What are the exit conditions (SL, RSI exit, SMMA crossover exit, EOD square-off)?
4. What is the stop-loss / risk management logic?
5. How does MANUAL_EXIT flag work?
6. What lot size / quantity is used per symbol?
7. What are the exact symbol names and exchanges?
Also read /home/ubuntu/bots/Claude_bot/config.py to understand:
- Broker config and symbol mappings
- Strategy parameters (SMMA period, RSI periods, etc.)
Output a clear summary of all the entry/exit rules and parameters.
2026-04-28 17:33:23
ASSISTANT (hermes, glm-5.1)
2026-04-28 17:33:23
TOOL (hermes, glm-5.1)
{"content": " 1|#!/usr/bin/env python3\n 2|\"\"\"\n 3|╔═══════════════════════════════════════════════════════════════════════════════╗\n 4|║ CLAUDE_BOT — RSI + MONDAY SMMA STRATEGY ║\n 5|║ ═════════════════════════════════════════ ║\n 6|║ Strategy: SMMA(2) on 5-min candles vs fixed weekly SMMA + RSI gap filter ║\n 7|║ ║\n 8|║ ENTRY (dual trigger — either fires the trade): ║\n 9|║ • Trigger A: SMMA2(5-min) crosses weekly SMMA + RSI gap > 3 → BUY CALL/PUT ║\n 10|║ • Trigger B: RSI crosses direction + SMMA2 already on same side + gap > 3 ║\n 11|║ (handles case where SMMA2 crossed first with stale RSI, or RSI leads) ║\n 12|║ ║\n 13|║ EXIT: ║\n 14|║ • SMMA2 crosses back (priority) → exit, reentry threshold resets to 3 ║\n 15|║ • RSI gap ≤ 3 → exit, reentry threshold set to 4 (rsi_exit_pending) ║\n 16|║ ║\n 17|║ RE-ENTRY after RSI-gap exit: SMMA2 same side + gap > 4 at 15-min close ║\n 18|║ MANUAL exit detected: requires fresh SMMA2 crossover (gap > 3) ║\n 19|║ ║\n 20|║ Timing: SMMA2 on 5-min candle closes | RSI(15m) refresh every 15min ║\n 21|║ Friday 3:28 PM → close all positions ║\n 22|║ All orders: LIMIT + NRML only (MARKET/MIS not allowed on NFO) ║\n 23|╚═══════════════════════════════════════════════════════════════════════════════╝\n 24|\"\"\"\n 25|\n 26|# ─── Memory shim (MUST be first for VPS) ───\n 27|import os\n 28|os.environ['NUMBA_DISABLE_JIT'] = '1'\n 29|os.environ['NUMBA_CACHE_DIR'] = '/tmp/numba_cache'\n 30|\n 31|import datetime\n 32|import time\n 33|import threading\n 34|import json\n 35|import sys\n 36|import logging\n 37|import pathlib\n 38|\n 39|from openalgo import api\n 40|\n 41|# Our modules\n 42|from config import (\n 43| BROKERS, PROFILES, TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID,\n 44| TELEGRAM_HEARTBEAT_INTERVAL, TRADING_START_TIME, TRADING_END_TIME,\n 45| FRIDAY_SQUAREOFF_TIME, POLL_INTERVAL, RECONCILE_INTERVAL,\n 46| ORDER_VERIFY_RETRIES, ORDER_VERIFY_DELAY, WARMUP_DAYS,\n 47|)\n 48|from telegram_bot import TelegramBot\n 49|\n 50|# ═══════════════════════════════════════════════════════════════════════════════\n 51|# 📝 LOGGING\n 52|# ═══════════════════════════════════════════════════════════════════════════════\n 53|_script_dir = pathlib.Path(__file__).resol
... [27511 chars total, truncated]
{"content": " 501|\n 502| def verify_order_filled(self, symbol, expected_qty, action):\n 503| for attempt in range(ORDER_VERIFY_RETRIES):\n 504| time.sleep(ORDER_VERIFY_DELAY)\n 505| positions = self.get_broker_positions()\n 506| if positions is None:\n 507| continue\n 508| actual_qty = positions.get(symbol, 0)\n 509| if action == \"BUY\" and actual_qty >= expected_qty:\n 510| return True\n 511| if action == \"SELL\" and actual_qty == 0:\n 512| return True\n 513| self.log.info(f\"Verify attempt {attempt+1}/{ORDER_VERIFY_RETRIES}: got qty={actual_qty}\")\n 514| return False\n 515|\n 516| # ─────────────────────────────────────────\n 517| # RECONCILIATION\n 518| # ─────────────────────────────────────────\n 519|\n 520| def reconcile(self):\n 521| \"\"\"Reconcile bot state with broker.\"\"\"\n 522| positions = self.get_broker_positions()\n 523| if positions is None:\n 524| return\n 525|\n 526| my_sym = self.state[\"current_symbol\"]\n 527|\n 528| if my_sym:\n 529| actual_qty = positions.get(my_sym, 0)\n 530| if actual_qty == 0:\n 531| self.log.warning(f\"MISMATCH: Bot has {my_sym}, broker has NONE → resetting (manual exit detected)\")\n 532| self.telegram.alert(\n 533| f\"🚨 *{self.name}*: Position mismatch! `{my_sym}` not found — manual exit detected. Resetting.\"\n 534| )\n 535| self.state[\"current_symbol\"] = None\n 536| self.state[\"current_state\"] = self.STATE_WAITING\n 537| self.state[\"option_entry_price\"] = 0.0\n 538| self.manual_exit_detected = True\n 539| self.smma_crossover_pending = False\n 540| self.smma_crossover_pending_side = None\n 541| self.emergency_covered = False\n 542| self.emergency_cover_symbol = None\n 543| self.emergency_alert_time = None\n 544| self.save_persistent_state()\n 545| else:\n 546| self.log.info(f\"VERIFIED: {my_sym} qty={actual_qty}\")\n 547| else:\n 548| unexpected = {sym: qty for sym, qty in positions.items() if qty != 0}\n 549| if unexpected:\n 550| for sym, qty in unexpected.items():\n 551| if self.emergency_covered and sym == self.emergency_cover_symbol:\n 552| self.log.info(f\"NOTE: Emergency cover position {sym} qty={qty} — awaiting manual close\")\n 553| else:\n 554| self.log.warning(f\"UNEXPECTED position {sym} qty={qty} — not tracked by bot!\")\n 555| self.t
... [26813 chars total, truncated]
2026-04-28 17:33:23
ASSISTANT (hermes, glm-5.1)
2026-04-28 17:33:23
TOOL (hermes, glm-5.1)
{"content": " 1001| start_date = end_date - datetime.timedelta(days=WARMUP_DAYS + 7) # +7 to cover weekends\n 1002| hist = self.client.history(\n 1003| symbol=self.profile[\"symbol_fut\"],\n 1004| exchange=self.profile[\"exchange\"],\n 1005| interval=\"15m\",\n 1006| start_date=start_date.strftime(\"%Y-%m-%d\"),\n 1007| end_date=end_date.strftime(\"%Y-%m-%d\"),\n 1008| )\n 1009|\n 1010| def _parse_ts_open(h):\n 1011| \"\"\"Parse response into list of (timestamp, open_price) tuples.\"\"\"\n 1012| rows = []\n 1013| if hasattr(h, 'iterrows'):\n 1014| for idx, row in h.iterrows():\n 1015| try:\n 1016| ts = idx.timestamp() if hasattr(idx, 'timestamp') else float(idx)\n 1017| except Exception:\n 1018| ts = 0\n 1019| rows.append((ts, float(row.get(\"open\", 0))))\n 1020| elif isinstance(h, dict) and \"data\" in h:\n 1021| raw = h[\"data\"]\n 1022| if isinstance(raw, list):\n 1023| for c in raw:\n 1024| if isinstance(c, list) and len(c) >= 2:\n 1025| rows.append((float(c[0]), float(c[1])))\n 1026| elif isinstance(c, dict):\n 1027| rows.append((\n 1028| float(c.get(\"timestamp\", c.get(\"time\", 0))),\n 1029| float(c.get(\"open\", 0)),\n 1030| ))\n 1031| elif isinstance(raw, dict) and \"candles\" in raw:\n 1032| for c in raw[\"candles\"]:\n 1033| if isinstance(c, list) and len(c) >= 2:\n 1034| rows.append((float(c[0]), float(c[1])))\n 1035| elif isinstance(h, dict) and \"candles\" in h:\n 1036| for c in h[\"candles\"]:\n 1037| if isinstance(c, list) and len(c) >= 2:\n 1038| rows.append((float(c[0]), float(c[1])))\n 1039| elif isinstance(c, dict):\n 1040| rows.append((\n 1041| float(c.get(\"timestamp\", c.get(\"time\", 0))),\n 1042| float(c.get(\"open\", 0)),\n 1043| ))\n 1044| elif isinstance(h, list):\n 1045| for c in h:\n 1046| if isinstance(c, dict):\n 1047| rows.append((\n 1048| float(c.get(\"timestamp\", c.get(\"time\", 0))),\n 1049| float(c.get(\"open\"
... [28300 chars total, truncated]
2026-04-28 17:33:23
ASSISTANT (hermes, glm-5.1)
2026-04-28 17:33:23
TOOL (hermes, glm-5.1)
{"content": " 1501| # rsi_exit_futures_price kept — updated on next RSI gap exit if it fires\n 1502| self.save_persistent_state()\n 1503|\n 1504| # ── Phase 3: Trigger B — RSI direction crossover → fresh entry ───────\n 1505| new_rsi_state = \"ABOVE\" if self.current_rsi20 > self.current_rsi120 else \"BELOW\"\n 1506| old_rsi_state = self.prev_rsi_state\n 1507| rsi_just_crossed = (old_rsi_state is not None and new_rsi_state != old_rsi_state)\n 1508| self.prev_rsi_state = new_rsi_state\n 1509| if rsi_just_crossed:\n 1510| self.save_persistent_state()\n 1511|\n 1512| if (rsi_just_crossed\n 1513| and self.state[\"current_state\"] == self.STATE_WAITING\n 1514| and not self.manual_exit_detected\n 1515| and self.monday_smma is not None\n 1516| and self.smma2_value is not None):\n 1517| smma2 = self.smma2_value\n 1518| current_smma_side = \"ABOVE\" if smma2 > self.monday_smma else \"BELOW\"\n 1519| gap, gap_ok = self._rsi_directional_gap(new_rsi_state, RSI_ENTRY_GAP)\n 1520| self.log.info(\n 1521| f\"RSI CROSSOVER {old_rsi_state}→{new_rsi_state} | \"\n 1522| f\"SMMA2_side={current_smma_side} | gap={gap:.1f} > {RSI_ENTRY_GAP}: {gap_ok}\"\n 1523| )\n 1524| if current_smma_side == new_rsi_state and gap_ok:\n 1525| side = \"BUY_CALL\" if new_rsi_state == \"ABOVE\" else \"BUY_PUT\"\n 1526| self.log.info(f\"Trigger B: RSI crossover + SMMA2 agrees ({current_smma_side}) → {side}\")\n 1527| self.smma_crossover_pending = False\n 1528| self.smma_crossover_pending_side = None\n 1529| # Fresh entry via Trigger B — reset RSI re-entry counter\n 1530| self.rsi_reentry_count = 0\n 1531| self.rsi_exit_pending = False\n 1532| self.rsi_exit_futures_price = None\n 1533| self.rsi_exit_direction = None\n 1534| self.enter_position(side, ltp)\n 1535| return\n 1536|\n 1537| # ── Phase 4: Pending SMMA2 crossover — wait for RSI gap ──────────────\n 1538| if not self.smma_crossover_pending or self.smma_crossover_pending_side is None:\n 1539| return\n 1540|\n 1541| pending_side = self.smma_crossover_pending_side\n 1542| smma2 = self.smma2_value if self.smma2_value is not None else 0\n 1543| current_smma_side = \"ABOVE\" if smma2 > (self.monday_smma or 0) else \"BELOW\"\n 1544|\n 1545| # If SMMA2 has since flipped, cancel pending\n 1546| if current_smma_side != pending_side:\n 1547| self.smma_crossover_pending = False\n 1548| self.smma_crossover_pending_side = None\n 1549| self
... [28642 chars total, truncated]
2026-04-28 17:33:23
ASSISTANT (hermes, glm-5.1)
2026-04-28 17:33:23
TOOL (hermes, glm-5.1)
{"content": " 2001| and self.current_rsi20 is not None\n 2002| and self.current_rsi120 is not None):\n 2003| smma2_val = self.smma2_value if self.smma2_value else ltp\n 2004| smma_side = \"ABOVE\" if smma2_val > self.monday_smma else \"BELOW\"\n 2005| rsi_gap_call = (self.current_rsi20 - self.current_rsi120)\n 2006| rsi_gap_put = (self.current_rsi120 - self.current_rsi20)\n 2007| both_bullish = (smma_side == \"ABOVE\" and rsi_gap_call > RSI_ENTRY_GAP)\n 2008| both_bearish = (smma_side == \"BELOW\" and rsi_gap_put > RSI_ENTRY_GAP)\n 2009| self.log.info(\n 2010| f\"STARTUP (Mon/late-RSI): LTP={ltp:.2f} SMMA={self.monday_smma:.2f} \"\n 2011| f\"smma_side={smma_side} gap_call={rsi_gap_call:.1f} gap_put={rsi_gap_put:.1f}\"\n 2012| )\n 2013| if both_bullish and self.state[\"current_state\"] == self.STATE_WAITING:\n 2014| self.enter_position(\"BUY_CALL\", ltp)\n 2015| elif both_bearish and self.state[\"current_state\"] == self.STATE_WAITING:\n 2016| self.enter_position(\"BUY_PUT\", ltp)\n 2017| else:\n 2018| self.log.info(f\"STARTUP (Mon/late-RSI): Conditions not aligned — waiting\")\n 2019| self.monday_alignment_done = True\n 2020| self.save_persistent_state()\n 2021|\n 2022| last_candle_fetch = now\n 2023|\n 2024| # Heartbeat log\n 2025| rsi20_str = f\"{self.current_rsi20:.1f}\" if self.current_rsi20 else \"--\"\n 2026| rsi120_str = f\"{self.current_rsi120:.1f}\" if self.current_rsi120 else \"--\"\n 2027| smma_confirmed = (self.smma_week_number == now.isocalendar()[1])\n 2028| smma_str = (f\"{self.monday_smma:.2f}⚠\" if self.monday_smma and not smma_confirmed\n 2029| else f\"{self.monday_smma:.2f}\" if self.monday_smma\n 2030| else \"--\")\n 2031| smma2_str = f\"{self.smma2_value:.3f}\" if self.smma2_value else \"--\"\n 2032| pos = self.state[\"current_symbol\"] or \"NONE\"\n 2033| smma2_side = (\n 2034| \"↑\" if (self.smma2_value and self.monday_smma and self.smma2_value > self.monday_smma)\n 2035| else \"↓\" if (self.smma2_value and self.monday_smma)\n 2036| else \"?\"\n 2037| )\n 2038|
... [15447 chars total, truncated]
2026-04-28 17:33:23
ASSISTANT (hermes, glm-5.1)
Here is the complete summary of the entry/exit logic and parameters extracted from the source code:
---
## STRATEGY OVERVIEW
**Name:** RSI + Monday SMMA Strategy (v4)
**Symbol:** Trades options (CE/PE) on NSE futures underlyings
**Order type:** LIMIT orders only, NRML product type, NFO exchange
---
## 1. SMMA CROSSOVER ENTRY CONDITIONS
There are **two independent triggers** (either can fire a trade):
**Trigger A — SMMA2 Crossover (checked on 5-min candle close):**
- SMMA(2) is calculated on 5-min futures candle closes (rolling Wilder-style smoothing)
- The "line in stone" is the **Monday SMMA(2)** calculated from daily OPEN prices, locked every Monday at 9:16 AM for the entire week
- A **crossover** is detected when SMMA(2) on 5-min crosses from BELOW to ABOVE the Monday SMMA (bullish), or ABOVE to BELOW (bearish)
- If crossover occurs AND RSI directional gap > 3 → immediate BUY_CALL (above) or BUY_PUT (below)
- If crossover occurs but RSI gap <= 3 AND RSI is already in the same direction → sets `smma_crossover_pending=True`, waits for RSI gap to widen (checked on both 5-min and 15-min cycles)
- If crossover occurs but RSI gap <= 3 AND RSI is NOT in same direction → no pending set, entry is blocked
**Trigger B — RSI Direction Crossover (checked on 15-min candle close):**
- When RSI20(closes of LOW prices) crosses RSI120(closes of HIGH prices) from one side to the other
- AND SMMA(2) is already on the **same side** as the new RSI direction
- AND RSI directional gap > 3
- → BUY_CALL (if ABOVE) or BUY_PUT (if BELOW)
**Monday Startup Alignment:**
- On Monday at startup, if both SMMA2 and RSI are already aligned (SMMA2 side matches, gap > 3), the bot auto-enters without waiting for a fresh crossover
- If holding the wrong direction (e.g. bearish but now bullish aligned), it closes and reverses
---
## 2. RSI GAP FILTER CONDITIONS
**RSI Calculation:**
- RSI(20) computed on 15-min candle LOW prices (fast momentum)
- RSI(120) computed on 15-min candle HIGH prices (slow trend)
- Both use Wilder smoothing (exponential)
**Directional Gap:**
- For ABOVE (bullish): gap = RSI20 - RSI120
- For BELOW (bearish): gap = RSI120 - RSI20
**Thresholds:**
- **Entry (fresh):** gap must be > **3** (`RSI_ENTRY_GAP = 3`)
- **RSI gap exit:** gap drops to <= 3 (see below)
- **RSI re-entry after RSI exit:** gap must be > **4** (stricter)
- **SL re-entry:** gap must be > **3** (same as entry)
---
## 3. EXIT CONDITIONS
**A. SMMA2 Cross-Back Exit (highest priority when in position):**
- Checked on every 5-min candle close
- If SMMA(2) crosses to the **wrong side** of Monday SMMA while in position (e.g., holding BULLISH but SMMA2 drops below Monday SMMA) → exit immediately
- On exit: clears `manual_exit_detected`, clears `rsi_exit_pending`, resets `rsi_reentry_count` is NOT reset (stays as is)
- Re-entry after this exit requires a **fresh SMMA2 crossover** (gap > 3)
**B. RSI Gap Exit:**
- Checked on every 15-min candle close while in position
- If directional RSI gap drops to <= 3 → exit
- Sets `rsi_exit_pending=True`, records `rsi_exit_futures_price` (futures LTP at exit time) and direction
- Re-entry threshold is raised to gap > 4 (see re-entry below)
- Max 2 re-entries per original SMMA2 crossover entry
**C. Stop-Loss Exit:**
- Checked **every poll cycle** (60 sec) while in position
- If unrealized P&L < -INR 2200 (`STOP_LOSS_AMOUNT = 2200`) → exit
- Sets `sl_exit_pending=True` with direction
- SL re-entry fires **at most once** per session (`sl_reentry_used` flag)
**D. Friday EOD Square-Off:**
- Every Friday at 15:28 (`FRIDAY_SQUAREOFF_TIME = 15:28`), any open position is closed with reason "friday_eod"
**E. Manual Telegram Square-Off:**
- `/squareoff <name>` or `/squareoff ALL` closes positions immediately
- Sets `manual_exit_detected = True`
---
## 4. STOP-LOSS / RISK MANAGEMENT LOGIC
- **Stop-loss amount:** INR 2,200 per position (`STOP_LOSS_AMOUNT = 2200`)
- Calculated as: `(current_option_LTP - option_entry_price) * quantity`
- If P&L drops below -2200, position is exited cleanly
- After SL exit, the bot watches for **SL re-entry**: if RSI gap re-expands > 3, SMMA2 still on correct side, and RSI hasn't flipped → re-enter (once per session max)
- If RSI flips or SMMA2 crosses wrong side after SL exit, SL re-entry is cancelled
- **2-lot cap:** Before entry, bot checks total positions for the underlying. If combined qty >= lot_size * 2, entry is blocked
- **Max trades per day:** 10 per profile
- **Post-exit cooldown:** Currently set to **0 minutes** (`POST_EXIT_COOLDOWN_MINUTES = 0`), effectively disabled
- **Whipsaw threshold:** 7 minutes (`WHIPSAW_THRESHOLD_MINUTES = 7`) — but cooldown is 0, so it has no effect currently
- **Emergency cover:** If an exit order fails to confirm, the bot buys an opposite-direction option as a hedge, then sends Telegram alerts for manual resolution
---
## 5. MANUAL_EXIT FLAG
- `manual_exit_detected` is set to `True` when the **reconcile()** call discovers the bot thinks it has a position but the broker shows qty=0 (i.e., someone manually closed it)
- Also set on Telegram `/squareoff`
- When `manual_exit_detected = True`:
- **RSI gap re-entry is blocked** (won't auto-re-enter)
- **SL re-entry is blocked**
- The bot requires a **fresh SMMA2 crossover** (gap > 3) to unlock trading again
- When a fresh SMMA2 crossover fires, `manual_exit_detected` is cleared to `False`
---
## 6. LOT SIZE / QUANTITY PER SYMBOL
| Profile | lot_size | max_lots | Quantity (lot_size x max_lots) |
|---|---|---|---|
| DLF_SMMA | 825 | 1 | **825** |
| HAL_SMMA | 150 | 1 | **150** |
| INFY_SMMA | 400 | 1 | **400** |
| INDUSINDBK_SMMA | 700 | 1 | **700** |
All profiles use `max_lots = 1`. Combined position cap is 2 * lot_size per underlying.
Option strike selection: **1 ITM** (`itm_offset = 1`) for all profiles.
---
## 7. EXACT SYMBOL NAMES AND EXCHANGES
| Profile | Broker | Futures Symbol | Underlying | Exchange | Expiry |
|---|---|---|---|---|---|
| DLF_SMMA | FLATTRADE | DLF28APR26FUT | DLF | NFO | 28APR26 |
| HAL_SMMA | FLATTRADE | HAL28APR26FUT | HAL | NFO | 28APR26 |
| INFY_SMMA | SHOONYA | INFY28APR26FUT | INFY | NFO | 28APR26 |
| INDUSINDBK_SMMA | SHOONYA | INDUSINDBK28APR26FUT | INDUSINDBK | NFO | 28APR26 |
Note: HAL_SMMA is **disabled** (`"enabled": False` in config).
---
## RE-ENTRY LOGIC SUMMARY
| Exit Type | Re-entry Condition | Re-entry Threshold | Max Re-entries | Additional Checks |
|---|---|---|---|---|
| SMMA2 cross-back | Needs fresh SMMA2 crossover | gap > 3 | N/A (new sequence) | Resets rsi_reentry_count = 0 |
| RSI gap exit | SMMA2 same side + gap widened + better price | gap > 4 | 2 per SMMA2 entry | Futures price must be more favorable than exit price (higher for CALL, lower for PUT) |
| Stop-loss exit | SMMA2 same side + gap > 3 | gap > 3 | 1 per session | RSI must not have flipped |
| Manual exit | Needs fresh SMMA2 crossover | gap > 3 | N/A | Clears manual_exit_detected flag |
---
## KEY STRATEGY PARAMETERS (constants)
| Parameter | Value | Description |
|---|---|---|
| `SMMA2_PERIOD` | 2 | SMMA period on 5-min closes |
| `RSI_ENTRY_GAP` | 3 | Min RSI directional gap for entry |
| RSI exit gap | 3 (implicit) | Exit when gap <= 3 |
| RSI re-entry gap | 4 (hardcoded) | Re-entry after RSI gap exit needs gap > 4 |
| `STOP_LOSS_AMOUNT` | 2200 | INR stop-loss threshold |
| `LIMIT_BUFFER` | 3.0 | INR buffer on LIMIT orders (buy: LTP+3, sell: LTP-3) |
| `POLL_INTERVAL` | 60 sec | Main loop polling interval |
| `SMMA2_CANDLE_INTERVAL` | 300 sec | 5-min candle interval |
| `CANDLE_FETCH_INTERVAL` | 300 sec | How often to fetch new candles |
| `CANDLE_DAYS_LOOKBACK` | 20 | Calendar days of history |
| `MIN_CANDLES_FOR_RSI` | 122 | Minimum candles needed for RSI(120) calculation |
| `WARMUP_DAYS` | 15 | Daily candle lookback for Monday SMMA |
| `POST_EXIT_COOLDOWN_MINUTES` | 0 | Currently disabled |
| `WHIPSAW_THRESHOLD_MINUTES` | 7 | Reference only (no effect with cooldown=0) |
| `TRADING_START_TIME` | 09:16 | Market open |
| `TRADING_END_TIME` | 15:30 | Market close |
| `FRIDAY_SQUAREOFF_TIME` | 15:28 | Friday forced close |
---
## BROKER CONFIG
- **SHOONYA**: OpenAlgo host at `https://shoonya.openalgo.theworkpc.com`
- **FLATTRADE**: OpenAlgo host at `https://flattrade.openalgo.theworkpc.com`
- Both use API keys defined in config (redacted in this summary)