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}