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
Real-time streaming (official SDK decoder, asyncio bridge, [stream] extra) streaming.py, data.py ✅ live-verified
Data socket protocol state (modes, reconnect + subscription restore) transport.py, ws.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 "nautilus-fyers[stream]"   # real-time streaming (official SDK decoder)
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 (HSM): solved by wrapping the official fyers-apiv3 SDK — the broker's own maintained decoder — as an optional streaming backend (pip install "nautilus-fyers[stream]"), the same pattern NautilusTrader's IBKR adapter uses with ibapi. With the extra installed, subscribe_quotes() delivers true streaming ticks; without it, quotes fall back to 1s REST polling. A native asyncio HSM codec remains a possible future replacement.
  • 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.3.1.tar.gz (44.5 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.3.1-py3-none-any.whl (36.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nautilus_fyers-0.3.1.tar.gz
  • Upload date:
  • Size: 44.5 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.3.1.tar.gz
Algorithm Hash digest
SHA256 28b451d83e760ef3a580051b652014079c8c8d5508cea6b53a7a26df7cd965b7
MD5 3041a5079de49d78fa25573c0d218cfd
BLAKE2b-256 10856f5dce6d02bfdf056700a6b6e56c9559098fb4b882ce5c5b2928ceae1689

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nautilus_fyers-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 36.5 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.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9eb0aa8b54e0667703ce36a088dfd785f06e33b182eb94405fa35146bedcc2b6
MD5 71805776d141ed0271f255334edb2afd
BLAKE2b-256 d32569582cfcdd01cc901709ddb58e104c2352c2e601b50bb9ca7d062a986a5d

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