Skip to main content

Fyers (Indian broker) adapter for NautilusTrader — REST + WebSocket bridge for NSE/BSE live data and execution

Project description

nautilus-fyers

A NautilusTrader-style adapter for Fyers (Indian broker, API v3), bridging Fyers REST + WebSocket APIs for live market data and execution on NSE/BSE instruments.

Built following the phased plan in fyers_nautilus_adapter_plan.md.

Architecture

Python layer  nautilus_fyers/   framework-neutral clients, config, auth
Rust core     crates/adapters/fyers/            wire models, enums, parsing, protocol state

The Python layer is fully functional standalone (stdlib only; websockets needed for live streaming). The Rust crate mirrors the NautilusTrader adapter crate layout (models/enums/parse per module, no async runtime dependency) so it can later be vendored into the nautilus_trader workspace and bound via PyO3.

Component Module Status
Instrument provider (symbol master, positional + header CSV) providers.py ✅ tested
OAuth2 auth flow + daily token refresh auth.py ✅ tested
REST client (quotes, depth, history, orders, positions, holdings, funds, tradebook) http.py ✅ tested
Rate limiting (token bucket, 10 req/s) + retries with backoff http.py ✅ tested
Data socket (lite/quote/full-depth modes, reconnect + subscription restore) transport.py, ws.py, data.py ✅ tested
Order socket (order/trade/position updates, replay dedup) transport.py, execution.py ✅ tested
Order lifecycle (submit/modify/cancel/cancel-all) + reconciliation reports execution.py ✅ tested
NautilusTrader TradingNode integration (factories, live clients, Equity/Option/Future instruments, reconciliation reports, account state) nautilus.py ✅ tested against nautilus_trader 1.230
Rust core (models, enums, parse, protocol state) crates/adapters/fyers ✅ tested

Quick start

1. Get an access token (daily)

export FYERS_CLIENT_ID="ABCD1234-100"
export FYERS_SECRET_KEY="..."
python examples/generate_access_token.py
export FYERS_ACCESS_TOKEN="<printed token>"

2. Stream live quotes

pip install websockets
python examples/basic_data_subscription.py
from nautilus_fyers import FyersDataClientConfig, QuoteEvent, create_data_client

client = create_data_client(FyersDataClientConfig.from_env())
client.on(QuoteEvent, my_async_handler)
await client.subscribe_quotes(["NSE:RELIANCE-EQ"])
await client.connect()

3. Trade

from nautilus_fyers import FyersExecClientConfig, create_execution_client

exec_client = create_execution_client(FyersExecClientConfig.from_env())
await exec_client.connect()   # order socket: real-time order/trade/position updates
response = await exec_client.submit_order(
    "NSE:SBIN-EQ", "BUY", 1, order_type="LIMIT", limit_price=795.0,
)
await exec_client.cancel_order(response["id"])
state = await exec_client.reconcile()  # orders + positions + trades

See examples/ for complete scripts, including a safe live order-lifecycle test that uses a far-from-market limit order (Fyers has no paper-trading sandbox).

4. Run a NautilusTrader TradingNode

pip install "nautilus-fyers[nautilus,live]"
from nautilus_trader.config import TradingNodeConfig
from nautilus_trader.live.node import TradingNode
from nautilus_fyers.nautilus import (
    FyersDataClientConfig, FyersExecClientConfig,
    FyersLiveDataClientFactory, FyersLiveExecClientFactory,
)

config = TradingNodeConfig(
    trader_id="FYERS-TRADER-001",
    data_clients={"FYERS": FyersDataClientConfig(client_id=..., access_token=...)},
    exec_clients={"FYERS": FyersExecClientConfig(client_id=..., access_token=...)},
)
node = TradingNode(config=config)
node.add_data_client_factory("FYERS", FyersLiveDataClientFactory)
node.add_exec_client_factory("FYERS", FyersLiveExecClientFactory)
node.build()
node.run()

Strategies address instruments as NSE:SBIN-EQ.FYERS. Execution updates (accepted/filled/cancelled/rejected, partial fills, account state, reconciliation reports) flow through the real-time order socket; quote ticks are synthesized by REST polling (configurable quote_poll_interval, default 1s) until the binary HSM market-data codec is implemented. See examples/nautilus_trading_node.py.

5. Load instruments

from nautilus_fyers import FyersInstrumentProvider

provider = FyersInstrumentProvider()
await provider.load_all_async({"segment": ["CM", "FO"]})  # downloads the daily symbol master
instrument = provider.get("NSE:RELIANCE-EQ.FYERS")

Development

python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest                      # Python tests
cargo test --manifest-path crates/adapters/fyers/Cargo.toml   # Rust tests

All test fixtures live under test_data/ and mirror real Fyers API payload shapes (http_*.json for REST, ws_*.json for WebSocket frames, plus a positional symbol-master CSV).

Implementation notes & deviations from the plan

  • Authorization header: Fyers v3 expects Authorization: <client_id>:<access_token>, not Bearer <token> as sketched in the plan. FyersConfig.authorization() builds the correct form.
  • Modify order uses HTTP PATCH (matching the official fyers-apiv3 SDK), not PUT.
  • Endpoint paths: trading endpoints live under /api/v3/... and market data under /data/... on api-t1.fyers.in; the plan's single /orders, /data/quotes paths were normalized accordingly.
  • WebSocket protocol (verified against the official fyers-apiv3 SDK source): auth travels on the websocket handshake as an authorization: <client_id>:<token> header — there is no JSON "CONNECT" frame. The order socket is wss://socket.fyers.in/trade/v3 (JSON), the data socket is wss://socket.fyers.in/hsm/v1-5/prod (binary "HSM" protocol that first requires symbol→HSM-token conversion via /data/symbol-token), and keepalive is a literal "ping" text frame. Raw order-socket frames use short field names (qty_filled, price_limit, tran_side, ...) which the event parsers accept alongside the mapped names.
  • Binary data frames: the HSM binary codec (and the protobuf tick-by-tick socket at rtsocket-api.fyers.in) is not implemented yet — live market data streaming will connect but deliver no ticks until then; REST snapshots and historical bars work. The order socket is JSON and fully supported.
  • Native Nautilus objects: NautilusTrader is an optional dependency. The core adapter emits neutral event dataclasses (QuoteEvent, OrderEvent, ...); nautilus_fyers.nautilus (imported only when NautilusTrader is installed) provides full TradingNode wiring — LiveMarketDataClient/LiveExecutionClient subclasses, Equity/Option/Future instrument conversion, order lifecycle events with partial-fill deltas, reconciliation reports, and account state from /funds.
  • Rust core: the crate contains the domain layer (models, enums, parsing, subscription and dedup state) with fixture-driven unit tests, but no HTTP/WebSocket I/O runtime — that arrives when the crate is vendored into the NautilusTrader workspace where nautilus-network provides the clients.

Restarts & carryforward

Order ownership survives restarts through three mechanisms:

  1. Every order is submitted with a Fyers orderTag carrying a sanitized copy of the Nautilus ClientOrderId, and the tag round-trips in order-book rows and socket frames.
  2. On connect, the execution client seeds its venue-order map and cumulative fill counters from the venue order book — so socket replays of already-filled orders never double-fill, and fills that happened while you were offline reconcile as deltas.
  3. Reconciliation reports resolve ClientOrderId from the returned tag by matching against cached orders.

For step 3 to work across a process restart, enable Nautilus cache persistence so cached orders survive (otherwise recovered orders are adopted as external/venue orders — safe, but not attributed to your strategy):

from nautilus_trader.config import CacheConfig, DatabaseConfig

config = TradingNodeConfig(
    ...,
    cache=CacheConfig(database=DatabaseConfig(type="redis", host="localhost", port=6379)),
)

Positions carried overnight (CNC) are recovered via position status reports on every start.

Symbol format

Fyers symbols look like NSE:RELIANCE-EQ, NSE:NIFTY25JUL25000CE, BSE:HDFCBANK-A. Instrument ids append the venue: NSE:RELIANCE-EQ.FYERS.

Rate limits & tokens

  • REST: ~10 requests/second (enforced client-side by a token bucket).
  • Access tokens expire daily (~6 AM IST). Use TokenManager with a refresh callback, or re-run examples/generate_access_token.py. Refresh tokens last ~15 days.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nautilus_fyers-0.2.0.tar.gz (39.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nautilus_fyers-0.2.0-py3-none-any.whl (32.6 kB view details)

Uploaded Python 3

File details

Details for the file nautilus_fyers-0.2.0.tar.gz.

File metadata

  • Download URL: nautilus_fyers-0.2.0.tar.gz
  • Upload date:
  • Size: 39.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for nautilus_fyers-0.2.0.tar.gz
Algorithm Hash digest
SHA256 bfd676d5217058319c0b27b597e9049b14a36741592c68324237100c638daf82
MD5 92c8f4da5b15638c4fd05a0bd08e2906
BLAKE2b-256 86da3c7887cd923a2fb332916894d40195f4a6f8b68b1b26feea9858e9af506b

See more details on using hashes here.

File details

Details for the file nautilus_fyers-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: nautilus_fyers-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 32.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for nautilus_fyers-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 189903e8de7c6a9861f1a6e1d4594c6a1eac7809bb1a42e7d8c328d726964e76
MD5 94879cb538adf91154cf2e12562ea55c
BLAKE2b-256 8e0b86671b7214e1e8b82c66022db811f8d292ec207ba5a4e61bca71e5a87857

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page