ig_client/lib.rs
1//! # IG Markets API Client for Rust
2//!
3//! A comprehensive Rust client for interacting with the IG Markets trading API. This library provides a type-safe and ergonomic way to access IG Markets' REST and WebSocket APIs for trading and market data retrieval.
4//!
5//! ## Overview
6//!
7//! The IG Markets API Client for Rust is designed to provide a reliable and efficient interface to the IG Markets trading platform. It handles authentication, session management, and all API interactions while providing a clean, idiomatic Rust interface for developers.
8//!
9//! ## Features
10//!
11//! - **Authentication**: Secure authentication with the IG Markets API using OAuth2
12//! - **Account Management**: Access account information, balances, and activity history
13//! - **Market Data**: Retrieve market data, prices, instrument details, and historical prices
14//! - **Order Management**: Create, modify, and close positions and orders with various order types
15//! - **Transaction History**: Access detailed transaction and activity history
16//! - **WebSocket Support**: Real-time market data streaming via WebSocket connections
17//! - **Fully Documented**: Comprehensive documentation for all components and methods
18//! - **Error Handling**: Robust error handling and reporting with detailed error types
19//! - **Type Safety**: Strong type checking for API requests and responses
20//! - **Async Support**: Built with async/await for efficient non-blocking operations
21//! - **Configurable**: Flexible configuration options for different environments (demo/live)
22//! - **Persistence**: Optional database integration for storing historical data
23//!
24//! ## Installation
25//!
26//! Add this to your `Cargo.toml`:
27//!
28//! ```toml
29//! [dependencies]
30//! ig-client = "0.1.2"
31//! tokio = { version = "1", features = ["full"] } # For async runtime
32//! dotenv = "0.15" # For environment variable loading
33//! tracing = "0.1" # For logging
34//! ```
35//!
36//! ### Requirements
37//!
38//! - Rust 1.56 or later (for async/await support)
39//! - An IG Markets account (demo or live)
40//! - API credentials from IG Markets
41//!
42//! ## Configuration
43//!
44//! Create a `.env` file in your project root with the following variables:
45//!
46//! ```,ignore
47//! IG_USERNAME=your_username
48//! IG_PASSWORD=your_password
49//! IG_API_KEY=your_api_key
50//! IG_ACCOUNT_ID=your_account_id
51//! IG_BASE_URL=https://demo-api.ig.com/gateway/deal # Use demo or live as needed
52//! IG_WEBSOCKET_URL=wss://demo-apd.marketdatasystems.com
53//! DATABASE_URL=postgres://user:password@localhost/ig_db # Optional for data persistence
54//! ```
55//!
56//! ## Usage Examples
57//!
58//! ### Complete Example Application
59//!
60//! Here's a complete example showing how to set up the client, authenticate, and perform various operations:
61//!
62//! ```rust,ignore
63//! use ig_client::application::services::account_service::{AccountService, IgAccountService};
64//! use ig_client::application::services::market_service::{IgMarketService, MarketService};
65//! use ig_client::application::services::order_service::{IgOrderService, OrderService};
66//! use ig_client::application::models::order::{CreateOrderRequest, Direction};
67//! use ig_client::config::Config;
68//! use ig_client::session::auth::IgAuth;
69//! use std::sync::Arc;
70//! use dotenv::dotenv;
71//! use tracing::{info, error, Level};
72//! use tracing_subscriber::FmtSubscriber;
73//!
74//! #[tokio::main]
75//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
76//! // Initialize logging
77//! let subscriber = FmtSubscriber::builder()
78//! .with_max_level(Level::INFO)
79//! .finish();
80//! tracing::subscriber::set_global_default(subscriber)?;
81//!
82//! // Load environment variables
83//! dotenv().ok();
84//! info!("Environment variables loaded");
85//!
86//! // Create configuration
87//! let config = Arc::new(Config::new());
88//! info!("Configuration created");
89//!
90//! // Authenticate
91//! let auth = IgAuth::new(config.clone());
92//! let session = auth.authenticate().await?;
93//! info!("Authentication successful");
94//!
95//! // Create services
96//! let account_service = IgAccountService::new(config.clone());
97//! let market_service = IgMarketService::new(config.clone());
98//! let order_service = IgOrderService::new(config.clone());
99//!
100//! // Get account information
101//! let account_info = account_service.get_accounts(&session).await?;
102//! info!("Account information retrieved: {} accounts", account_info.accounts.len());
103//!
104//! // Search for a market
105//! let search_result = market_service.search_markets(&session, "EUR/USD").await?;
106//! info!("Found {} markets matching search", search_result.markets.len());
107//!
108//! if let Some(market) = search_result.markets.first() {
109//! info!("Selected market: {} ({})", market.instrument_name, market.epic);
110//!
111//! // Get market details
112//! let market_details = market_service.get_market_details(&session, &market.epic).await?;
113//! info!("Market details retrieved: {}", market_details.instrument.name);
114//!
115//! // Create and place a demo order (if this is a demo account)
116//! if session.account_type == "DEMO" {
117//! let order = CreateOrderRequest::market(
118//! market.epic.clone(),
119//! Direction::Buy,
120//! 0.1, // Small size for demo
121//! None,
122//! None,
123//! );
124//!
125//! let order_result = order_service.create_order(&session, &order).await?;
126//! info!("Order placed: deal reference = {}", order_result.deal_reference);
127//!
128//! // Get positions
129//! let positions = account_service.get_positions(&session).await?;
130//! info!("Current positions: {}", positions.positions.len());
131//! }
132//! }
133//!
134//! info!("Example completed successfully");
135//! Ok(())
136//! }
137//! ```
138//!
139//! ### Authentication
140//!
141//! ```rust,ignore
142//! use ig_client::session::auth::IgAuth;
143//! use ig_client::config::Config;
144//! use std::sync::Arc;
145//!
146//! #[tokio::main]
147//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
148//! // Load configuration from environment variables
149//! let config = Arc::new(Config::new());
150//!
151//! // Create authentication handler
152//! let auth = IgAuth::new(config.clone());
153//!
154//! // Authenticate and get a session
155//! let session = auth.authenticate().await?;
156//!
157//! println!("Successfully authenticated!");
158//! Ok(())
159//! }
160//! ```
161//!
162//! ### Getting Account Information
163//!
164//! ```rust,ignore
165//! use ig_client::application::services::account_service::{AccountService, IgAccountService};
166//! use std::sync::Arc;
167//!
168//! // Create account service
169//! let account_service = IgAccountService::new(config.clone());
170//!
171//! // Get account information
172//! let account_info = account_service.get_accounts(&session).await?;
173//! println!("Accounts: {:?}", account_info);
174//!
175//! // Get positions
176//! let positions = account_service.get_positions(&session).await?;
177//! println!("Open positions: {}", positions.positions.len());
178//!
179//! // Get transaction history
180//! let from_date = chrono::Utc::now() - chrono::Duration::days(7);
181//! let to_date = chrono::Utc::now();
182//! let transactions = account_service.get_transactions(&session, from_date, to_date).await?;
183//! println!("Transactions in the last week: {}", transactions.transactions.len());
184//! ```
185//!
186//! ### Market Data
187//!
188//! ```rust,ignore
189//! use ig_client::application::services::market_service::{MarketService, IgMarketService};
190//!
191//! // Create market service
192//! let market_service = IgMarketService::new(config.clone());
193//!
194//! // Search for markets
195//! let search_result = market_service.search_markets(&session, "Apple").await?;
196//! println!("Found {} markets matching 'Apple'", search_result.markets.len());
197//!
198//! // Get market details
199//! if let Some(market) = search_result.markets.first() {
200//! let details = market_service.get_market_details(&session, &market.epic).await?;
201//! println!("Market details for {}: {}", market.instrument_name, details.instrument.name);
202//!
203//! // Get historical prices
204//! let prices = market_service.get_prices(
205//! &session,
206//! &market.epic,
207//! "DAY", // Resolution
208//! 30, // Number of data points
209//! ).await?;
210//! println!("Retrieved {} historical price points", prices.prices.len());
211//! }
212//! ```
213//!
214//! ### Placing and Managing Orders
215//!
216//! ```rust,ignore
217//! use ig_client::application::services::order_service::{OrderService, IgOrderService};
218//! use ig_client::application::models::order::{CreateOrderRequest, Direction, OrderType, TimeInForce};
219//!
220//! // Create order service
221//! let order_service = IgOrderService::new(config.clone());
222//!
223//! // Create a market order
224//! let market_order = CreateOrderRequest::market(
225//! "CS.D.EURUSD.CFD.IP".to_string(), // EPIC
226//! Direction::Buy, // Direction
227//! 1.0, // Size
228//! None, // Limit level
229//! None, // Stop level
230//! );
231//!
232//! // Place the order
233//! let result = order_service.create_order(&session, &market_order).await?;
234//! println!("Market order placed: {:?}", result);
235//!
236//! // Create a limit order
237//! let limit_order = CreateOrderRequest {
238//! epic: "CS.D.EURUSD.CFD.IP".to_string(),
239//! direction: Direction::Buy,
240//! size: 1.0,
241//! order_type: OrderType::Limit,
242//! level: Some(1.05), // Limit price
243//! guaranteed_stop: false,
244//! time_in_force: TimeInForce::GoodTillDate,
245//! good_till_date: Some("2025-06-01T12:00:00".to_string()),
246//! stop_level: None,
247//! stop_distance: None,
248//! limit_level: None,
249//! limit_distance: None,
250//! deal_reference: Some("my-custom-reference".to_string()),
251//! };
252//!
253//! let result = order_service.create_order(&session, &limit_order).await?;
254//! println!("Limit order placed: {:?}", result);
255//!
256//! // Close a position
257//! let positions = account_service.get_positions(&session).await?;
258//! if let Some(position) = positions.positions.first() {
259//! let close_request = order_service.close_position(
260//! &session,
261//! &position.position.deal_id,
262//! position.position.direction.clone(),
263//! position.position.size,
264//! ).await?;
265//! println!("Position closed: {:?}", close_request);
266//! }
267//! ```
268//!
269//! ### WebSocket Streaming
270//!
271//! ```rust,ignore
272//! use ig_client::application::services::market_listener::{MarketListener, MarketListenerCallback};
273//! use ig_client::application::models::market::MarketData;
274//! use std::sync::Arc;
275//! use tokio::sync::mpsc;
276//!
277//! // Create a channel to receive market updates
278//! let (tx, mut rx) = mpsc::channel(100);
279//!
280//! // Create a callback function to handle market updates
281//! let callback: MarketListenerCallback = Arc::new(move |market_data: &MarketData| {
282//! let data = market_data.clone();
283//! let tx = tx.clone();
284//! tokio::spawn(async move {
285//! let _ = tx.send(data).await;
286//! });
287//! Ok(())
288//! });
289//!
290//! // Create and start the market listener
291//! let listener = MarketListener::new(callback);
292//! listener.connect(&session).await?;
293//!
294//! // Subscribe to market updates
295//! let epics = vec!["CS.D.EURUSD.CFD.IP", "CS.D.USDJPY.CFD.IP"];
296//! listener.subscribe(&epics).await?;
297//!
298//! // Process market updates
299//! while let Some(market_data) = rx.recv().await {
300//! println!("Market update for {}: bid={}, offer={}",
301//! market_data.epic, market_data.bid.unwrap_or(0.0), market_data.offer.unwrap_or(0.0));
302//! }
303//! ```
304//!
305//! ## Documentation
306//!
307//! Comprehensive documentation is available for all components of the library. The documentation includes detailed explanations of all modules, structs, and functions, along with examples of how to use them.
308//!
309//! ### API Documentation
310//!
311//! You can access the API documentation on [docs.rs](https://docs.rs/ig-client) or generate it locally with:
312//!
313//! ```bash
314//! make doc-open
315//! ```
316//!
317//! ### Architecture
318//!
319//! The library is organized into several modules:
320//!
321//! - **config**: Configuration handling and environment variable loading
322//! - **session**: Authentication and session management
323//! - **application**: Core business logic and services
324//! - **models**: Data structures for API requests and responses
325//! - **services**: Service implementations for different API areas
326//! - **transport**: HTTP and WebSocket communication with the IG Markets API
327//! - **utils**: Utility functions for parsing, logging, etc.
328//! - **error**: Error types and handling
329//!
330//! ## Development
331//!
332//! This project includes a comprehensive Makefile with commands for common development tasks.
333//!
334//! ### Building
335//!
336//! ```bash
337//! make build # Debug build
338//! make release # Release build
339//! ```
340//!
341//! ### Testing
342//!
343//! ```bash
344//! make test # Run all tests
345//! ```
346//!
347//! ### Code Quality
348//!
349//! ```bash
350//! make fmt # Format code with rustfmt
351//! make lint # Run clippy linter
352//! make doc # Check documentation coverage
353//! make check # Run tests, format check, and linting
354//! make pre-push # Run all checks before pushing code
355//! ```
356//!
357//! ### Documentation
358//!
359//! ```bash
360//! make doc-open # Generate and open documentation
361//! ```
362//!
363//! ### Code Coverage
364//!
365//! ```bash
366//! make coverage # Generate code coverage report (XML)
367//! make coverage-html # Generate HTML coverage report
368//! make open-coverage # Open HTML coverage report
369//! ```
370//!
371//! ### Benchmarking
372//!
373//! ```bash
374//! make bench # Run benchmarks
375//! make bench-show # Show benchmark results
376//! ```
377//!
378//! ### Continuous Integration
379//!
380//! ```bash
381//! make workflow # Run all CI workflow steps locally
382//! ```
383//!
384//! ## Project Structure
385//!
386//! ```text
387//! ├── src/
388//! │ ├── application/ # Core business logic
389//! │ │ ├── models/ # Data models
390//! │ │ │ ├── account.rs # Account-related models
391//! │ │ │ ├── market.rs # Market data models
392//! │ │ │ ├── order.rs # Order models
393//! │ │ │ └── transaction.rs # Transaction models
394//! │ │ └── services/ # Service implementations
395//! │ │ ├── account_service.rs
396//! │ │ ├── market_service.rs
397//! │ │ └── order_service.rs
398//! │ ├── config.rs # Configuration handling
399//! │ ├── error.rs # Error types
400//! │ ├── session/ # Authentication and session
401//! │ │ └── auth.rs # Authentication handler
402//! │ ├── transport/ # API communication
403//! │ │ ├── http.rs # HTTP client
404//! │ │ └── websocket.rs # WebSocket client
405//! │ └── utils/ # Utility functions
406//! ├── examples/ # Example applications
407//! ├── tests/ # Integration tests
408//! └── Makefile # Development commands
409//! ```
410//!
411//! ## Contributing
412//!
413//! Contributions are welcome! Here's how you can contribute:
414//!
415//! 1. Fork the repository
416//! 2. Create a feature branch: `git checkout -b feature/my-feature`
417//! 3. Make your changes and commit them: `git commit -m 'Add some feature'`
418//! 4. Run the tests: `make test`
419//! 5. Push to the branch: `git push origin feature/my-feature`
420//! 6. Submit a pull request
421//!
422//! Please make sure your code passes all tests and linting checks before submitting a pull request.
423
424/// Configuration for the IG Markets API client
425pub mod config;
426
427/// Core application logic and services
428pub mod application;
429
430/// User interface and presentation layer
431pub mod presentation;
432
433/// Authentication and session management
434pub mod session;
435
436/// Network transport and API communication
437pub mod transport;
438
439pub(crate) mod constants;
440
441/// Error types and handling
442pub mod error;
443
444/// Data persistence and storage
445pub mod storage;
446
447/// Utility functions and helpers
448pub mod utils;