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