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;