For the complete documentation index, see llms.txt. This page is also available as Markdown.

19 - PlaceOrder Call Flow

Overview

The PlaceOrder API is the core order execution endpoint in OpenAlgo. It handles order validation, authentication, broker routing, and response processing through multiple layers.

Complete Flow Diagram

┌──────────────────────────────────────────────────────────────────────────────┐
│                           PlaceOrder Complete Flow                           │
└──────────────────────────────────────────────────────────────────────────────┘

                             Client Request (JSON)


┌──────────────────────────────────────────────────────────────────────────────┐
│  Layer 1: REST resource   restx_api/place_order.py                           │
│  POST /api/v1/placeorder  ->  class PlaceOrder(Resource).post                │
│                                                                              │
│  @limiter.limit(ORDER_RATE_LIMIT)      10 per second by default              │
│  track_latency("PLACE")                wrapped onto the Resource at          │
│                                        startup by utils/latency_monitor.py   │
│                                                                              │
│  order_data = OrderSchema().load(request.json)                               │
│  ValidationError  ->  400 without reaching the service layer                 │
│  api_key = order_data.get("apikey")                                          │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│  Layer 2: services/place_order_service.py   place_order(...)                 │
│                                                                              │
│  Step 1  Action Center routing                                               │
│          should_route_to_pending(api_key, "placeorder")                      │
│          semi-auto  ->  queue_order(...) and return here                     │
│          auto       ->  continue                                             │
│                                                                              │
│  Step 2  validate_order_data(order_data)                                     │
│          REQUIRED_ORDER_FIELDS, VALID_EXCHANGES, VALID_ACTIONS,              │
│          VALID_PRICE_TYPES, VALID_PRODUCT_TYPES, then OrderSchema again      │
│          failure  ->  OrderFailedEvent, or AnalyzerErrorEvent in             │
│                       analyzer mode  ->  400                                 │
│                                                                              │
│  Step 3  get_auth_token_broker(api_key)      database/auth_db.py             │
│          None  ->  403 "Invalid openalgo apikey", deliberately not           │
│                    logged so a key-guessing loop cannot flood the DB         │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│  Layer 3: place_order_with_auth(...)   mode routing                          │
│                                                                              │
│  if get_analyze_mode():                database/settings_db.py               │
│      services/sandbox_service.py  ->  sandbox_place_order(...)               │
│      sandbox/order_manager.py     ->  OrderManager(user_id).place_order()    │
│      bus.publish(OrderPlacedEvent(mode="analyze", ...)) and return           │
│  else:                                                                       │
│      import_broker_module(broker_name)                                       │
│      importlib.import_module(f"broker.{broker_name}.api.order_api")          │
│      import failure  ->  OrderFailedEvent  ->  404                           │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│  Layer 4: broker plugin                                                      │
│  broker_module.place_order_api(order_data, auth_token)                       │
│                                                                              │
│  A  transform_data(data)     broker/<key>/mapping/transform_data.py          │
│     OpenAlgo field names  ->  broker field names                             │
│                                                                              │
│  B  get_br_symbol(symbol, exchange)      database/token_db.py                │
│     "SBIN" + "NSE"  ->  "SBIN-EQ" for Zerodha                                │
│                                                                              │
│  C  get_httpx_client().post(<broker order URL>, ...)                         │
│     shared pooled HTTP client, broker auth header                            │
│                                                                              │
│  D  return (response, response_data, order_id)                               │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│  Layer 5: result handling and fan-out                                        │
│                                                                              │
│  res.status == 200                   res.status != 200, or exception         │
│  bus.publish(                        bus.publish(                            │
│      OrderPlacedEvent(...))              OrderFailedEvent(...))              │
│                                                                              │
│  subscribers/__init__.py fans "order.placed" and "order.failed" out to       │
│  log, socketio, telegram and whatsapp subscribers on the EventBus pool.      │
│  log_subscriber writes order_logs (live) or analyzer_logs (analyze).         │
│                                                                              │
│  restx_api/place_order.py returns                                            │
│  make_response(jsonify(response_data), status_code)                          │
│  track_latency then writes order_latency off-thread.                         │
└──────────────────────────────────────────────────────────────────────────────┘

Request Format

Basic Order Request

Limit Order

Stop-Loss Order

Validation Rules

Mandatory Fields

Field
Type
Description

apikey

string

OpenAlgo API key

strategy

string

Strategy identifier

symbol

string

Trading symbol

exchange

string

Exchange code

action

string

BUY or SELL

quantity

integer

Order quantity (≥1)

Valid Values

utils/constants.py is the single source of truth:

Defaults when the field is omitted come from OrderSchema and utils/constants.py: pricetype defaults to MARKET, product to MIS, and price, trigger_price and disclosed_quantity to "0". quantity arrives as a float and is coerced to an integer by the schema's coerce_quantity post-load hook.

Order Routing Modes

Auto Mode (Default)

Orders are executed immediately without manual intervention.

Semi-Auto Mode

Orders require manual approval before execution.

Analyzer Mode (Sandbox)

When analyze_mode = True:

Broker Integration

Dynamic Module Loading

Broker-Specific Implementation

Each broker implements:

Error Handling

Error
HTTP Code
Raised in

Marshmallow ValidationError

400

restx_api/place_order.py, before the service is called

Missing field, invalid exchange, action, pricetype or product

400

validate_order_data() in the service

Sandbox call without apikey, or neither api_key nor auth_token plus broker

400

place_order() / place_order_with_auth()

Invalid or revoked API key

403

get_auth_token_broker() returned None; the message is Invalid openalgo apikey

Broker module import failed

404

import_broker_module(); the message is Broker-specific module not found

Broker rejected the order

broker status

place_order_with_auth() forwards res.status, falling back to 500

Unhandled exception

500

place_order_with_auth() or the resource's outer except

Rate limit

429

Flask-Limiter, handled by the @app.errorhandler(429) in app.py

The resource always returns make_response(jsonify(response_data), status_code), so the status code is whatever the service returned. Nothing in the resource rewrites a broker status into a generic 500.

Event Bus Side-Effects

All post-order side-effects (logging, Socket.IO, Telegram, WhatsApp) are dispatched through the Event Bus. The service publishes a single typed event; subscribers handle each concern independently.

Publishing

What Subscribers Do

subscribers/__init__.py subscribes four modules to both order.placed and order.failed:

Subscriber
Action

log_subscriber

_log_event routes on event.mode: analyze writes analyzer_logs via async_log_analyzer, anything else writes order_logs via async_log_order

socketio_subscriber

Emits the live order event or the analyzer update to the browser

telegram_subscriber

Sends the Telegram alert

whatsapp_subscriber

Sends the WhatsApp alert

bus.publish() never blocks the request thread. EventBus dispatches on a shared ThreadPoolExecutor(max_workers=10, thread_name_prefix="eventbus") with a bounded pending queue of 1000; over-capacity events are dropped with a warning rather than queued indefinitely.

See 53-event-bus for full architecture details.

Security Layers

API Key Verification

Request Sanitization

  • API keys removed from logs

  • Sensitive data encrypted at rest

  • Rate limiting per endpoint

Performance Optimizations

Optimization
Description

Connection pooling

HTTP clients reuse connections

API key caching

Reduce Argon2 hashing overhead

Async logging

Non-blocking order logs

Thread pool

10 worker threads for async ops

Key Files Reference

File
Purpose

restx_api/place_order.py

REST resource, rate limit, response shaping

restx_api/schemas.py

OrderSchema request validation

services/place_order_service.py

place_order, place_order_with_auth, validate_order_data, import_broker_module

services/order_router_service.py

Semi-auto routing (should_route_to_pending, queue_order)

services/sandbox_service.py

Analyzer-mode entry (sandbox_place_order)

sandbox/order_manager.py

OrderManager.place_order simulated execution

database/settings_db.py

get_analyze_mode() mode flag

database/auth_db.py

get_auth_token_broker() and the API key caches

broker/{name}/api/order_api.py

place_order_api(data, auth)

broker/{name}/mapping/transform_data.py

Data transformation

utils/event_bus.py

EventBus and the module-level bus singleton

events/order_events.py

OrderPlacedEvent (order.placed), OrderFailedEvent (order.failed)

subscribers/

Log, Socket.IO, Telegram and WhatsApp subscribers

database/apilog_db.py

order_logs writes from log_subscriber

utils/latency_monitor.py

track_latency("PLACE") wrapper and order_latency writes

Last updated