trading-ig 0.1.1

Async Rust client for the IG Markets REST and Lightstreamer streaming APIs
Documentation

trading-ig

Crates.io Documentation CI Security audit License Rust 1.94+ Downloads

🤖 About this crate

This crate was generated by Claude Opus (Anthropic), working from the Python library ig-python/trading-ig as the reference specification. The Rust API mirrors the Python one endpoint-for- endpoint. Huge thanks to the original trading-ig authors and contributors — without their work (and their BSD-3 license) this port wouldn't exist.

Async Rust client for the IG Markets REST and Lightstreamer streaming APIs.

Install

[dependencies]
trading-ig = "0.1"

# Optional features:
# trading-ig = { version = "0.1", features = ["stream", "polars"] }

Goals

  • Async-first (tokio), reqwest under the hood.
  • Strongly typed: all requests/responses modelled with serde.
  • Composable: usable as a library independently of any framework.
  • Minimal dependencies.
  • Structured logs/tracing via the tracing crate.
  • Well-tested: HTTP responses are covered by mock-server tests built from reusable fixtures.

Quick start

use trading_ig::{IgClient, Environment, Credentials};

#[tokio::main]
async fn main() -> trading_ig::Result<()> {
    let client = IgClient::builder()
        .environment(Environment::Demo)
        .api_key(std::env::var("IG_API_KEY")?)
        .credentials(Credentials::password(
            std::env::var("IG_USERNAME")?,
            std::env::var("IG_PASSWORD")?,
        ))
        .build()?;

    client.session().login().await?;

    let accounts = client.accounts().list().await?;
    for account in accounts {
        println!("{} ({})", account.account_name, account.account_id);
    }
    Ok(())
}

Examples

Three self-contained examples live in examples/:

Example What it shows
login_and_list_accounts Log in (v3) and print all account IDs
search_market_and_get_history Search for EUR/USD, fetch the last hour of minute bars
open_then_close_position Type-state builder syntax for opening and closing a position

All examples read credentials from IG_API_KEY, IG_USERNAME, and IG_PASSWORD:

IG_API_KEY=xxx IG_USERNAME=you IG_PASSWORD=secret \
  cargo run --example login_and_list_accounts

🔒 Recommended for funded accounts

For any account that holds real money (live, or funded demo), we recommend enabling the encryption feature and using session().login_with_encryption() instead of session().login(). The password is then RSA-encrypted client-side with IG's public key before transmission — it never appears in plaintext in any intermediate proxy or server-side log, even in the event of a TLS MITM or compromised CA.

[dependencies]
trading-ig = { version = "0.1", features = ["encryption"] }
client.session().login_with_encryption().await?;
// instead of:
// client.session().login().await?;

See SECURITY.md for the full rationale and our disclosure policy.

Cargo features

feature default description
rustls-tls yes TLS via rustls
native-tls no TLS via system OpenSSL
stream no Lightstreamer streaming client
encryption no Encrypted-password login (RSA)
polars no Conversions from tabular API responses to DataFrame
live no Compile the live integration test suite (read-only path)
live-trading no Also compile write/mutation live tests (implies live)

polars feature

Enable the polars feature to convert tabular API responses directly into Polars DataFrames for analysis:

[dependencies]
trading-ig = { version = "0.1", features = ["polars"] }
use trading_ig::dataframe::IntoDataFrame;

// Convert a list of open positions into a DataFrame.
let positions = client.dealing().positions().list_v2().await?;
let df = positions.to_dataframe()?;
println!("{df}");

// Convert historical prices into a DataFrame (one row per bar).
let prices = client.prices().history_v3(&epic, Default::default()).await?;
let df = prices.to_dataframe()?;
println!("{df}");

The IntoDataFrame trait is implemented on Vec<Account>, Vec<PositionV2>, Vec<WorkingOrderV2>, HistoricalPrices, Vec<Activity>, Vec<Transaction>, Vec<MarketSummary>, and Vec<Sentiment>.

Live integration tests

A separate test file (tests/live_integration.rs) exercises the real IG Demo API end-to-end. All tests are #[ignore]d and require explicit credentials, so they never run in CI.

Read-only tests (session, accounts, markets, prices, positions, watchlists, client sentiment, history):

IG_API_KEY=your-key IG_USERNAME=you IG_PASSWORD=secret \
  cargo test --features live --ignored --test live_integration

Write / mutation tests (watchlist create/delete, preferences round-trip) additionally require the live-trading feature and IG_LIVE_TRADING_OK=1:

IG_API_KEY=your-key IG_USERNAME=you IG_PASSWORD=secret IG_LIVE_TRADING_OK=1 \
  cargo test --features live-trading --ignored --test live_integration

If IG_API_KEY is absent the whole suite skips gracefully, so cargo test --all-features in CI passes without hitting the network.

Contributing

After cloning, opt into the project's git hooks:

git config core.hooksPath .githooks

This enables:

  • pre-commit — runs cargo fmt --check and cargo clippy --all-targets --all-features --no-deps -- -D warnings on commits that touch Rust or Cargo files. Skipped for doc-only / fixture-only commits.
  • pre-push — runs cargo test --all-targets --all-features before pushing to a remote.

Skip a check if you really need to: git commit --no-verify / git push --no-verify. The CI workflow runs the same checks regardless, so anything you bypass locally will fail in PR.

Project knowledge

Internal conventions, architecture decisions, and the IG API spec live under _knowledge/. Start with _knowledge/index.md — it lists every file with a one-line summary so you can load only what you need.

License

BSD-3-Clause, mirroring the original trading-ig Python project.