Placing & Managing Orders
Every order type - market, limit, SL, smart, basket, split - plus modify and cancel.
- ·placeorder() types
- ·placesmartorder()
- ·basket & split orders
- ·modify / cancel
- ·Order & position books
- ·Close all positions
Up to now you've been a spectator: pulling quotes, charting history, building signals. This is the chapter where you finally pick up the remote control and act. Placing an order is the moment a strategy stops being an idea on a screen and becomes a position in the market - and it's also the moment mistakes start costing money. So we're going to be careful, methodical, and we're going to do every single thing in analyze mode, where orders are simulated and nothing actually trades.
By the end you'll know how to send every order type the SDK supports - market, limit, stop-loss, smart, basket and split - how to modify and cancel them, how to read back what happened from your order, trade and position books, and how to flatten everything with a single call. These are the exact building blocks every bot in the rest of the series uses to execute.
Everything below was run on a server in analyze mode, so the responses are realistic but simulated. Before you ever run order code yourself, confirm the mode - the very first example does exactly that. Going live should be a deliberate choice, never an accident.
Always check the mode first
A professional pilot runs a pre-flight checklist before every takeoff. Yours is one line: am I in analyze mode or live mode? client.analyzerstatus() answers it. Get into the habit of running this before any order session - it's the cheapest insurance you'll ever buy.
# Before placing any order, confirm the server is in analyze mode (simulated).
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
status = client.analyzerstatus()
data = status["data"]
print(f"Mode : {data['mode']}")
print(f"Analyze mode: {data['analyze_mode']}")
if data["analyze_mode"]:
print("Safe to practise: every order below is simulated, nothing trades.")
else:
print("WARNING: live mode is on. Orders would hit the real exchange.")Mode : analyze Analyze mode: True Safe to practise: every order below is simulated, nothing trades.
The anatomy of an order
Every order you place answers the same handful of questions. Learn these five and you can place anything:
- symbol and exchange - what you're trading and where (
RELIANCEonNSE,GOLDM03JUL26FUTonMCX). - action -
BUYorSELL. - price_type - how you want to be filled:
MARKET,LIMIT,SL, orSL-M. - product - what kind of position:
CNC(delivery - you hold the shares),NRML(futures & options carried overnight), orMIS(intraday, auto-squared-off the same day). - quantity - how many shares or how many units (for F&O, a whole number of the lot size).
In the OpenAlgo Python SDK, placeorder, placesmartorder and splitorder all take the price type as price_type= (with the underscore). The exception is basketorder: each leg is a dictionary that uses the key pricetype (no underscore) instead - a small inconsistency that trips people up, so watch for it. Across the platform: products are CNC / NRML / MIS, actions are BUY / SELL, and price types are MARKET / LIMIT / SL / SL-M.
Market orders: fill me now
A market order says "I don't care about the exact price, just get me in (or out) right now." It fills almost instantly at whatever price the market is offering. It's the right tool when being in the trade matters more than the exact price - for example, exiting a losing position fast. The response hands you back an order id (your receipt) and a status.
# A MARKET order: buy 1 RELIANCE for delivery (CNC) at the best available price.
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
# price_type=MARKET means "fill me now at whatever the market is".
# product=CNC means delivery (you intend to hold the shares).
response = client.placeorder(
strategy="Chapter25",
symbol="RELIANCE",
exchange="NSE",
action="BUY",
price_type="MARKET",
product="CNC",
quantity=1,
)
print(response)
print(f"Status : {response['status']}")
print(f"Order id: {response.get('orderid')}"){'mode': 'analyze', 'orderid': '26062323464338', 'status': 'success'}
Status : success
Order id: 26062323464338NSE and BSE equities stop trading at 15:30 IST, and MIS (intraday) orders are rejected after the broker's square-off time. That's why this example uses CNC (delivery). When you want to practise intraday-style orders in the evening, switch to an MCX commodity - those trade well into the night.
Limit orders: fill me at my price or better
A limit order sets the worst price you'll accept. A BUY limit at 700 means "buy, but never pay more than 700." If the market is above your price, the order simply rests as pending until the market comes to you (or you cancel it). Limits give you price control at the cost of certainty - you might never get filled.
# A LIMIT order: buy SBIN only if the price reaches your chosen level or better.
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
# A LIMIT order needs a price. We bid below the market so it rests as a pending
# order instead of filling instantly. trigger_price is 0 (not a stop order).
response = client.placeorder(
strategy="Chapter25",
symbol="SBIN",
exchange="NSE",
action="BUY",
price_type="LIMIT",
product="CNC",
quantity=1,
price=700,
trigger_price=0,
)
print(f"Status : {response['status']}")
print(f"Order id: {response.get('orderid')} (resting at 700)")Status : success Order id: 26062385309963 (resting at 700)
Stop orders: protect the downside
This is the order type that keeps you in business. A stop order sits dormant until the price hits a trigger, then springs to life to get you out of a trade that's going wrong.
- SL (Stop-Loss Limit) - when the trigger is hit, a limit order is sent. You control the price but risk not filling in a fast move.
- SL-M (Stop-Loss Market) - when the trigger is hit, a market order is sent. Guaranteed to fill, but at whatever price is available.
Here we attach both to F&O positions: an SL on a NIFTY future and an SL-M on a gold future.
# Stop orders that protect a position: SL (stop-limit) and SL-M (stop-market).
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
# SL = Stop-Loss LIMIT. When price hits trigger_price (24820), a SELL LIMIT at
# price (24800) is sent. Use on an NFO future (NRML = overnight carry).
sl = client.placeorder(
strategy="Chapter25", symbol="NIFTY30JUN26FUT", exchange="NFO",
action="SELL", price_type="SL", product="NRML",
quantity=65, price=24800, trigger_price=24820,
)
print(f"SL order id: {sl.get('orderid')} status: {sl['status']}")
# SL-M = Stop-Loss MARKET. Only a trigger is needed; once hit it fills at market.
slm = client.placeorder(
strategy="Chapter25", symbol="GOLDM03JUL26FUT", exchange="MCX",
action="SELL", price_type="SL-M", product="NRML",
quantity=1, trigger_price=140000,
)
print(f"SL-M order id: {slm.get('orderid')} status: {slm['status']}")SL order id: 26062344429557 status: success SL-M order id: 26062352256073 status: success
Remember the price rules. MARKET: no price needed. LIMIT: needs a price. SL: needs both a price (the limit) and a trigger_price. SL-M: needs only a trigger_price.
Smart orders: trade to a target, not a quantity
Here's a subtle but powerful idea. A normal order adds a fixed quantity. A smart order (placesmartorder) instead drives your position to an absolute target size you specify with position_size. OpenAlgo looks at what you currently hold and sends only the difference.
Why does that matter? Because bots fire repeatedly. If your signal says "be long 5 lots" and you're already long 3, a smart order quietly buys just 2 more - it never doubles you up by mistake. Set position_size=0 and it flattens you. It's the cleanest way to keep a live position exactly where your strategy wants it.
# placesmartorder() drives your position to a TARGET size, not a fixed quantity.
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
# position_size is the absolute target holding you want to end up with.
# OpenAlgo checks your current position and only sends the difference.
# Here: target 5 long. If you hold 0, it buys 5; if you hold 3, it buys 2.
response = client.placesmartorder(
strategy="Chapter25",
symbol="RELIANCE",
exchange="NSE",
action="BUY",
price_type="MARKET",
product="CNC",
quantity=1,
position_size=5,
)
print(f"Status : {response['status']}")
print(f"Order id: {response.get('orderid')}")
print("Smart orders keep you AT a target size instead of adding blindly.")Status : success Order id: 26062320063210 Smart orders keep you AT a target size instead of adding blindly.
Basket orders: many orders, one call
A basket order fires a whole list of different orders in a single request. Perfect for a pairs trade (long one stock, short another), or for entering a multi-stock portfolio at once. Each item in the list is its own little dictionary with the same fields you already know.
# A basket order fires several different orders in one call (a long/short pair).
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
# Each basket leg is its own dictionary, and basket legs use the key "pricetype"
# (no underscore) -- unlike placeorder and splitorder, which use price_type=.
orders = [
{"symbol": "RELIANCE", "exchange": "NSE", "action": "BUY",
"quantity": 1, "pricetype": "MARKET", "product": "CNC"},
{"symbol": "INFY", "exchange": "NSE", "action": "SELL",
"quantity": 1, "pricetype": "MARKET", "product": "CNC"},
]
response = client.basketorder(orders=orders)
print(f"Basket status: {response['status']}")
for leg in response["results"]:
print(f" {leg['symbol']:10s} -> {leg['status']} id {leg['orderid']}")Basket status: success RELIANCE -> success id 26062381789276 INFY -> success id 26062384311086
Split orders: one big order, many small fills
A split order takes one large quantity and chops it into smaller child orders automatically. Two reasons to do this: very large F&O orders can exceed the exchange's freeze quantity (the maximum size allowed in one order), and breaking a big order into pieces reduces your market impact - the tendency of a single huge order to move the price against you. Give it a total quantity and a splitsize, and it does the arithmetic.
# A split order chops one big order into smaller child orders automatically.
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
# Buy 5 lots of GOLDM but in chunks of 2 -> orders of 2, 2, 1. Useful for large
# F&O orders that exceed the exchange freeze limit, or to reduce market impact.
response = client.splitorder(
symbol="GOLDM03JUL26FUT",
exchange="MCX",
action="BUY",
quantity=5,
splitsize=2,
price_type="MARKET",
product="NRML",
)
print(f"Status: {response['status']} total {response['total_quantity']} in chunks of {response['split_size']}")
for leg in response["results"]:
print(f" order {leg['order_num']}: qty {leg['quantity']} id {leg['orderid']}")Status: success total 5 in chunks of 2 order 1: qty 2 id 26062343652068 order 2: qty 2 id 26062346057578 order 3: qty 1 id 26062348347937
Modifying and cancelling
A resting limit or stop order isn't set in stone. As long as it hasn't filled, you can modify its price or quantity, or cancel it entirely. Modifying needs the order id plus the full order details (with the changed value); cancelling needs only the id. Here's the whole life of one order - placed, repriced, then pulled.
# The full life of a resting order: place it, modify the price, then cancel it.
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
# 1) Place a LIMIT order that rests away from the market.
placed = client.placeorder(
strategy="Chapter25", symbol="SBIN", exchange="NSE", action="BUY",
price_type="LIMIT", product="CNC", quantity=1, price=700,
)
order_id = placed["orderid"]
print(f"Placed : {order_id} at 700")
# 2) Modify it: raise the limit price to 710. Re-send the order's details.
modified = client.modifyorder(
order_id=order_id, strategy="Chapter25", symbol="SBIN", exchange="NSE",
action="BUY", price_type="LIMIT", product="CNC", quantity=1, price=710,
)
print(f"Modified: {modified['status']} -> new price 710")
# 3) Changed your mind? Cancel it by id.
cancelled = client.cancelorder(order_id=order_id, strategy="Chapter25")
print(f"Cancelled: {cancelled['status']}")Placed : 26062309011793 at 700 Modified: success -> new price 710 Cancelled: success
You can only modify or cancel an order that is still open/pending. Once an order is complete, cancelled or rejected, it's frozen - there's nothing left to change. You also cannot flip an order's action (BUY to SELL) by modifying; cancel it and place a fresh one instead.
Reading back what happened
Placing orders is only half the job; a real bot also reads the market's reply. Three books tell you everything:
- Order book (
orderbook()) - every order you sent today and its current state (complete, open, rejected...). Itsdatais a dictionary holding anorderslist plus astatisticssummary. - Trade book (
tradebook()) - only the orders that actually filled, with their fill prices. Itsdatais a plain list. - Position book (
positionbook()) - what you currently hold, with live P&L. Also a plain list.
# Read the order book: every order you sent today and a summary of their states.
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
book = client.orderbook()
data = book["data"]
orders = data["orders"]
stats = data["statistics"]
print(f"Orders today: {len(orders)}")
print(f" completed: {stats['total_completed_orders']:.0f} "
f"open: {stats['total_open_orders']:.0f} "
f"rejected: {stats['total_rejected_orders']:.0f}")
# Show the five most recent orders.
for o in orders[:5]:
print(f" {o['action']:4s} {o['symbol']:16s} {o['pricetype']:7s} "
f"qty {o['quantity']} -> {o['order_status']}")Orders today: 192 completed: 171 open: 3 rejected: 1 BUY SBIN LIMIT qty 1 -> cancelled BUY GOLDM03JUL26FUT MARKET qty 1 -> complete BUY GOLDM03JUL26FUT MARKET qty 2 -> complete BUY GOLDM03JUL26FUT MARKET qty 2 -> complete SELL INFY MARKET qty 1 -> complete
# Trade book = what actually FILLED. Position book = what you currently HOLD.
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
# Both books return their data as a list of dictionaries.
trades = client.tradebook()["data"]
print(f"Trades filled today: {len(trades)}")
for t in trades[:3]:
print(f" {t['action']:4s} {t['symbol']:16s} @ {t['average_price']}")
positions = client.positionbook()["data"]
open_pos = [p for p in positions if int(float(p["quantity"])) != 0]
print(f"\nOpen positions: {len(open_pos)}")
for p in open_pos[:5]:
print(f" {p['symbol']:16s} qty {p['quantity']:>4} ltp {p['ltp']} pnl {p['pnl']}")Trades filled today: 171 BUY GOLDM03JUL26FUT @ 144360.0 BUY GOLDM03JUL26FUT @ 144360.0 BUY GOLDM03JUL26FUT @ 144360.0 Open positions: 9 RELIANCE qty 6 ltp 1309.5 pnl 0.0 INFY qty -1 ltp 1029.3 pnl 0.0 SBIN qty 1 ltp 1024.2 pnl 0.0 GOLDM03JUL26FUT qty 1 ltp 144336.0 pnl -293.01 GOLDM03JUL26FUT qty 5 ltp 144336.0 pnl -1076.0
Notice the shapes differ: orderbook() returns data as a dictionary (orders + statistics), while tradebook() and positionbook() return data as a plain list. When in doubt, print the response once and look before you reach into it - a habit that saves a lot of KeyError surprises.
The panic buttons
Finally, two calls every trader should know cold. cancelallorder() wipes out every pending order in one shot. closeposition() squares off every open position with market orders - a SELL for each long, a BUY for each short. Together they're your emergency exit and your end-of-day flatten.
# The two "panic buttons": cancel every pending order and flatten every position.
import os
from openalgo import api
client = api(
api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
# cancelallorder() removes every open/pending order in one shot.
cancelled = client.cancelallorder(strategy="Chapter25")
print(f"Cancel all : {cancelled['status']} - {cancelled.get('message')}")
# closeposition() squares off every open position with market orders
# (a SELL for longs, a BUY for shorts). Your end-of-day flatten button.
closed = client.closeposition(strategy="Chapter25")
print(f"Close all : {closed['status']} - {closed.get('message')}")
print("Use these for an emergency exit or a clean end-of-day square-off.")Cancel all : success - Canceled 3 orders. Failed to cancel 0 orders. Close all : success - Closed 9 positions Use these for an emergency exit or a clean end-of-day square-off.
closeposition() is deliberately blunt: no confirmation, no selectivity - it flattens everything. That's exactly what you want in an emergency or at the closing bell, but never wire it to a hair-trigger. For closing one specific position, place a single counter order instead.
Try it yourself
- Run example 1, then place a market order and immediately read the order book - find your order in the list.
- Change the smart-order
position_sizeto 0 and predict what it should do before you run it. - Build a basket that buys two stocks you follow and shorts a third, then read the position book to confirm all three legs.
Recap
- Always confirm analyze mode before sending orders - it's a one-line pre-flight check.
- An order is five answers: symbol+exchange, action, price_type, product, quantity.
placeorder,placesmartorderandsplitorderuseprice_type=; onlybasketorderlegs use the keypricetype(no underscore). - MARKET fills now, LIMIT fills at your price or better, SL/SL-M are stops that wake on a trigger to protect you.
- Smart orders trade to an absolute target size; basket fires many orders at once; split chops one big order into safe chunks.
- You can modify or cancel only orders that are still open; read results from the order, trade and position books.
cancelallorder()andcloseposition()are your panic buttons - powerful, blunt, and best used deliberately.
You can now place and manage any order through code. Next we change gears completely: before risking a rupee on these orders, we'll prove a strategy actually has an edge - by building a backtest from scratch and then with VectorBT.