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.

→ Full guides:

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.1.tar.gz (39.4 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.1-py3-none-any.whl (32.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nautilus_fyers-0.2.1.tar.gz
  • Upload date:
  • Size: 39.4 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.1.tar.gz
Algorithm Hash digest
SHA256 d297b2ed319c5bf8cc7bf4a7066dc4bba89b0a80837b49c8da55b641eb21fe2f
MD5 3d632aa2ef31dfcae47594b0d82f4927
BLAKE2b-256 aa8ce6ef54130e186c5b1ce9366ec674d87e38d83d12f7202043d6d2d8c7a5cb

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nautilus_fyers-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 32.8 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 23e0f4433b00a3369b9b822da6fc3da851bb4e7366b2760659aa7e2bc17949fa
MD5 ac2c61e9f70a89cec615d7b0c1f8edb3
BLAKE2b-256 d081eac9977e620468a72761a3fef879b328a8e26df08a8ac54093de0947d87c

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