IG Markets API Client for Rust
A comprehensive Rust client for the IG Markets trading API. This library provides a type-safe, async-first way to access IG Markets' REST and real-time streaming APIs for trading and market-data retrieval.
Overview
The IG Markets API Client for Rust offers a reliable interface to the IG Markets trading platform. It handles authentication and session management, automatic token refresh, rate limiting, finite retry with backoff, and real-time streaming over the Lightstreamer protocol, exposing a clean, idiomatic Rust API.
Features
- Authentication: IG session v2 (
CST/X-SECURITY-TOKENheaders) and v3 (OAuth bearer) with automatic, transparent token refresh and account switching. - Account Management: Accounts, balances, positions, working orders, preferences, activity, and transaction history.
- Market Data: Market search, instrument details, market navigation, and historical prices at several resolutions.
- Order Management: Create, update, and close positions and working orders with typed request builders.
- Watchlists: Full CRUD over watchlists and their instruments.
- Client Sentiment: Sentiment for single, multiple, and related markets.
- Indicative Costs: Costs and charges for opening, closing, or editing positions, plus cost history.
- Real-time Streaming: Market, price, trade, and account updates over Lightstreamer, with thread-safe dynamic subscription management.
- Rate Limiting:
governor-backed pacing configured per IG's trading vs non-trading budgets. - Finite Retry: Exponential backoff with jitter via
RetryConfig(bounded — no unbounded retry loops). - Type Safety: Strongly typed request / response DTOs and domain enums.
- Async: Built on
tokiowith a shared, pooledreqwestclient. - Persistence (optional): PostgreSQL storage via
sqlx.
Installation
Add this to your Cargo.toml:
[]
= "0.12.1"
= { = "1", = ["full"] } # Async runtime
= "0.1" # Logging facade
# Optional, only if you use the PostgreSQL persistence layer:
= { = "0.9", = ["runtime-tokio", "tls-native-tls", "postgres"] }
You do not need to add dotenv yourself — Config::new() loads a local
.env file internally.
Requirements
- Rust 2024 edition on the stable toolchain.
- An IG Markets account (demo or live) and API credentials.
- A PostgreSQL database (optional, only for the persistence layer).
Configuration
Config::new() reads configuration from the environment (and a local .env
file, if present). Create a .env file in your project root with the
following variables:
IG_USERNAME=your_username
IG_PASSWORD=your_password
IG_API_KEY=your_api_key
IG_ACCOUNT_ID=your_account_id
IG_API_VERSION=3 # 2 (CST/XST) or 3 (OAuth); defaults to 3
IG_REST_BASE_URL=https://demo-api.ig.com/gateway/deal # Use demo or live as needed
IG_REST_TIMEOUT=30 # REST request timeout in seconds
IG_WS_URL=wss://demo-apd.marketdatasystems.com # Lightstreamer endpoint
IG_WS_RECONNECT_INTERVAL=5 # Reconnect interval in seconds
IG_RATE_LIMIT_MAX_REQUESTS=4 # Rate-limiter budget
IG_RATE_LIMIT_PERIOD_SECONDS=12 # Rate-limiter period (seconds)
IG_RATE_LIMIT_BURST_SIZE=3 # Rate-limiter burst size
DATABASE_URL=postgres://user:password@localhost/ig_db # Optional persistence
DATABASE_MAX_CONNECTIONS=5 # Optional connection-pool size
TX_LOOP_INTERVAL_HOURS=1 # Transaction loop interval (hours)
TX_PAGE_SIZE=20 # Transaction page size
TX_DAYS_LOOKBACK=7 # Days to look back for transactions
Live examples and integration tests default to the IG demo environment;
pointing anything at production requires an explicit opt-in via
IG_REST_BASE_URL / IG_WS_URL.
Usage
The main entry points are Config and Client. A single Client
implements every REST service trait (AccountService, MarketService,
OrderService, WatchlistService, SentimentService, CostsService,
OperationsService). Session login and token refresh are performed
transparently on first use. The prelude re-exports the full public surface,
including the streaming API and the service traits, so a single glob import
is usually enough:
use *;
Client setup and account information
use *;
async
Market data
use *;
async
Placing an order
use *;
async
Real-time streaming
DynamicMarketStreamer wraps the lower-level StreamerClient and manages
subscriptions in a thread-safe way. Its constructor is synchronous — it
only wires up in-memory channels; the network connection is established later
by start. Updates arrive as PriceData, resolved through the prelude.
use *;
use HashSet;
async
Available Services
Every service trait below is implemented by Client; bring the traits into
scope via use ig_client::prelude::*;.
AccountService
get_accounts()— all accounts for the authenticated userget_positions()/get_positions_w_filter(filter)— open positionsget_working_orders()— working ordersget_activity(from, to)/get_activity_with_details(from, to)— activityget_activity_by_period(period_ms)— activity for a period in millisecondsget_transactions(from, to)— transaction history (paginated internally)get_preferences()/update_preferences(trailing_stops_enabled)
MarketService
search_markets(term)— search markets by keywordget_market_details(epic)/get_multiple_market_details(epics)get_historical_prices(epic, resolution, from, to)andget_historical_prices_by_date_range(epic, resolution, start, end)get_historical_prices_by_count_v1/_v2(epic, resolution, num_points)get_recent_prices(params)get_market_navigation()/get_market_navigation_node(node_id)get_all_markets()/get_vec_db_entries()get_categories()/get_category_instruments(category_id, page, size)
OrderService
create_order(request)— open a positionget_order_confirmation(deal_reference)andget_order_confirmation_w_retry(deal_reference, retries, delay_ms)update_position(deal_id, update)/update_level_in_position(deal_id, level)close_position(request)/get_position(deal_id)create_working_order(request)/update_working_order(deal_id, update)/delete_working_order(deal_id)
WatchlistService
get_watchlists()/create_watchlist(name, epics)get_watchlist(id)/delete_watchlist(id)add_to_watchlist(id, epic)/remove_from_watchlist(id, epic)
SentimentService
get_client_sentiment(market_ids)get_client_sentiment_by_market(market_id)get_related_sentiment(market_id)
CostsService
get_indicative_costs_open(request)/_close(request)/_edit(request)get_costs_history(from, to)/get_durable_medium(quote_reference)
OperationsService
get_client_apps()— API application detailsdisable_client_app()— disable the current API key
Rate Limiting
All requests are paced by a governor-backed RateLimiter so the client
stays within IG's published limits (trading and non-trading endpoints have
different budgets). The budget is configured from the environment via
IG_RATE_LIMIT_MAX_REQUESTS, IG_RATE_LIMIT_PERIOD_SECONDS, and
IG_RATE_LIMIT_BURST_SIZE (see RateLimiterConfig). On top of pacing,
transient failures (429 / 5xx / connection errors) are retried with
finite exponential backoff and jitter via RetryConfig; non-idempotent
trading calls are never retried blindly.
Architecture
The crate is organized as a module-oriented library under src/:
application— all I/O. The RESTClientand its service-trait implementations,Auth/Session(login, refresh, logout, account switching),Config, theRateLimiter, and the streaming layer (StreamerClient,DynamicMarketStreamer). Service traits live underapplication::interfaces.model— pure request / response DTOs, streaming message DTOs, session DTOs, and the retry policy. Serde only; no I/O.presentation— domain entities per area: account, chart, instrument, market, order, price, trade, transaction. Serde only; no I/O.storage— optional PostgreSQL persistence viasqlx(sits on top ofmodel/presentation).utils— leaf helpers: env-var config, logging, finance (P&L), parsing, deal-reference id generation.error— canonical typed error enums:AppError,AuthError, andFetchError.constants— crate-wide endpoint paths, header names, and defaults.prelude— curated public re-exports (the recommended import surface).
API Documentation
Browse the API documentation on docs.rs or generate it locally with:
Project Structure
src/
├── application/ # I/O: client, auth, config, rate limiter, streaming
│ ├── interfaces/ # Service traits (account, market, order, costs, …)
│ ├── auth.rs # Auth / Session: login, refresh, logout, switch
│ ├── client.rs # Client (REST services) + StreamerClient
│ ├── config.rs # Config, Credentials, REST / WS / rate-limiter config
│ ├── rate_limiter.rs # governor-backed request pacing
│ └── dynamic_streamer.rs # DynamicMarketStreamer (subscriptions)
├── model/ # Pure DTOs: requests, responses, streaming, retry
├── presentation/ # Domain entities (account, market, order, price, …)
├── storage/ # Optional PostgreSQL persistence via sqlx
├── utils/ # config, logger, finance, parsing, id helpers
├── constants.rs # Endpoint paths, header names, defaults
├── error.rs # AppError / AuthError / FetchError
├── prelude.rs # Curated public re-exports
└── lib.rs # Public API and crate docs (README source)
examples/ # Runnable demos (workspace members)
tests/ # unit/ and env-gated integration/ tests
benches/ # Criterion benchmarks
Development
This project includes a Makefile with common development tasks:
Contributing
Contributions are welcome:
- Fork the repository.
- Create a feature branch:
git checkout -b feature/my-feature. - Make your changes and commit them.
- Run the checks:
make pre-push. - Push the branch and open a pull request.
Please make sure your code passes all tests and linting checks before submitting a pull request.
What's New in 0.12.1
- The
historical_pricesunique-constraint migration now tolerates PostgreSQL SQLSTATE42P07("relation already exists") when an index with the constraint's name already exists without an attached constraint — previously this failed application startup on every run (#79).
What's New in 0.12.0
A large correctness, safety, and API-consistency release. Highlights:
Security & reliability
- Credentials, session tokens (CST / X-SECURITY-TOKEN / OAuth) and the DB
connection URL are redacted from
Debug/Displayand never logged. - Retries are finite by default with exponential backoff + jitter; HTTP 429 is retried; unbounded retry loops are gone.
- The rate limiter honours the configured
max_requestsand separates the trading, non-trading and historical budgets. - Token expiry/refresh is consistent (single refresh-and-replay on 401); the streaming connection no longer holds a lock across its lifetime and every spawned task has a shutdown path.
Correctness
- Order size rounds to the nearest tick (no more
0.29 → 0.28); P&L math is unified and correct when a market price is missing; position netting takes the larger side's direction. - Storage: the unique-constraint migration actually runs, empty-epic stats no
longer panic,
instrument_typeis stored unquoted, and historical prices persist in UTC (snapshotTimeUTC). - DTOs capture previously-dropped IG fields (
affectedDeals/profiton confirms,EXECUTE_AND_ELIMINATE, OPU stop/limit/trailing fields); IG numeric fields arei64/unsigned, noti32.
API & structure (breaking — 0.12.0)
- Constructors are fallible and panic-free: use
Client::try_new(),Auth::try_new(),HttpClient::new_lazy()(new()/Defaultremoved). - Module boundaries restored:
model/presentationare pure DTO layers;HttpClientand the streaming adapters live inapplication. - Typed errors are wired up (
AuthError, deserialization context with the auth-response body redacted). The prelude now exports the streaming API and all service traits.
Testing
- Offline test coverage for the auth flow, HTTP retry/status mapping,
streaming lifecycle, and serde round-trips (via a
wiremockdev-dependency).
Contribution
We welcome contributions to this project! If you would like to contribute, please follow these steps:
- Fork the repository.
- Create a new branch for your feature or bug fix.
- Make your changes and ensure that the project still builds and all tests pass.
- Commit your changes and push your branch to your forked repository.
- Submit a pull request to the main repository.
Contact Information
If you have any questions, issues, or would like to provide feedback, please feel free to contact the project maintainer:
- Author: Joaquín Béjar García
- Email: jb@taunais.com
- Telegram: @joaquin_bejar
- Repository: https://github.com/joaquinbejar/ig-client
- Documentation: https://docs.rs/ig-client
We appreciate your interest and look forward to your contributions!
✍️ License
Licensed under MIT license
Disclaimer
This software is not officially associated with IG Markets. Trading financial instruments carries risk, and this library is provided as-is without any guarantees. Always test thoroughly with a demo account before using in a live trading environment.