Capstone: End-to-End Algo Bot¶

Open In Colab

SDK + strategy template + risk sizing + dry-run OMS dispatch, combined.

Part 19 of 20 in the ServLoci algo/options trading notebook series — full index in notebooks/README.md.

Setup¶

# Get your dedicated static IPv6 + SOCKS5 credentials free:
#   https://comm.servloci.in/register        (or /auth/google?free=1 for an instant trial)
# Your api_key / api_secret pair shows up in the portal after signup:
#   https://comm.servloci.in/user
!pip install -q "requests[socks]"
!curl -sL https://comm.servloci.in/sdk/servloci.py -o servloci.py

import os
from servloci import ServLoci

SERVLOCI_API_KEY = os.environ.get("SERVLOCI_API_KEY", "dhan:1000000001")   # broker:client_id
SERVLOCI_API_SECRET = os.environ.get("SERVLOCI_API_SECRET", "")            # from the portal — leave blank to run this notebook in demo mode

sl = None
if SERVLOCI_API_SECRET:
    sl = ServLoci(api_key=SERVLOCI_API_KEY, api_secret=SERVLOCI_API_SECRET)
    print("ServLoci configured:", sl.host, sl.port)
else:
    print("SERVLOCI_API_SECRET not set — running in demo mode (no live proxy calls).")

Combines the SDK, an options template, risk-based sizing, and dry-run order dispatch into one runnable skeleton — the sum of notebooks 01, 10, 15, 16.

import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("servloci-bot")

DRY_RUN = True

def nearest_strike(spot, step):
    return round(spot / step) * step

def iron_condor(spot, step, qty=1):
    atm = nearest_strike(spot, step)
    return [
        {"side": "sell", "type": "CE", "strike": atm + 2 * step, "qty": qty},
        {"side": "buy", "type": "CE", "strike": atm + 4 * step, "qty": qty},
        {"side": "sell", "type": "PE", "strike": atm - 2 * step, "qty": qty},
        {"side": "buy", "type": "PE", "strike": atm - 4 * step, "qty": qty},
    ]

def position_size(capital, risk_pct, max_loss_per_lot):
    if max_loss_per_lot <= 0:
        return 0
    return max(int((capital * risk_pct) // max_loss_per_lot), 0)

def run_once(spot, capital=500_000, risk_pct=0.02, assumed_max_loss_per_lot=4500):
    lots = position_size(capital, risk_pct, assumed_max_loss_per_lot)
    if lots == 0:
        log.warning("position size is 0 lots at current risk budget — skipping")
        return
    legs = iron_condor(spot, step=50, qty=lots)
    log.info("built iron condor: %s", legs)

    if sl is None:
        log.info("[DRY RUN — no SERVLOCI_API_SECRET] would dispatch %d lot(s)", lots)
        return

    session = sl.session()
    # oms = OrderManager(session, base_url=os.environ["BROKER_API_BASE"])   # from notebook 16
    for leg in legs:
        order = {
            "symbol": f"NIFTY{leg['strike']}{leg['type']}",
            "transaction_type": "BUY" if leg["side"] == "buy" else "SELL",
            "quantity": leg["qty"] * 75,
            "order_type": "MARKET",
            "product": "INTRADAY",
        }
        if DRY_RUN:
            log.info("[DRY RUN] would place: %s", order)
        else:
            pass  # oms.place(order)

run_once(spot=24000)

That's the series. Recap: static IP (00) → SDK (01) → broker auth (02-05) → options math (06-10) → live data (11-12) → backtesting (13-14) → risk + execution (15-19).

Keep building at https://comm.servloci.in/tools/strategy-builder, or grab your own static IP at https://comm.servloci.in/register.


« Previous: Signal-to-Order Pipeline

Try the concepts above interactively: Options Strategy Builder · Docs · Get your static IP