Signal-to-Order Pipeline¶
Poll a signal source and dry-run dispatch to an OMS.
Part 18 of 37 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).")
Keep signal generation and order dispatch as separate steps¶
It's tempting to write one function that computes an indicator and immediately places an order. Don't — collapsing "decide" and "act" into one step removes the only place you can intercept a bad decision before it becomes a live position.
The pattern below is deliberately three stages:
check_signal()— pure decision logic. Given data, it returns an intent (BUY/SELL/HOLD) and nothing else. It never touches a broker.- Risk checks — sit between the signal and the order (position sizing from notebook 15, a max-loss guard, a check that you're not already in this position). This is where a signal gets rejected even though it fired.
dispatch()— the only place an order is actually sent, and only after the first two stages agree.
DRY_RUN exists so you can run the full pipeline against live or historical data
and watch what it would do, with zero chance of a live fill — this is how you
catch a signal that fires every single bar (usually a bug, not an edge) before it
reaches a broker. That failure mode — a rule that looks profitable in a backtest
because it's actually just fitting noise in the sample it was tuned on — is called
overfitting, and a pipeline with a dry-run stage is one of the cheapest
defenses against shipping it.
import time
def check_signal():
"""Replace with your real signal source."""
return "BUY" # | "SELL" | "HOLD"
def dispatch(signal, oms=None, dry_run=True):
if signal == "HOLD":
return None
order = {"symbol": "NIFTY24800CE", "transaction_type": signal, "quantity": 75, "order_type": "MARKET", "product": "INTRADAY"}
if dry_run or oms is None:
print("[DRY RUN] would place:", order)
return order
return oms.place(order)
DRY_RUN = True # flip to False only once OMS + credentials are wired and tested end-to-end
for _ in range(3): # demo: 3 polls instead of an infinite loop
dispatch(check_signal(), dry_run=DRY_RUN)
time.sleep(1)
« Previous: Paper Trading Loop
Next: Capstone: End-to-End Algo Bot »
Try the concepts above interactively: Options Strategy Builder · Docs · Get your static IP