bhardwajaditya/trader-in-broker

Exécuteur d'ordres pour les actions indiennes (NSE, BSE). Transforme les ordres validés par /trader-in-risk en tickets réels pour Zerodha Kite (par défaut) ou Upstox, et ne transmet jamais rien sans votre accord explicite.

Qu'est-ce que trader-in-broker ?

Ce skill agit comme le dernier rempart avant l'envoi d'un ordre réel sur les marchés indiens (NSE, BSE). Il est conçu pour fonctionner avec les courtiers Zerodha Kite (par défaut) et Upstox. Son fonctionnement est extrêmement strict : un ordre n'est exécuté qu'après avoir passé le validateur de risques /trader-in-risk, que le mode 'paper' est désactivé, que l'utilisateur a tapé 'oui' explicitement dans le chat, et qu'il a cliqué sur 'Autoriser une fois' sur la carte de permission de Claude pour chaque appel. Comme les courtiers indiens ne proposent plus d'ordres 'bracket', chaque trade est décomposé en trois ordres : un achat d'entrée, un stop-loss au marché (SL-M) et une limite de target. Une attention particulière est portée au fait que les jambes stop et target ne sont pas liées (pas d'OCO), ce qui signifie qu'elles doivent être gérées avec soin pour éviter des positions courtes non désirées. Le mode 'paper' (par défaut) utilise un registre local simulé avec P&L réalisé après déduction des coûts estimés (courtage, STT, timbre fiscal, GST), car les courtiers indiens n'offrent pas de compte de démonstration. En mode live, le skill construit également l'ordre de vente pour clôturer un trade MIS avant 15h10 IST, avant le 'square-off' automatique du courtier vers 15h20. Il affiche le JSON exact pour l'outil MCP 'place_order' de Kite ou le corps de la requête Upstox, ainsi qu'une estimation des coûts et de la perte maximale au stop, le tout en roupies.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/bhardwajaditya/day-trader-skill-india/tree/main/trader-in-broker

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait trader-in-broker ?

The Order Executor is the only skill in this pack that can lead to a real order, so it is the strictest. An order reaches the market only after these gates, every time, for every order:

  1. trader-in-risk passed this exact order today (matched by fingerprint; the same ORDER_FIELDS and hash as trader-in-risk/check.py).
  2. "paper": false in trader-in/config.json. With paper (the default) nothing is ever sent; fills go to a local simulated ledger.
  3. The user typed an explicit yes for this one order in the chat.
  4. The user clicked Allow once on Claude's own permission card for each place_order call, because order tools are set to "Needs approval".

No yes, no order. A yes covers one ticket (its buy and its stop and target legs), never a batch.

${CLAUDE_SKILL_DIR} below is this skill's folder (where this SKILL.md lives).

python3 "${CLAUDE_SKILL_DIR}/ticket.py" show                      # every ticket, READY or not
python3 "${CLAUDE_SKILL_DIR}/ticket.py" approve --index 1 --reply "yes"
python3 "${CLAUDE_SKILL_DIR}/ticket.py" paper-fill --index 1 --price 1196.30           # paper: the buy
python3 "${CLAUDE_SKILL_DIR}/ticket.py" paper-fill --symbol RELIANCE --side sell --qty 12 --price 1212.70 --leg target
python3 "${CLAUDE_SKILL_DIR}/ticket.py" paper                     # the paper ledger and P&L
python3 "${CLAUDE_SKILL_DIR}/ticket.py" result --index 1 --leg entry --status placed --broker-id ID --filled-qty 12 --filled-price 1196.30
python3 "${CLAUDE_SKILL_DIR}/ticket.py" result --index 1 --leg stop --status placed --broker-id ID
python3 "${CLAUDE_SKILL_DIR}/ticket.py" fill --symbol RELIANCE --side sell --qty 12 --price 1212.70 --leg target
python3 "${CLAUDE_SKILL_DIR}/ticket.py" flatten --symbol RELIANCE --qty 12
python3 "${CLAUDE_SKILL_DIR}/ticket.py" log

ticket.py never talks to a broker. It renders tickets, checks that a reply is a real yes for that order, keeps the paper ledger, builds the flatten ticket, and keeps the log in trader-in/state/broker.json. --now "2026-10-27 10:15" (or bare 10:15) pins the clock in IST. Symbols are accepted as RELIANCE, RELIANCE.NS, NSE:RELIANCE, M&M, BSE:500325, 500325.BO.

What it reads and writes

filewhat
trader-in/orders.jsonthe proposed orders (trader-in-budget writes it; flatten replaces it)
trader-in/config.jsonbroker (kite or upstox), paper, product, flat_by, costs, capital_inr
trader-in/state/risk.jsontrader-in-risk's verdicts; only a PASS from today is READY
trader-in/state/broker.jsonevery yes, no, placed, leg placed and filled, with each entry's max loss incl. costs
trader-in/state/paper.jsonthe paper ledger: cash, open positions, closed trades, realised P&L after est. costs
trader-in/portfolio.jsoncash and shares (positions keyed by symbol like RELIANCE.NS, with exchange)
trader-in/plan.jsonthe flat-by time for flatten
trader-in/universe.json(Upstox) ISINs from the scanner, if present
trader-in/state/EQUITY_L.csv(Upstox) NSE's equity list, downloaded once when an ISIN is needed

One trade, three orders (read this before every bracket)

trader-in-budget writes one "bracket" (entry, stop, target). Zerodha and Upstox no longer offer bracket orders for this, so the ticket turns it into:

legorderwhen
1BUY, LIMIT at the entry (or MARKET), MIS or CNCnow
2SELL, SL-M with trigger_price = the stoponly after leg 1 has filled
3SELL, LIMIT at the targetonly after leg 1 has filled

Legs 2 and 3 are NOT linked (no OCO). When one fills, the other is still live. Cancel it at once, or it can fill too and leave the user short (for MIS) or be rejected (for CNC). /trader-in-alerts can watch the stop and target levels and ring when either is hit. Placed before leg 1 fills, legs 2 and 3 are plain sell orders and can open a short.

Every ticket shows the estimated costs (spec §6: brokerage min(₹20, 0.03%) per MIS leg, STT, exchange and SEBI charges, stamp duty, 18% GST) and the max loss at the stop including the round-trip costs. That same figure is logged for trader-in-risk's daily loss limit. Costs are always an estimate: the contract note has the real ones.

Paper (the default)

Indian brokers have no paper account, so paper is a local ledger in trader-in/state/paper.json. Nothing is sent anywhere.

  1. ticket.py show, one READY ticket, the user's yes, ticket.py approve.
  2. When the price trades at the entry, ticket.py paper-fill --index N (the limit price) or --price P. A buy limit cannot fill above its limit.
  3. When the stop or the target trades, record that ONE sell: paper-fill --symbol RELIANCE --side sell --qty 12 --price 1188.10 --leg stop. The ledger books gross P&L, the estimated round-trip costs and the net.
  4. ticket.py paper prints cash, open positions and realised P&L.

The paper entry is logged as placed (with "paper": true), so trader-in-risk counts it against the trades-per-day and daily-loss limits exactly as a live one.

Setup A: Zerodha Kite (official MCP server) — the default

Zerodha's server is github.com/zerodha/kite-mcp-server (MIT, Go). Checked against its source and README on 2026-10-27 (latest commit 5e044dc, tag v0.4.0-dev24).

The hosted server cannot place orders. The README says the hosted https://mcp.kite.trade/mcp "excludes potentially destructive trading operations for security", and Zerodha's own pages describe it as read-only (one Z-Connect post says GTT orders were the exception; the scope has changed over time, so check with Claude's tool list after connecting). It is free and needs no API key: the login tool returns a Kite link, the user logs in with their own Kite password and 2FA in the browser, never in the chat.

Hosted, read-only (quotes, holdings, positions, margins, orders, history):

  • Claude Desktop: Settings > Developer > Edit Config, add the "kite" block from claude_desktop_config.example.json (it runs npx mcp-remote https://mcp.kite.trade/mcp; needs Node.js), restart.
  • Claude Code: claude mcp add --transport http kite https://mcp.kite.trade/mcp (or the same npx mcp-remote command as a stdio server: claude mcp add kite -- npx mcp-remote https://mcp.kite.trade/mcp).

To place orders through Claude, the user runs their own copy of the server with their own Kite Connect app:

  1. Create a Kite Connect app at developers.kite.trade (check the current price there). Its redirect URL must point at the local server's callback; in the server source the callback path is /callback on APP_HOST:APP_PORT (default http://localhost:8080/callback). We did not test this end to end.
  2. From 1 April 2026 Zerodha's support pages say order placement through the API needs a static IP whitelisted with Zerodha (NSE/SEBI retail-algo rules). A home connection without a static IP may get orders rejected. Check Zerodha's current rule before going live.
  3. Build it: git clone https://github.com/zerodha/kite-mcp-server && cd kite-mcp-server && go build -o kite-mcp-server (Go 1.21+).
  4. Add the "kite-orders" block from claude_desktop_config.example.json: the full path to the binary, APP_MODE stdio, and the user types KITE_API_KEY and KITE_API_SECRET into that file themselves. In Claude Code: claude mcp add kite-orders -e APP_MODE=stdio -e KITE_API_KEY="$KITE_API_KEY" -e KITE_API_SECRET="$KITE_API_SECRET" -- /full/path/to/kite-mcp-server with the values exported in the user's own shell first. Keep only one of kite / kite-orders if you do not need both.
  5. Restart, ask Claude to call login, the user opens the link and logs in. Kite sessions expire daily (Kite Connect access tokens last until the next morning), so expect to log in again each trading day.

Keys and tokens never go in the chat. Never ask for them, never read the config file back into the conversation. If the user pastes a key or token, tell them to delete that message and regenerate it at developers.kite.trade.

Tool permissions. Customize > Connectors (Claude Desktop), open the Kite server, set:

  • place_order, modify_order, cancel_order, place_gtt_order, modify_gtt_order, delete_gtt_order: Needs approval. Never Always allow.
  • read-only tools (get_profile, get_margins, get_holdings, get_positions, get_orders, get_order_history, get_order_trades, get_trades, get_gtts, get_quotes, get_ltp, get_ohlc, get_historical_data, search_instruments, login): Always allow is fine.
  • In Claude Code, keep them on ask in .claude/settings.json: "permissions": {"ask": ["mcp__kite-orders__place_order", "mcp__kite-orders__modify_order", "mcp__kite-orders__cancel_order"]}; never put an order tool in allow. The server also takes EXCLUDED_TOOLS=place_order,modify_order,cancel_order for a read-only copy.

place_order arguments (from the server source): variety (regular), exchange (NSE/BSE), tradingsymbol, transaction_type (BUY/SELL), quantity (int), product (MIS/CNC), order_type (MARKET/LIMIT/SL/SL-M), price (LIMIT, SL), trigger_price (SL, SL-M), validity (DAY), optional tag (≤ 20 chars; the ticket adds trin + 12 hex of the fingerprint so the order can be found later). The server's tool has no market_protection argument; NSE now applies market protection to API MARKET and SL-M orders, so a market order may be converted to a protected limit by the broker. Limit entries avoid this. For BSE, Kite's tradingsymbol is the scrip name, not the numeric code: look it up with search_instruments.

Setup B: Upstox ("broker": "upstox")

  • Upstox's official MCP server (https://mcp.upstox.com/mcp, installed from the upstox/upstox-plugin-marketplace plugin upstox-mcp) is read-only: Upstox's page says you cannot place orders through it. Connections expire daily (OAuth consent in the browser).
  • Upstox's official agent skill (/plugin install upstox-skill@upstox-plugins-official in Claude Code, after /plugin marketplace add upstox/upstox-plugin-marketplace) can place orders with the v3 order API. It reads a daily access token from the UPSTOX_ACCESS_TOKEN environment variable (set by the user in their own shell, never in chat), shows its own preview and waits for confirmation. Upstox says it does not run in plain Claude Desktop chat.
  • Or the user places the order in the Upstox app from the ticket.

The ticket prints the body for POST https://api-hft.upstox.com/v3/order/place (v2 /v2/order/place is marked deprecated; it takes the same fields minus slice): quantity (int), product (I intraday = MIS, D delivery = CNC), validity DAY, price (0 for MARKET / SL-M), instrument_token, order_type, transaction_type, disclosed_quantity 0, trigger_price (0 unless SL / SL-M), is_amo false (Upstox ignores it), tag, and market_protection: -1 (automatic) on MARKET and SL-M, since 0 is rejected for API market orders.

instrument_token is NSE_EQ|<ISIN> (or BSE_EQ|<ISIN>). The ISIN comes from, in order: --isin, an isin in the order, trader-in/universe.json from the scanner, else NSE's EQUITY_L.csv (downloaded once with a browser User-Agent to trader-in/state/EQUITY_L.csv, archives. then nsearchives.). BSE limitation: the NSE list only maps NSE symbols. A BSE scrip code like 500325.BO cannot be mapped, so pass --isin; for a .BO name symbol the NSE listing's ISIN is used (same company), but check it. We did not verify the Upstox instruments file format here. With no ISIN a live Upstox ticket is not READY; paper tickets do not need one.

Steps: the entry

  1. ticket.py show. Only READY tickets go to the user. A ticket that is NOT READY or BLOCKED goes back to /trader-in-risk (it has no broker JSON).
  2. Show ONE ticket, including the not-OCO warning, and ask: "Place this order? Reply yes or no." One order per question. Never offer "yes to all".
  3. ticket.py approve --index N --reply "<the user's reply, word for word>". "Not recorded" means the reply was not an explicit yes for this order ("sure", "haan", "yes to all", a yes naming another stock or amount). Ask again. Never turn a reply into a yes yourself.
  4. Paper: paper-fill as above. Live, Kite: call place_order with exactly the leg 1 JSON; the user clicks Allow once. Confirm the fill (get_order_history / get_positions), then place_order leg 2, then leg 3, each with its own Allow once. Live, Upstox: hand the bodies to the Upstox agent skill or the app, one leg at a time, same order.
  5. Record each live leg: ticket.py result --index N --leg entry|stop|target --status placed|denied|failed --broker-id <id> (--filled-qty, --filled-price on the entry). Only the entry counts as an order for trader-in-risk; the legs are logged as leg_placed. A failed order is not retried without a new check and a new yes.

Steps: while it is open, and out by flat_by

  • /trader-in-alerts watches the stop, the target and the flat-by time.
  • When the stop or the target fills: live, ticket.py fill ... --leg stop (it reminds you to cancel the other leg now: get_orders, then cancel_order, Needs approval); paper, paper-fill ... --leg stop.
  • By flat_by (15:10 IST default), anything still open gets closed. Brokers auto square-off open MIS positions from about 15:20 IST and charge a fee for it (Zerodha lists ₹50 + GST per order), so do not leave it to them:
    1. Live: ask the broker how many shares are held: get_positions (read only).
    2. ticket.py flatten --symbol RELIANCE --qty 12 writes a MARKET sell (same product) to trader-in/orders.json.
    3. /trader-in-risk. A sell that only closes what the user holds is never blocked by the daily limits or the entry window.
    4. Live: first cancel the open SL-M stop and LIMIT target for that stock (get_orders, cancel_order each). Otherwise either can fill after the flatten and make the user short.
    5. ticket.py show, the user's yes, then place_order (live) or paper-fill --index 1 --price P (paper), then result.

Example

ORDER 1 of 1, PAPER (local simulated ledger, nothing is sent to Zerodha Kite), Zerodha Kite
  BUY 12 RELIANCE (NSE), MIS intraday, limit 1196.30, good for today
  stop-loss 1188.10 (SL-M sell, placed after the buy fills)
  target 1212.70 (LIMIT sell, placed after the buy fills)
  est. costs ₹15.16 round trip at the stop, ₹15.35 at the target (estimate)
  max loss about ₹113.56 if the stop fills at 1188.10 (₹98.40 + ₹15.16 est. costs; it can fill worse in a fast market)
  READY: trader-in-risk PASS, waiting for your yes
  Leg 1, the entry: place_order with exactly:
    {"variety": "regular", "exchange": "NSE", "tradingsymbol": "RELIANCE", "transaction_type": "BUY",
     "quantity": 12, "product": "MIS", "order_type": "LIMIT", "validity": "DAY", "price": 1196.3, "tag": "trin..."}
  Leg 2, the stop-loss, ONLY after leg 1 fills: place_order with exactly:
    {... "transaction_type": "SELL", "order_type": "SL-M", "trigger_price": 1188.1 ...}
  Leg 3, the target, ONLY after leg 1 fills: place_order with exactly:
    {... "transaction_type": "SELL", "order_type": "LIMIT", "price": 1212.7 ...}
  WARNING: the stop and the target are two separate orders, NOT linked (no OCO). ...
  MIS is intraday: be flat by 15:10 IST. Brokers auto square-off open MIS positions from about 15:20 IST ...

The why and the plan stay in the file; they are never sent.

What it never does

  • Never places an order without every gate, and never two tickets on one yes.
  • Never places orders from a scheduled or background task: nobody is there to say yes.
  • Never asks for, reads, stores or repeats API keys, secrets, access tokens, passwords or TOTPs.
  • Never switches "paper" to false. The user does that themselves.
  • Never places the stop or target leg before the entry has filled.
  • Never places F&O, options, short sales or index orders (^NSEI, ^BSESN are context only).
  • Never promises a fill price: a market order and an SL-M fill at whatever the market gives.

Safety

  • Education and tooling, not investment advice. A PASS is a rules check, not a recommendation. For advice, talk to a SEBI-registered investment adviser.
  • Paper by default ("paper": true, a local simulated ledger). Going live is the user's own decision, in their own config.
  • Nothing in this pack places an order except trader-in-broker, and only when all hold: trader-in-risk passed that exact order today, "paper": false, the user typed an explicit yes for that one order in the chat, and the user clicked Allow once on Claude's own card because the order tool is set to "Needs approval". Never set an order tool to "Always allow".
  • The stop and target are not linked at Indian brokers: cancel the other one the moment one fills.
  • Costs on the ticket are estimates; MIS positions are auto squared off around 15:20 IST for a fee.
  • Broker keys and tokens live in the user's own config file or environment, never in the chat.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

bhardwajaditya/trader-in-alerts

Price Alerts for Indian stocks (NSE, BSE). Rings when a price, a stop or the opening bell hits. Turns the trade plan into three alerts in one go (the stop, the target and "flat by" at your flat-by time, 15:10 IST by default, before brokers square off MIS at about 15:20), plus above / below / stop / target levels in rupees, percent moves from the previous close for stocks and for Nifty, Bank Nifty or Sensex, any time of day, the 09:15 IST opening bell and the 09:00 pre-open, each ringing once so you are not spammed. Reminds you that the stop and target are separate orders at Zerodha, Upstox and other Indian brokers (no OCO for MIS): when one fills, cancel the other. Knows NSE hours in IST with the 2026 NSE holidays, the Budget-day and Muhurat special sessions, fetches free delayed Yahoo quotes for .NS / .BO tickers one at a time, and writes trader-in/prices.json (INR) for sizing and the risk check. Use when the user says "alert me when", "tell me if Reliance hits 1,200", "watch my stop", "watch my trade",…

bhardwajaditya/trader-in-news

News Monitor for Indian stocks (NSE / BSE). Tracks breaking news on every stock you watch and flags what moves it: Yahoo Finance and Google News (India edition) headlines, each company's exchange corporate announcements on NSE with a BSE fallback (financial results, board meeting outcomes, dividends, buybacks, acquisitions, credit ratings, KMP resignations and appointments, insider trading / SAST, promoter pledges, fund raising) and how far each stock is from the previous close in rupees. Shows only what is new since the last check, newest first, in IST, tags each item (earnings, analyst, deal, capital, people, halt, regulatory, macro...) and never repeats one. Use when the user says "any news on Reliance", "why is TCS moving", "news on my Nifty watchlist", "morning briefing", "pre-market news", "check NSE announcements", "any corporate announcements", "did HDFC Bank file anything", "monitor the news on my Indian stocks", or after /trader-in-scanner finds setups.

Skills associés