trading-ig 0.1.1

Async Rust client for the IG Markets REST and Lightstreamer streaming APIs
Documentation
# trading-ig

[![Crates.io](https://img.shields.io/crates/v/trading-ig.svg)](https://crates.io/crates/trading-ig)
[![Documentation](https://docs.rs/trading-ig/badge.svg)](https://docs.rs/trading-ig)
[![CI](https://github.com/tibs245/trading-ig-rust/actions/workflows/ci.yml/badge.svg)](https://github.com/tibs245/trading-ig-rust/actions/workflows/ci.yml)
[![Security audit](https://github.com/tibs245/trading-ig-rust/actions/workflows/audit.yml/badge.svg)](https://github.com/tibs245/trading-ig-rust/actions/workflows/audit.yml)
[![License](https://img.shields.io/crates/l/trading-ig.svg)](LICENSE)
[![Rust 1.94+](https://img.shields.io/badge/rust-1.94+-blue.svg)](https://www.rust-lang.org)
[![Downloads](https://img.shields.io/crates/d/trading-ig.svg)](https://crates.io/crates/trading-ig)

> ## 🤖 About this crate
>
> This crate was **generated by [Claude Opus]https://www.anthropic.com/claude
> (Anthropic)**, working from the Python library
> [`ig-python/trading-ig`]https://github.com/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](https://labs.ig.com/) REST and
Lightstreamer streaming APIs.

- **Crate**: <https://crates.io/crates/trading-ig>
- **Docs**: <https://docs.rs/trading-ig>
- **Repository**: <https://github.com/tibs245/trading-ig-rust>
- **Changelog**: [`CHANGELOG.md`]CHANGELOG.md

## Install

```toml
[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

```rust
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/`](examples/):

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

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

```bash
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.

```toml
[dependencies]
trading-ig = { version = "0.1", features = ["encryption"] }
```

```rust
client.session().login_with_encryption().await?;
// instead of:
// client.session().login().await?;
```

See [`SECURITY.md`](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](https://docs.rs/polars) `DataFrame`s for analysis:

```toml
[dependencies]
trading-ig = { version = "0.1", features = ["polars"] }
```

```rust
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):

```bash
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`:

```bash
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:

```bash
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/`](_knowledge/). Start with
[`_knowledge/index.md`](_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`](https://github.com/ig-python/trading-ig)
Python project.