diff --git a/README.md b/README.md index 388893d..2c7fd7d 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ A **high-performance async Python SDK** for the [ProjectX Trading Platform](http This Python SDK acts as a bridge between your trading strategies and the ProjectX platform, handling all the complex API interactions, data processing, and real-time connectivity. -## πŸš€ v3.1.9 - Stable Production Release +## πŸš€ v3.1.11 - Stable Production Release -**Latest Version**: v3.1.9 - Fixed tick price alignment in real-time data. See [CHANGELOG.md](CHANGELOG.md) for full release history. +**Latest Version**: v3.1.11 - Fixed ManagedTrade market price fetching for risk-managed trades. See [CHANGELOG.md](CHANGELOG.md) for full release history. ### πŸ“¦ Production Stability Guarantee @@ -113,7 +113,9 @@ async def main(): print(f\"Connected to account: {suite.client.account_info.name}\") - print(f\"Trading {suite.instrument.name} - Tick size: ${suite.instrument.tickSize}\") + # Get instrument info if needed + instrument = await suite.client.get_instrument(suite.instrument_id or \"MNQ\") + print(f\"Trading {instrument.name} - Tick size: ${instrument.tickSize}\") data = await suite.client.get_bars(\"MNQ\", days=5) print(f\"Retrieved {len(data)} bars\") @@ -173,18 +175,20 @@ if __name__ == \"__main__\": import asyncio from project_x_py import TradingSuite -async def on_tick(tick_data): +async def on_tick(event): + tick_data = event.data print(f\"Price: ${tick_data['price']}\") async def main(): suite = await TradingSuite.create(\"MNQ\") - suite.data.add_tick_callback(on_tick) + # Register tick callback + await suite.data.add_callback(\"tick\", on_tick) current_price = await suite.data.get_current_price() response = await suite.orders.place_bracket_order( - contract_id=suite.instrument.id, + contract_id=suite.instrument_id, side=0, # Buy size=1, entry_price=current_price, @@ -267,28 +271,43 @@ await suite.orders.cancel_order(order_id) #### PositionManager Async position tracking and analytics: ```python -position_manager = suite["position_manager"] -positions = await position_manager.get_all_positions() -pnl = await position_manager.get_portfolio_pnl() -await position_manager.close_position(contract_id) +positions = await suite.positions.get_all_positions() +pnl = await suite.positions.get_portfolio_pnl() +await suite.positions.close_position(contract_id) ``` #### RealtimeDataManager Async multi-timeframe data management: ```python -data_manager = suite["data_manager"] -await data_manager.initialize(initial_days=5) -data = await data_manager.get_data("15min") -current_price = await data_manager.get_current_price() +# Data manager is automatically initialized +data = await suite.data.get_data("15min") +current_price = await suite.data.get_current_price() ``` #### OrderBook -Async Level 2 market depth analysis: +Async Level 2 market depth analysis (when enabled): ```python -orderbook = suite["orderbook"] -spread = await orderbook.get_bid_ask_spread() -imbalance = await orderbook.get_market_imbalance() -icebergs = await orderbook.detect_iceberg_orders() +# Enable orderbook in features when creating suite +suite = await TradingSuite.create("MNQ", features=["orderbook"]) + +spread = await suite.orderbook.get_bid_ask_spread() +imbalance = await suite.orderbook.get_market_imbalance() +icebergs = await suite.orderbook.detect_iceberg_orders() +``` + +#### RiskManager +Risk management and managed trades (when enabled): +```python +# Enable risk manager in features +suite = await TradingSuite.create("MNQ", features=["risk_manager"]) + +# Use managed trades for automatic risk management +async with suite.managed_trade(max_risk_percent=0.01) as trade: + # Market price fetched automatically (v3.1.11+) + result = await trade.enter_long( + stop_loss=current_price - 50, + take_profit=current_price + 100 + ) ``` ### Technical Indicators @@ -330,17 +349,33 @@ data_with_ob = ob.calculate(data, use_wicks=True) The `examples/` directory contains comprehensive async examples: -1. **01_basic_client_connection.py** - Async authentication and basic operations -2. **02_order_management.py** - Async order placement and management -3. **03_position_management.py** - Async position tracking and P&L -4. **04_realtime_data.py** - Real-time async data streaming -5. **05_orderbook_analysis.py** - Async market depth analysis -6. **06_multi_timeframe_strategy.py** - Async multi-timeframe trading -7. **07_technical_indicators.py** - Using indicators with async data -8. **08_order_and_position_tracking.py** - Integrated async monitoring -9. **09_get_check_available_instruments.py** - Interactive async instrument search -10. **12_simplified_strategy.py** - NEW: Simplified strategy using auto-initialization -11. **13_factory_comparison.py** - NEW: Comparison of factory function approaches +### Core Functionality +- **00_trading_suite_demo.py** - Complete TradingSuite demonstration +- **01_basic_client_connection.py** - Async authentication and basic operations +- **02_order_management.py** - Async order placement and management +- **03_position_management.py** - Async position tracking and P&L +- **04_realtime_data.py** - Real-time async data streaming + +### Advanced Features +- **05_orderbook_analysis.py** - Async market depth analysis +- **06_advanced_orderbook.py** - Advanced orderbook analytics +- **06_multi_timeframe_strategy.py** - Async multi-timeframe trading +- **07_technical_indicators.py** - Using indicators with async data +- **08_order_and_position_tracking.py** - Integrated async monitoring +- **09_get_check_available_instruments.py** - Interactive async instrument search + +### Event System & Data Access +- **10_unified_event_system.py** - Event-driven trading with EventBus +- **11_simplified_data_access.py** - Simplified data access patterns +- **12_simplified_multi_timeframe.py** - Multi-timeframe analysis +- **12_simplified_strategy.py** - Simplified strategy using auto-initialization + +### Risk Management & Order Lifecycle +- **13_enhanced_models.py** - Enhanced data models demonstration +- **15_order_lifecycle_tracking.py** - Complete order lifecycle monitoring +- **15_risk_management.py** - Risk management features +- **16_managed_trades.py** - ManagedTrade context manager usage +- **16_join_orders.py** - Advanced order joining techniques ## πŸ”§ Configuration @@ -415,15 +450,16 @@ async def place_order(self, ...): ## πŸ”§ Troubleshooting -### Common Issues with Factory Functions +### Common Issues -#### JWT Token Not Available +#### Authentication Issues ```python -# Error: "JWT token is required but not available from client" -# Solution: Ensure client is authenticated before creating suite -async with ProjectX.from_env() as client: - await client.authenticate() # Don't forget this! - suite = await create_initialized_trading_suite("MNQ", client) +# Error: "PROJECT_X_API_KEY environment variable is required" +# Solution: Set environment variables before running +export PROJECT_X_API_KEY="your_api_key" +export PROJECT_X_USERNAME="your_username" + +# Or use config file at ~/.config/projectx/config.json ``` #### Instrument Not Found @@ -435,14 +471,13 @@ async with ProjectX.from_env() as client: #### Connection Timeouts ```python -# If initialization times out, try manual setup with error handling: +# The TradingSuite handles connections automatically +# If you need custom timeout handling: try: - suite = await create_trading_suite( - instrument="MNQ", - project_x=client, - auto_connect=False + suite = await TradingSuite.create( + "MNQ", + timeout=30 # Custom timeout in seconds ) - await suite["realtime_client"].connect() except Exception as e: print(f"Connection failed: {e}") ``` diff --git a/docs/_reference_docs/ASYNC_MIGRATION_GUIDE.md b/docs/_reference_docs/ASYNC_MIGRATION_GUIDE.md deleted file mode 100644 index 839770b..0000000 --- a/docs/_reference_docs/ASYNC_MIGRATION_GUIDE.md +++ /dev/null @@ -1,450 +0,0 @@ -# Async Migration Guide for project-x-py - -This guide helps you migrate from the synchronous ProjectX SDK to the new async/await architecture. -**Note:** All public classes and factory functions are now async-ready by defaultβ€”no Async* prefix is required. - -## Table of Contents - -1. [Overview](#overview) -2. [Key Benefits](#key-benefits) -3. [Breaking Changes](#breaking-changes) -4. [Migration Steps](#migration-steps) -5. [Code Examples](#code-examples) -6. [Common Patterns](#common-patterns) -7. [Troubleshooting](#troubleshooting) -8. [Performance Tips](#performance-tips) - -## Overview - -The project-x-py SDK has been completely refactored to use Python's async/await patterns. This enables: - -- **Concurrent Operations**: Execute multiple API calls simultaneously -- **Non-blocking I/O**: Process WebSocket events without blocking other operations -- **Better Resource Usage**: Single thread can handle many concurrent connections -- **Improved Responsiveness**: UI/strategy code won't freeze during API calls - -## Key Benefits - -### 1. Concurrent API Calls - -**Before (Synchronous):** -```python -# Sequential - takes 3+ seconds -positions = client.get_positions() # 1 second -orders = client.get_orders() # 1 second -instruments = client.search_instruments("MNQ") # 1 second -``` - -**After (Async):** -```python -# Concurrent - takes ~1 second total -positions, orders, instruments = await asyncio.gather( - client.get_positions(), - client.get_orders(), - client.search_instruments("MNQ") -) -``` - -### 2. Real-time Event Handling - -**Before:** -```python -# Blocking callback -def on_position_update(data): - process_data(data) # Blocks other events - -realtime_client.add_callback("position_update", on_position_update) -``` - -**After:** -```python -# Non-blocking async callback -async def on_position_update(data): - await process_data(data) # Doesn't block - -await realtime_client.add_callback("position_update", on_position_update) -``` - -## Breaking Changes - -1. **All API methods are now async** - Must use `await` -2. **Context managers are async** - Use `async with` -3. **Callbacks can be async** - Better event handling -4. **Imports updated** - All classes and factory functions are async-ready and use the canonical names (no Async* prefix). - -## Migration Steps - -### Step 1: Update Imports - -```python -# Old imports -from project_x_py import ProjectX, create_trading_suite - -# New imports (all classes/factories are async-ready by default) -from project_x_py import ProjectX, create_trading_suite -``` - -### Step 2: Update Client Creation - -```python -# Old synchronous client -client = ProjectX.from_env() -client.authenticate() - -# New async client -async with ProjectX.from_env() as client: - await client.authenticate() -``` - -### Step 3: Update API Calls - -```python -# Old synchronous calls -positions = client.search_open_positions() -instrument = client.get_instrument("MGC") -data = client.get_data("MGC", days=5) - -# New async calls -positions = await client.search_open_positions() -instrument = await client.get_instrument("MGC") -data = await client.get_data("MGC", days=5) -``` - -### Step 4: Update Manager Usage - -```python -# Old synchronous managers -order_manager = create_order_manager(client) -position_manager = create_position_manager(client) - -# New async managers (all managers are now async-ready by default) -order_manager = create_order_manager(client) -await order_manager.initialize() - -position_manager = create_position_manager(client) -await position_manager.initialize() -``` - -## Code Examples - -### Basic Connection - -**Synchronous:** -```python -from project_x_py import ProjectX - -def main(): - client = ProjectX.from_env() - account = client.get_account_info() - print(f"Connected as: {account.name}") -``` - -**Async (now using canonical names):** -```python -import asyncio -from project_x_py import ProjectX - -async def main(): - async with ProjectX.from_env() as client: - await client.authenticate() - print(f"Connected as: {client.account_info.name}") - -asyncio.run(main()) -``` - -### Order Management - -**Synchronous:** -```python -order_manager = create_order_manager(client) -response = order_manager.place_market_order("MGC", 0, 1) -orders = order_manager.search_open_orders() -``` - -**Async (now using canonical names):** -```python -order_manager = create_order_manager(client) -await order_manager.initialize() - -response = await order_manager.place_market_order("MGC", 0, 1) -orders = await order_manager.search_open_orders() -``` - -### Real-time Data - -**Synchronous:** -```python -realtime_client = create_realtime_client(jwt_token, account_id) -data_manager = create_data_manager("MGC", client, realtime_client) - -realtime_client.connect() -data_manager.initialize() -data_manager.start_realtime_feed() -``` - -**Async (now using canonical names):** -```python -realtime_client = create_realtime_client(jwt_token, account_id) -data_manager = create_data_manager("MGC", client, realtime_client) - -await realtime_client.connect() -await data_manager.initialize() -await data_manager.start_realtime_feed() -``` - -### Complete Trading Suite - -**Synchronous:** -```python -suite = create_trading_suite( - "MGC", client, jwt_token, account_id, - timeframes=["5min", "15min"] -) - -suite["realtime_client"].connect() -suite["data_manager"].initialize() -``` - -**Async (now using canonical names):** -```python -suite = await create_trading_suite( - "MGC", client, jwt_token, account_id, - timeframes=["5min", "15min"] -) - -await suite["realtime_client"].connect() -await suite["data_manager"].initialize() -``` - -## Common Patterns - -All public classes and factory functions are async-readyβ€”use canonical names (no Async* prefix). - -### 1. Concurrent Operations - -```python -# Fetch multiple datasets concurrently -async def get_market_overview(client): - tasks = [] - for symbol in ["MGC", "MNQ", "MES"]: - tasks.append(client.get_data(symbol, days=1)) - - results = await asyncio.gather(*tasks) - return dict(zip(["MGC", "MNQ", "MES"], results)) -``` - -### 2. Error Handling - -```python -# Proper async error handling -async def safe_order_placement(order_manager, symbol, side, size): - try: - response = await order_manager.place_market_order(symbol, side, size) - return response - except ProjectXOrderError as e: - logger.error(f"Order failed: {e}") - return None -``` - -### 3. Event-Driven Patterns - -```python -# Async event handlers -async def setup_event_handlers(realtime_client, order_manager): - async def on_order_fill(data): - # Non-blocking processing - await process_fill(data) - await send_notification(data) - - await realtime_client.add_callback("order_update", on_order_fill) -``` - -### 4. Background Tasks - -```python -# Run background monitoring -async def monitor_positions(position_manager): - while True: - positions = await position_manager.get_all_positions() - pnl = await position_manager.get_portfolio_pnl() - - if pnl < -1000: # Stop loss - await close_all_positions(position_manager) - break - - await asyncio.sleep(5) # Check every 5 seconds - -# Run as background task -monitor_task = asyncio.create_task(monitor_positions(position_manager)) -``` - -## Troubleshooting - -### Common Issues - -1. **"RuntimeError: This event loop is already running"** - - Don't use `asyncio.run()` inside Jupyter notebooks - - Use `await` directly in notebook cells - -2. **"coroutine was never awaited"** - - You forgot to use `await` with an async method - - Add `await` before the method call - -3. **"async with outside async function"** - - Wrap your code in an async function - - Use `asyncio.run(main())` to execute - -4. **Mixing sync and async code** - - Use all async components together - - Don't mix sync and async managers - -### Best Practices - -1. **Always use async context managers:** - ```python - async with ProjectX.from_env() as client: - # Client is properly cleaned up - ``` - -2. **Group related operations:** - ```python - # Good - concurrent execution - positions, orders = await asyncio.gather( - position_manager.get_all_positions(), - order_manager.search_open_orders() - ) - ``` - -3. **Handle cleanup properly:** - ```python - try: - await realtime_client.connect() - # ... do work ... - finally: - await realtime_client.cleanup() - ``` - -## Performance Tips - -### 1. Use Concurrent Operations - -```python -# Slow - sequential -for symbol in symbols: - data = await client.get_data(symbol) - process(data) - -# Fast - concurrent -tasks = [client.get_data(symbol) for symbol in symbols] -all_data = await asyncio.gather(*tasks) -for data in all_data: - process(data) -``` - -### 2. Avoid Blocking Operations - -```python -# Bad - blocks event loop -def heavy_calculation(data): - time.sleep(1) # Blocks! - return result - -# Good - non-blocking -async def heavy_calculation(data): - await asyncio.sleep(1) # Non-blocking - # Or run in executor for CPU-bound work - loop = asyncio.get_event_loop() - return await loop.run_in_executor(None, cpu_bound_work, data) -``` - -### 3. Batch Operations - -```python -# Efficient batch processing -async def process_orders_batch(order_manager, orders): - tasks = [] - for order in orders: - task = order_manager.place_limit_order(**order) - tasks.append(task) - - # Place all orders concurrently - results = await asyncio.gather(*tasks, return_exceptions=True) - return results -``` - -## Example: Complete Migration - -Here's a complete example showing a trading bot migration: - -**Old Synchronous Bot:** -```python -from project_x_py import ProjectX, create_trading_suite - -def trading_bot(): - client = ProjectX.from_env() - - suite = create_trading_suite( - "MGC", client, client.session_token, - client.account_info.id - ) - - suite["realtime_client"].connect() - suite["data_manager"].initialize() - - while True: - data = suite["data_manager"].get_data("5min") - positions = suite["position_manager"].get_all_positions() - - signal = analyze(data) - if signal: - suite["order_manager"].place_market_order("MGC", 0, 1) - - time.sleep(60) -``` - -**New Async Bot (now using canonical names):** -```python -import asyncio -from project_x_py import ProjectX, create_trading_suite - -async def trading_bot(): - async with ProjectX.from_env() as client: - await client.authenticate() - - suite = await create_trading_suite( - "MGC", client, client.jwt_token, - client.account_info.id - ) - - await suite["realtime_client"].connect() - await suite["data_manager"].initialize() - - while True: - # Concurrent data fetching - data, positions = await asyncio.gather( - suite["data_manager"].get_data("5min"), - suite["position_manager"].get_all_positions() - ) - - signal = analyze(data) - if signal: - await suite["order_manager"].place_market_order("MGC", 0, 1) - - await asyncio.sleep(60) - -# Run the bot -asyncio.run(trading_bot()) -``` - -## Summary - -The async migration provides significant benefits: - -- **3-5x faster** for concurrent operations -- **Non-blocking** real-time event handling -- **Better resource usage** with single-threaded concurrency -- **Modern Python** patterns for cleaner code - -Start by migrating small scripts, then move to larger applications. The async patterns will quickly become natural and you'll appreciate the performance benefits! - -For more examples, see the `examples/async_*.py` files in the repository. \ No newline at end of file diff --git a/docs/_reference_docs/ERROR_HANDLING_MIGRATION_GUIDE.md b/docs/_reference_docs/ERROR_HANDLING_MIGRATION_GUIDE.md deleted file mode 100644 index 5678e55..0000000 --- a/docs/_reference_docs/ERROR_HANDLING_MIGRATION_GUIDE.md +++ /dev/null @@ -1,519 +0,0 @@ -# Error Handling and Logging Migration Guide - -This guide provides instructions for migrating existing code to use the new centralized error handling and logging utilities introduced in Phase 4 of the refactoring plan. - -## Overview - -The new error handling system provides: -- Consistent error handling patterns via decorators -- Structured logging with JSON support -- Automatic retry logic for network operations -- Standardized error messages and codes -- Better error context and debugging information - -## Migration Steps - -### 1. Replace Manual Error Handling with Decorators - -#### Before: -```python -async def get_orders(self) -> list[Order]: - try: - response = await self._make_request("GET", "/orders") - return [Order(**order) for order in response] - except httpx.HTTPError as e: - self.logger.error(f"Failed to fetch orders: {e}") - raise ProjectXConnectionError(f"Failed to fetch orders: {e}") from e - except Exception as e: - self.logger.error(f"Unexpected error fetching orders: {e}") - raise ProjectXError(f"Unexpected error: {e}") from e -``` - -#### After: -```python -from project_x_py.utils import handle_errors, validate_response - -@handle_errors("fetch orders") -@validate_response(response_type=list) -async def get_orders(self) -> list[Order]: - response = await self._make_request("GET", "/orders") - return [Order(**order) for order in response] -``` - -### 2. Replace Manual Retry Logic - -#### Before: -```python -async def _make_request(self, method: str, endpoint: str, retry_count: int = 0): - try: - response = await self.client.request(method, endpoint) - return response.json() - except httpx.ConnectError as e: - if retry_count < self.config.retry_attempts: - wait_time = 2 ** retry_count - self.logger.warning(f"Connection error, retrying in {wait_time}s: {e}") - await asyncio.sleep(wait_time) - return await self._make_request(method, endpoint, retry_count + 1) - raise ProjectXConnectionError(f"Failed to connect: {e}") from e -``` - -#### After: -```python -from project_x_py.utils import retry_on_network_error - -@retry_on_network_error(max_attempts=3, initial_delay=1.0) -async def _make_request(self, method: str, endpoint: str): - response = await self.client.request(method, endpoint) - return response.json() -``` - -### 3. Use Structured Logging - -#### Before: -```python -import logging - -class OrderManager: - def __init__(self): - self.logger = logging.getLogger(__name__) - - async def place_order(self, order: OrderRequest): - self.logger.info(f"Placing order: {order.symbol} {order.side} {order.size}") - # ... implementation - self.logger.info(f"Order placed successfully: {order_id}") -``` - -#### After: -```python -from project_x_py.utils import ProjectXLogger, LogMessages, log_api_call - -class OrderManager: - def __init__(self): - self.logger = ProjectXLogger.get_logger(__name__) - - async def place_order(self, order: OrderRequest): - self.logger.info( - LogMessages.ORDER_PLACE, - extra={ - "symbol": order.symbol, - "side": order.side, - "size": order.size, - "order_type": order.order_type, - } - ) - - # Track API performance - start_time = time.time() - response = await self._make_request("POST", "/orders", data=order.dict()) - - log_api_call( - self.logger, - method="POST", - endpoint="/orders", - status_code=response.status_code, - duration=time.time() - start_time, - order_id=response.get("id"), - ) -``` - -### 4. Use Standardized Error Messages - -#### Before: -```python -if not self.is_authenticated: - raise ProjectXAuthenticationError("Not authenticated. Please login first.") - -if order.size <= 0: - raise ProjectXOrderError(f"Invalid order size: {order.size}") - -if instrument is None: - raise ProjectXInstrumentError(f"Instrument not found: {symbol}") -``` - -#### After: -```python -from project_x_py.utils import ErrorMessages, format_error_message - -if not self.is_authenticated: - raise ProjectXAuthenticationError(ErrorMessages.AUTH_SESSION_EXPIRED) - -if order.size <= 0: - raise ProjectXOrderError( - format_error_message(ErrorMessages.ORDER_INVALID_SIZE, size=order.size) - ) - -if instrument is None: - raise ProjectXInstrumentError( - format_error_message(ErrorMessages.INSTRUMENT_NOT_FOUND, symbol=symbol) - ) -``` - -### 5. Handle Rate Limiting - -#### Before: -```python -async def get_market_data(self, symbol: str): - try: - return await self._make_request("GET", f"/market/{symbol}") - except ProjectXError as e: - if e.error_code == 429: # Rate limited - # Manual rate limit handling - retry_after = int(e.response_data.get("retry_after", 60)) - await asyncio.sleep(retry_after) - return await self.get_market_data(symbol) - raise -``` - -#### After: -```python -from project_x_py.utils import handle_rate_limit - -@handle_rate_limit(fallback_delay=60.0) -async def get_market_data(self, symbol: str): - return await self._make_request("GET", f"/market/{symbol}") -``` - -### 6. Batch Error Handling - -#### Before: -```python -async def process_orders(self, orders: list[OrderRequest]): - results = [] - errors = [] - - for order in orders: - try: - result = await self.place_order(order) - results.append(result) - except Exception as e: - errors.append((order.id, str(e))) - self.logger.error(f"Failed to place order {order.id}: {e}") - - if errors: - self.logger.error(f"Failed to place {len(errors)} orders") - - return results, errors -``` - -#### After: -```python -from project_x_py.utils import ErrorContext - -async def process_orders(self, orders: list[OrderRequest]): - results = [] - - async with ErrorContext("process orders", logger=self.logger) as ctx: - for order in orders: - try: - result = await self.place_order(order) - results.append(result) - except Exception as e: - ctx.add_error(f"order_{order.id}", e) - - return results, ctx.errors -``` - -### 7. Enhanced Exception Context - -#### Before: -```python -async def execute_trade(self, trade: TradeRequest): - try: - # ... implementation - except Exception as e: - self.logger.error(f"Trade execution failed: {e}") - raise ProjectXError(f"Trade execution failed: {e}") from e -``` - -#### After: -```python -from project_x_py.utils import enhance_exception - -async def execute_trade(self, trade: TradeRequest): - try: - # ... implementation - except Exception as e: - raise enhance_exception( - e, - operation="execute_trade", - instrument=trade.instrument, - size=trade.size, - side=trade.side, - strategy=trade.strategy_name, - ) -``` - -## Module-Specific Migration Examples - -### Client HTTP Module - -```python -# client/http.py -from project_x_py.utils import ( - handle_errors, - retry_on_network_error, - ProjectXLogger, - LogMessages, - log_api_call, -) - -class HttpMixin: - def __init__(self): - self.logger = ProjectXLogger.get_logger(__name__) - - @handle_errors("API request") - @retry_on_network_error( - max_attempts=3, - initial_delay=1.0, - retry_on=(httpx.ConnectError, httpx.TimeoutException) - ) - async def _make_request( - self, - method: str, - endpoint: str, - data: dict | None = None, - ) -> dict: - start_time = time.time() - - # Make request - response = await self.client.request( - method, - self.base_url + endpoint, - json=data, - ) - - # Log API call - log_api_call( - self.logger, - method=method, - endpoint=endpoint, - status_code=response.status_code, - duration=time.time() - start_time, - ) - - response.raise_for_status() - return response.json() -``` - -### Order Manager Module - -```python -# order_manager/core.py -from project_x_py.utils import ( - handle_errors, - validate_response, - ErrorMessages, - format_error_message, - LogContext, -) - -class OrderManagerCore: - @handle_errors("place market order") - @validate_response(required_fields=["id", "status"]) - async def place_market_order( - self, - contract_id: str, - side: int, - size: int, - ) -> OrderPlaceResponse: - # Add logging context - with LogContext( - self.logger, - operation="place_market_order", - contract_id=contract_id, - side=side, - size=size, - ): - # Validate inputs - if size <= 0: - raise ProjectXOrderError( - format_error_message( - ErrorMessages.ORDER_INVALID_SIZE, - size=size - ) - ) - - # Place order - response = await self._submit_order( - contract_id=contract_id, - side=side, - size=size, - order_type="MARKET", - ) - - return OrderPlaceResponse(**response) -``` - -### WebSocket Module - -```python -# realtime.py -from project_x_py.utils import ( - handle_errors, - retry_on_network_error, - ErrorContext, - LogMessages, -) - -class ProjectXRealtimeClient: - @handle_errors("WebSocket connection") - @retry_on_network_error(max_attempts=5, initial_delay=2.0) - async def connect(self) -> bool: - self.logger.info(LogMessages.WS_CONNECT) - - try: - await self._ws.connect() - self.logger.info(LogMessages.WS_CONNECTED) - return True - except Exception as e: - self.logger.error( - LogMessages.WS_CONNECTION_FAILED, - extra={"reason": str(e)} - ) - raise -``` - -## Best Practices - -### 1. Decorator Order -When using multiple decorators, apply them in this order: -```python -@handle_errors("operation name") # Outermost - catches all errors -@handle_rate_limit() # Handle rate limits -@retry_on_network_error() # Retry on network errors -@validate_response() # Innermost - validates response -async def my_method(): - pass -``` - -### 2. Logging Context -Use structured logging with extra fields: -```python -self.logger.info( - "Processing order", - extra={ - "order_id": order.id, - "symbol": order.symbol, - "size": order.size, - "user_id": self.user_id, - } -) -``` - -### 3. Error Messages -Always use error message constants: -```python -# Good -raise ProjectXError( - format_error_message(ErrorMessages.ORDER_NOT_FOUND, order_id=order_id) -) - -# Bad -raise ProjectXError(f"Order not found: {order_id}") -``` - -### 4. Performance Logging -Track operation performance: -```python -from project_x_py.utils import log_performance - -start_time = time.time() -result = await expensive_operation() -log_performance( - self.logger, - "expensive_operation", - start_time, - items_processed=len(result), -) -``` - -## Configuration - -### SDK-Wide Logging Configuration -```python -# In your application startup -from project_x_py.utils import configure_sdk_logging - -# Development -configure_sdk_logging( - level=logging.DEBUG, - format_json=False, -) - -# Production -configure_sdk_logging( - level=logging.INFO, - format_json=True, - log_file="/var/log/projectx/app.log", -) -``` - -### Environment Variables -```bash -# Control logging -export PROJECTX_LOG_LEVEL=DEBUG -export PROJECTX_LOG_FORMAT=json - -# Control error handling -export PROJECTX_MAX_RETRIES=5 -export PROJECTX_RETRY_DELAY=2.0 -``` - -## Testing - -When testing code with error handling decorators: - -```python -import pytest -from unittest.mock import Mock - -@pytest.mark.asyncio -async def test_with_error_handling(): - # Mock the logger to verify error logging - mock_logger = Mock() - - # Test successful case - result = await my_decorated_function() - assert result is not None - - # Test error case - with pytest.raises(ProjectXError): - await my_failing_function() - - # Verify error was logged - mock_logger.error.assert_called() -``` - -## Gradual Migration Strategy - -1. **Phase 1**: Migrate critical paths (authentication, order placement) -2. **Phase 2**: Migrate all API client methods -3. **Phase 3**: Migrate WebSocket and real-time components -4. **Phase 4**: Migrate utility functions and helpers -5. **Phase 5**: Remove old error handling code - -## Checklist - -For each module being migrated: - -- [ ] Replace manual try/except with `@handle_errors` -- [ ] Add `@retry_on_network_error` to network operations -- [ ] Add `@handle_rate_limit` to API methods -- [ ] Add `@validate_response` where appropriate -- [ ] Replace logger creation with `ProjectXLogger.get_logger()` -- [ ] Use `LogMessages` constants for common operations -- [ ] Replace hardcoded error strings with `ErrorMessages` -- [ ] Add structured logging with `extra` fields -- [ ] Use `ErrorContext` for batch operations -- [ ] Add performance logging for slow operations -- [ ] Update tests to work with decorators -- [ ] Remove old error handling code - -## Benefits After Migration - -1. **Consistent Error Messages**: Users see standardized, helpful error messages -2. **Better Debugging**: Structured logs with context make debugging easier -3. **Automatic Retries**: Network issues are handled automatically -4. **Performance Tracking**: Built-in performance metrics -5. **Reduced Code**: Less boilerplate error handling code -6. **Better Testing**: Easier to test with consistent patterns \ No newline at end of file diff --git a/docs/_reference_docs/FACTORY_REMOVAL_PLAN.md b/docs/_reference_docs/FACTORY_REMOVAL_PLAN.md deleted file mode 100644 index 6f82e22..0000000 --- a/docs/_reference_docs/FACTORY_REMOVAL_PLAN.md +++ /dev/null @@ -1,163 +0,0 @@ -# Factory Functions Removal Plan - -## Overview -The new TradingSuite class (v3.0.0) completely replaces all factory functions, providing a cleaner, more intuitive API. This document tracks the removal of obsolete factory functions. - -## Obsolete Factory Functions to Remove - -### 1. `create_trading_suite()` (340 lines!) -**Replaced by:** `TradingSuite.create()` -- Old: Complex 340-line function with many parameters -- New: Simple class method with sensible defaults -- **Action:** DELETE after examples updated - -### 2. `create_initialized_trading_suite()` -**Replaced by:** `TradingSuite.create()` (auto-initializes by default) -- Old: Wrapper around create_trading_suite -- New: Built into TradingSuite.create() -- **Action:** DELETE after examples updated - -### 3. `create_order_manager()` -**Replaced by:** `suite.orders` (automatically created) -- Old: Manual instantiation required -- New: Automatically wired in TradingSuite -- **Action:** DELETE immediately (no direct usage in examples) - -### 4. `create_position_manager()` -**Replaced by:** `suite.positions` (automatically created) -- Old: Manual instantiation with optional params -- New: Automatically wired with proper dependencies -- **Action:** DELETE immediately (no direct usage in examples) - -### 5. `create_realtime_client()` -**Replaced by:** Internal to TradingSuite -- Old: Manual WebSocket client creation -- New: Automatically created and managed -- **Action:** DELETE after checking internal usage - -### 6. `create_data_manager()` -**Replaced by:** `suite.data` (automatically created) -- Old: Manual instantiation with timeframes -- New: Automatically created with config -- **Action:** DELETE immediately (no direct usage in examples) - -## Comparison - -### Old Way (v2.x) -```python -# 50+ lines of setup -async with ProjectX.from_env() as client: - await client.authenticate() - - # Manual component creation - realtime_client = create_realtime_client( - jwt_token=client.session_token, - account_id=str(client.account_info.id) - ) - - # More manual creation - data_manager = create_data_manager( - instrument="MNQ", - project_x=client, - realtime_client=realtime_client, - timeframes=["1min", "5min"] - ) - - order_manager = create_order_manager(client, realtime_client) - position_manager = create_position_manager(client, realtime_client, order_manager) - - # Manual connections - await realtime_client.connect() - await realtime_client.subscribe_user_updates() - await position_manager.initialize(realtime_client, order_manager) - await data_manager.initialize() - - # Manual subscriptions - instrument_info = await client.get_instrument("MNQ") - await realtime_client.subscribe_market_data([instrument_info.id]) - await data_manager.start_realtime_feed() - - # Now ready to use... -``` - -### New Way (v3.0) -```python -# 1 line! -suite = await TradingSuite.create("MNQ") -# Everything is ready to use! -``` - -## Files to Update - -### Examples Using Factory Functions -1. `examples/integrated_trading_suite.py` - Uses `create_trading_suite` -2. `examples/factory_functions_demo.py` - Demonstrates all factory functions -3. `examples/12_simplified_strategy.py` - Uses `create_initialized_trading_suite` -4. `examples/13_factory_comparison.py` - Compares factory approaches - -### Test Files -1. `tests/test_factory_functions.py` - Tests factory functions directly - -## Removal Steps - -### Phase 1: Immediate Removals (Safe) -These can be removed now as they're not directly used in examples: -- `create_order_manager()` -- `create_position_manager()` -- `create_data_manager()` - -### Phase 2: After Example Updates -These need examples updated first: -- `create_trading_suite()` -- `create_initialized_trading_suite()` -- `create_realtime_client()` - -### Phase 3: Clean Up Exports -Remove from `__all__` in `__init__.py`: -- "create_data_manager" -- "create_initialized_trading_suite" -- "create_order_manager" -- "create_position_manager" -- "create_realtime_client" -- "create_trading_suite" - -## Benefits of Removal - -1. **Simpler API Surface**: 6 functions β†’ 1 class -2. **Less Code**: ~500 lines removed -3. **No Confusion**: One obvious way to initialize -4. **Better Maintenance**: Single point of initialization logic -5. **Cleaner Documentation**: Focus on TradingSuite only - -## Migration for Users - -### Before (v2.x) -```python -suite = await create_trading_suite( - instrument="MNQ", - project_x=client, - timeframes=["1min", "5min"], - enable_orderbook=True, - auto_connect=True, - auto_subscribe=True, - initial_days=5 -) -``` - -### After (v3.0) -```python -suite = await TradingSuite.create( - "MNQ", - timeframes=["1min", "5min"], - features=["orderbook"], - initial_days=5 -) -``` - -## Timeline - -1. **NOW**: Create this removal plan βœ… -2. **Day 2**: Update examples to use TradingSuite -3. **Day 2**: Remove all factory functions -4. **Day 2**: Update tests -5. **Day 2**: Clean up __init__.py exports \ No newline at end of file diff --git a/docs/_reference_docs/PHASE3_ANALYSIS.md b/docs/_reference_docs/PHASE3_ANALYSIS.md deleted file mode 100644 index 14b3e70..0000000 --- a/docs/_reference_docs/PHASE3_ANALYSIS.md +++ /dev/null @@ -1,54 +0,0 @@ -# Phase 3: Utility Functions Refactoring Analysis - -## Current State Analysis - -### Overlapping Functionality - -1. **Bid-Ask Spread Analysis** - - `utils/market_microstructure.py::analyze_bid_ask_spread()`: Generic spread analysis on any DataFrame - - `orderbook/analytics.py`: Has spread tracking built into orderbook operations - - **Overlap**: Both calculate spread metrics, but orderbook version is integrated with real-time data - -2. **Volume Profile** - - `utils/market_microstructure.py::calculate_volume_profile()`: Generic volume profile on any DataFrame - - `orderbook/profile.py::get_volume_profile()`: Orderbook-specific volume profile using trade data - - **Overlap**: Nearly identical logic for binning and calculating POC/Value Area - -### Other Utility Files - -- `utils/trading_calculations.py`: Generic trading math (tick values, position sizing) - No overlap -- `utils/data_utils.py`: Data manipulation utilities - No overlap -- `utils/formatting.py`: Display formatting - No overlap -- `utils/pattern_detection.py`: Technical pattern detection - No overlap -- `utils/portfolio_analytics.py`: Portfolio-level analytics - No overlap - -## Refactoring Recommendations - -### 1. Move Orderbook-Specific Analysis -- **Move** `analyze_bid_ask_spread()` logic into `orderbook/analytics.py` as a method that can work on historical data -- **Move** `calculate_volume_profile()` logic into `orderbook/profile.py` as a static analysis method - -### 2. Keep Generic Market Analysis in Utils -- Create a new `utils/market_analysis.py` for truly generic market calculations that don't belong to any specific domain -- Keep functions that work on generic DataFrames without domain knowledge - -### 3. Clear Boundaries - -**Utils (Generic)**: -- Functions that work on any DataFrame/data structure -- No domain-specific knowledge required -- Reusable across different contexts -- Examples: data transformation, mathematical calculations, formatting - -**Domain-Specific (orderbook/)**: -- Functions that understand orderbook structure -- Functions that work with orderbook-specific data types -- Integration with real-time feeds -- Examples: bid-ask analysis, volume profile, liquidity analysis - -## Implementation Plan - -1. **Deprecate** `utils/market_microstructure.py` -2. **Create** static methods in orderbook modules for DataFrame-based analysis -3. **Update** imports in any code using the old functions -4. **Document** the new structure clearly \ No newline at end of file diff --git a/docs/_reference_docs/REFACTORING_PLAN.md b/docs/_reference_docs/REFACTORING_PLAN.md deleted file mode 100644 index 0d8602b..0000000 --- a/docs/_reference_docs/REFACTORING_PLAN.md +++ /dev/null @@ -1,247 +0,0 @@ -# ProjectX Python SDK Refactoring Plan - -## Executive Summary - -This document outlines the refactoring plan for the ProjectX Python SDK v2.0.4. The analysis identified several areas of redundancy, potential issues, and opportunities for improvement while maintaining the current async-first architecture. - -## Key Findings - -### 1. Duplicate RateLimiter Implementations -**Issue**: Two separate RateLimiter classes exist with different implementations: -- `client/rate_limiter.py`: Async sliding window implementation -- `utils/rate_limiter.py`: Synchronous context manager implementation - -**Impact**: Confusion about which to use, inconsistent rate limiting behavior - -### 2. Protocol Files Organization -**Issue**: Multiple `protocols.py` files scattered across packages: -- `client/protocols.py` -- `order_manager/protocols.py` -- No protocols in `position_manager`, `realtime`, `orderbook` packages - -**Impact**: Inconsistent type checking patterns, harder to maintain - -### 3. Type Definition Redundancy -**Issue**: Similar type definitions scattered across: -- `order_manager/types.py` -- `orderbook/types.py` -- `realtime/types.py` -- `realtime_data_manager/types.py` -- `position_manager/types.py` - -**Impact**: Potential for drift between similar types, maintenance overhead - -### 4. Utility Function Overlap -**Issue**: Market microstructure utilities in `utils/market_microstructure.py` have potential overlap with orderbook analytics functionality - -**Impact**: Unclear separation of concerns, possible duplicate implementations - -### 5. Missing Centralized Error Handling -**Issue**: While custom exceptions exist, there's no consistent error handling pattern across modules - -**Impact**: Inconsistent error messages, harder debugging - -### 6. Import Structure Issues -**Issue**: Complex import chains and potential circular dependency risks between managers - -**Impact**: Slower imports, harder to test in isolation - -## Refactoring Plan - -### Phase 1: Consolidate Rate Limiting (Priority: High) βœ… COMPLETED -1. **Remove** `utils/rate_limiter.py` (synchronous version) -2. **Move** `client/rate_limiter.py` to `utils/async_rate_limiter.py` -3. **Update** all imports to use the centralized async rate limiter -4. **Add** comprehensive tests for the unified rate limiter - -### Phase 2: Centralize Type Definitions (Priority: High) βœ… COMPLETED -1. **Create** `project_x_py/types/` package with: - - `base.py`: Core types used across modules - - `trading.py`: Order and position related types - - `market_data.py`: Market data and real-time types - - `protocols.py`: All protocol definitions -2. **Migrate** all protocol files to centralized location -3. **Update** imports across all modules -4. **Remove** redundant type definitions - -### Phase 3: Refactor Utility Functions (Priority: Medium) βœ… COMPLETED -1. **Review** overlap between `utils/market_microstructure.py` and orderbook analytics -2. **Move** orderbook-specific analysis to `orderbook/analytics.py` -3. **Keep** generic market analysis in utils -4. **Document** clear boundaries between utilities and domain-specific code - -### Phase 4: Implement Consistent Error Handling (Priority: Medium) βœ… COMPLETED -1. **Create** `error_handler.py` with centralized error handling decorators -2. **Add** consistent logging patterns for errors -3. **Implement** retry logic decorators for network operations -4. **Standardize** error messages and context - -### Phase 5: Optimize Import Structure (Priority: Low) βœ… COMPLETED -1. **Create** lazy import patterns for heavy dependencies -2. **Move** TYPE_CHECKING imports to reduce runtime overhead -3. **Analyze** and break circular dependencies -4. **Implement** `__all__` exports consistently - -### Phase 6: Clean Up Unused Code (Priority: Low) βœ… COMPLETED -1. **Remove** `__pycache__` directories from version control -2. **Add** `.gitignore` entries for Python cache files -3. **Remove** any dead code identified by static analysis -4. **Update** documentation to reflect changes - -## Implementation Guidelines - -### Breaking Changes Policy -As per CLAUDE.md guidelines: -- **No backward compatibility** required -- **Clean code priority** over compatibility -- **Remove legacy code** when implementing improvements - -### Testing Strategy -1. **Unit tests** for each refactored component -2. **Integration tests** for cross-module functionality -3. **Performance benchmarks** for critical paths -4. **Type checking** with mypy strict mode - -### Migration Path -1. Each phase should be a separate PR -2. Update examples after each phase -3. Run full test suite between phases -4. Update documentation continuously - -## Risk Mitigation - -### High Risk Areas -1. **Rate Limiter Migration**: Could affect API call timing - - Mitigation: Comprehensive testing of rate limit behavior - -2. **Type Consolidation**: Could break type checking - - Mitigation: Run mypy after each change - -3. **Import Restructuring**: Could introduce circular dependencies - - Mitigation: Use import graphs to verify structure - -### Low Risk Areas -1. **Utility refactoring**: Well-isolated functions -2. **Error handling**: Additive changes only -3. **Code cleanup**: No functional impact - -## Success Metrics - -1. **Code Quality** - - Reduced duplicate code by 30% - - Improved type coverage to 95% - - Zero circular dependencies - -2. **Performance** - - Faster import times (target: <0.5s) - - Reduced memory footprint - - Consistent async performance - -3. **Maintainability** - - Clear module boundaries - - Centralized configuration - - Comprehensive documentation - -## Timeline - -- **Week 1-2**: Phase 1 βœ… & Phase 2 βœ… (High priority items) -- **Week 3-4**: Phase 3 βœ… & Phase 4 (Medium priority items) -- **Week 5-6**: Phase 5 & 6 (Low priority items) - -## Progress Updates - -### Phase 1 Completion (Completed) -- βœ… Removed synchronous rate limiter from `utils/rate_limiter.py` -- βœ… Moved async rate limiter to `utils/async_rate_limiter.py` -- βœ… Updated all imports across the codebase -- βœ… Added comprehensive test suite with 9 test cases -- βœ… Enhanced documentation with examples and use cases -- βœ… Verified backward compatibility through client re-export - -### Phase 2 Completion (Completed) -- βœ… Created `project_x_py/types/` package with organized structure -- βœ… Created `base.py` with core types and constants -- βœ… Created `trading.py` with order and position enums/types -- βœ… Created `market_data.py` with orderbook and real-time types -- βœ… Created `protocols.py` consolidating all protocol definitions -- βœ… Updated 23+ files to use centralized imports -- βœ… Removed 7 redundant type definition files -- βœ… Added comprehensive type consistency tests -- βœ… Fixed all protocol method signatures to match implementations -- βœ… Fixed one bug in `calculate_portfolio_pnl` (wrong method name) -- βœ… Resolved all mypy type errors by: - - Removing explicit self type annotations from mixin methods - - Adding TYPE_CHECKING type hints to mixins for attributes from main classes - - Fixing method signature mismatches in protocols -- βœ… All 249 unit tests passing -- βœ… mypy reports "Success: no issues found in 70 source files" - -### Phase 3 Completion (Completed) -- βœ… Reviewed overlap between `utils/market_microstructure.py` and orderbook modules -- βœ… Added `MarketAnalytics.analyze_dataframe_spread()` static method to orderbook/analytics.py -- βœ… Added `VolumeProfile.calculate_dataframe_volume_profile()` static method to orderbook/profile.py -- βœ… Completely removed `utils/market_microstructure.py` to eliminate redundancy -- βœ… Updated all imports and package exports -- βœ… Created comprehensive documentation in `utils/README.md` explaining boundaries -- βœ… Added test suite for new static methods (11 test cases) -- βœ… All tests passing - -### Phase 4 Completion (Completed) -- βœ… Created `utils/error_handler.py` with centralized error handling decorators: - - `@handle_errors` - Consistent error catching, logging, and re-raising - - `@retry_on_network_error` - Exponential backoff retry for network errors - - `@handle_rate_limit` - Automatic retry after rate limit with smart delay - - `@validate_response` - Response structure validation - - `ErrorContext` - Context manager for batch error collection -- βœ… Created `utils/logging_config.py` with consistent logging patterns: - - `StructuredFormatter` - JSON and human-readable log formatting - - `ProjectXLogger` - Factory for configured loggers - - `LogMessages` - Standard log message constants - - `LogContext` - Context manager for adding log context -- βœ… Created `utils/error_messages.py` for standardized error messages: - - `ErrorMessages` - Comprehensive error message templates - - `ErrorCode` - Standardized error codes by category - - Error context and enhancement utilities -- βœ… Added comprehensive test suite (39 test cases) -- βœ… Fixed deprecation warnings for UTC datetime usage -- βœ… All tests passing -- βœ… Created ERROR_HANDLING_MIGRATION_GUIDE.md for implementing the new patterns - -**Note**: The error handling infrastructure has been created but not yet applied throughout the codebase. See ERROR_HANDLING_MIGRATION_GUIDE.md for implementation instructions. - -### Phase 5 Completion (Completed) -- βœ… TYPE_CHECKING imports already well-optimized throughout codebase -- βœ… No circular dependencies found - architecture is clean -- βœ… Added `__all__` exports to key modules: - - `exceptions.py` - All exception classes - - `config.py` - Configuration functions and classes - - `models.py` - All data model classes -- βœ… Created `measure_import_performance.py` script to track import times -- βœ… Measured baseline performance: ~130-160ms per module import -- ❌ Decided NOT to implement lazy imports: - - Only 7-10% improvement for added complexity - - Import time dominated by dependencies (polars), not our code - - Not worth the maintenance overhead - -**Key Findings**: -- The codebase already uses TYPE_CHECKING effectively -- No circular dependencies exist -- Architecture is clean and well-structured -- Import performance is acceptable as-is - -### Phase 6 Completion (Completed) -- βœ… Verified no `__pycache__` directories are tracked in git -- βœ… Confirmed `.gitignore` already has comprehensive Python cache entries: - - `__pycache__/`, `*.py[cod]`, `*$py.class` - - `.mypy_cache/`, `.pytest_cache/` -- βœ… Removed dead code identified by static analysis: - - Fixed 4 unused imports in TYPE_CHECKING blocks - - Cleaned up empty TYPE_CHECKING blocks after import removal -- βœ… Verified no orphaned references to removed modules -- βœ… All tests passing - -**Summary**: The codebase is now clean with no dead code, proper gitignore configuration, and all unnecessary imports removed. - -## Conclusion - -This refactoring plan addresses the identified redundancies and structural issues while maintaining the SDK's async-first architecture. The phased approach ensures minimal disruption while progressively improving code quality and maintainability. \ No newline at end of file diff --git a/docs/_reference_docs/SDK_IMPROVEMENTS_PLAN.md b/docs/_reference_docs/SDK_IMPROVEMENTS_PLAN.md deleted file mode 100644 index 65877cc..0000000 --- a/docs/_reference_docs/SDK_IMPROVEMENTS_PLAN.md +++ /dev/null @@ -1,1073 +0,0 @@ -# SDK Improvements Implementation Plan for v3.0.0 - -## Overview -This document outlines the comprehensive improvements being implemented in the ProjectX Python SDK v3.0.0 to enhance developer experience and make it easier to implement trading strategies. The improvements focus on simplifying common patterns, reducing boilerplate code, and providing better abstractions for strategy developers. - -**Version**: v3.0.0-dev -**Branch**: refactor_v3 -**Start Date**: 2025-08-04 -**Target Completion**: 2025-09-08 (5 weeks) -**Status**: IN PROGRESS - -## Development Philosophy (Updated v3.1.1) -- **Maintain backward compatibility** - Keep existing APIs working with deprecation warnings -- **Clean code with migration paths** - Provide gradual transitions to new implementations -- **Modern patterns** - Use latest Python 3.12+ features while maintaining compatibility -- **Developer experience** - Prioritize simplicity and intuitiveness with stable APIs -- **Semantic versioning** - Follow MAJOR.MINOR.PATCH strictly - -## 1. Event-Driven Architecture Improvements - -### Current State -- Callbacks are scattered across different components -- Each component has its own callback registration system -- No unified way to handle all events - -### Proposed Solution: Unified Event Bus - -#### Implementation Details -```python -# New event_bus.py module -class EventBus: - """Unified event system for all SDK components.""" - - async def on(self, event: str | EventType, handler: Callable) -> None: - """Register handler for event type.""" - - async def emit(self, event: str | EventType, data: Any) -> None: - """Emit event to all registered handlers.""" - - async def once(self, event: str | EventType, handler: Callable) -> None: - """Register one-time handler.""" - -# Integration in TradingSuite -class TradingSuite: - def __init__(self): - self.events = EventBus() - - async def on(self, event: str, handler: Callable) -> None: - """Unified event registration.""" - await self.events.on(event, handler) -``` - -#### Event Types -```python -class EventType(Enum): - # Market Data Events - NEW_BAR = "new_bar" - QUOTE_UPDATE = "quote_update" - TRADE_TICK = "trade_tick" - - # Order Events - ORDER_PLACED = "order_placed" - ORDER_FILLED = "order_filled" - ORDER_CANCELLED = "order_cancelled" - ORDER_REJECTED = "order_rejected" - - # Position Events - POSITION_OPENED = "position_opened" - POSITION_CLOSED = "position_closed" - POSITION_UPDATED = "position_updated" - - # System Events - CONNECTED = "connected" - DISCONNECTED = "disconnected" - ERROR = "error" -``` - -#### Usage Example -```python -suite = await TradingSuite.create("MNQ") - -# Single place for all events -await suite.on(EventType.POSITION_CLOSED, handle_position_closed) -await suite.on(EventType.NEW_BAR, handle_new_bar) -await suite.on(EventType.ORDER_FILLED, handle_order_filled) -``` - -### Implementation Steps -1. Create `event_bus.py` module with EventBus class -2. Add EventType enum with all event types -3. Integrate EventBus into existing components -4. Update components to emit events through the bus -5. Maintain backward compatibility with deprecation warnings -6. Update documentation and examples with migration guides - -### Timeline: 2 weeks - ---- - -## 2. Simplified Data Access - -### Current State -- Requires understanding of internal DataFrame structure -- Multiple steps to get common values -- No caching of frequently accessed values - -### Proposed Solution: Convenience Methods - -#### Implementation Details -```python -# Enhanced RealtimeDataManager -class RealtimeDataManager: - async def get_latest_price(self, timeframe: str = None) -> float: - """Get the most recent close price.""" - - async def get_latest_bar(self, timeframe: str) -> dict: - """Get the most recent complete bar.""" - - async def get_indicator_value(self, indicator: str, timeframe: str, **params) -> float: - """Get latest indicator value with automatic calculation.""" - - async def get_price_change(self, timeframe: str, periods: int = 1) -> float: - """Get price change over N periods.""" - - async def get_volume_profile(self, timeframe: str, periods: int = 20) -> dict: - """Get volume profile for recent periods.""" -``` - -#### Indicator Integration -```python -# Automatic indicator calculation and caching -suite = await TradingSuite.create("MNQ") - -# Instead of manual calculation -rsi = await suite.data.get_indicator_value("RSI", "5min", period=14) -macd = await suite.data.get_indicator_value("MACD", "15min") - -# Bulk indicator access -indicators = await suite.data.get_indicators(["RSI", "MACD", "ATR"], "5min") -``` - -### Implementation Steps -1. Add convenience methods to RealtimeDataManager -2. Implement smart caching for indicator values -3. Create indicator registry for automatic calculation -4. Add method chaining support -5. Update examples to show new patterns - -### Timeline: 1 week - ---- - -## 3. Order Lifecycle Management - -### Current State -- Manual tracking of order states -- No built-in waiting mechanisms -- Complex logic for order monitoring - -### Proposed Solution: Order Tracking Context Manager - -#### Implementation Details -```python -# New order_tracker.py module -class OrderTracker: - """Context manager for order lifecycle tracking.""" - - async def __aenter__(self): - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - await self.cleanup() - - async def wait_for_fill(self, timeout: float = 30) -> Order: - """Wait for order to be filled.""" - - async def wait_for_status(self, status: OrderStatus, timeout: float = 30) -> Order: - """Wait for specific order status.""" - - async def modify_or_cancel(self, new_price: float = None) -> bool: - """Modify order or cancel if modification fails.""" - -# Usage -async with suite.track_order() as tracker: - order = await suite.orders.place_limit_order( - contract_id=instrument.id, - side=OrderSide.BUY, - size=1, - price=current_price - 10 - ) - - try: - filled_order = await tracker.wait_for_fill(timeout=60) - print(f"Order filled at {filled_order.average_price}") - except TimeoutError: - await tracker.modify_or_cancel(new_price=current_price - 5) -``` - -#### Order Chain Builder -```python -# Fluent API for complex orders -order_chain = ( - suite.orders.market_order(size=1) - .with_stop_loss(offset=50) - .with_take_profit(offset=100) - .with_trail_stop(offset=25, trigger_offset=50) -) - -result = await order_chain.execute() -``` - -### Implementation Steps -1. Create OrderTracker class -2. Implement async waiting mechanisms -3. Add order chain builder pattern -4. Create common order templates -5. Add integration tests - -### Timeline: 2 weeks - ---- - -## 4. Better Error Recovery - -### Current State -- Manual reconnection handling -- Lost state during disconnections -- No order queuing during outages - -### Proposed Solution: Automatic Recovery System - -#### Implementation Details -```python -# Enhanced connection management -class ConnectionManager: - """Handles connection lifecycle with automatic recovery.""" - - async def maintain_connection(self): - """Background task to monitor and maintain connections.""" - - async def queue_during_disconnection(self, operation: Callable): - """Queue operations during disconnection.""" - - async def recover_state(self): - """Recover state after reconnection.""" - -# Integration -class TradingSuite: - def __init__(self): - self.connection_manager = ConnectionManager() - self._operation_queue = asyncio.Queue() - - async def execute_with_retry(self, operation: Callable, max_retries: int = 3): - """Execute operation with automatic retry and queuing.""" -``` - -#### State Persistence -```python -# Automatic state saving and recovery -class StateManager: - async def save_state(self, key: str, data: Any): - """Save state to persistent storage.""" - - async def load_state(self, key: str) -> Any: - """Load state from persistent storage.""" - - async def auto_checkpoint(self, interval: int = 60): - """Automatic periodic state checkpointing.""" -``` - -### Implementation Steps -1. Create ConnectionManager class -2. Implement operation queuing system -3. Add state persistence layer -4. Create recovery strategies -5. Add comprehensive logging - -### Timeline: 3 weeks - ---- - -## 5. Simplified Initialization βœ… COMPLETED (2025-08-04) - -### Previous State -- Multiple steps required for setup -- Complex parameter passing -- No sensible defaults - -### Implemented Solution: Single-Line Initialization - -#### Implementation Details -```python -# New simplified API -class TradingSuite: - @classmethod - async def create( - cls, - instrument: str, - timeframes: list[str] = None, - features: list[str] = None, - **kwargs - ) -> 'TradingSuite': - """Create fully initialized trading suite with sensible defaults.""" - - @classmethod - async def from_config(cls, config_path: str) -> 'TradingSuite': - """Create from configuration file.""" - - @classmethod - async def from_env(cls, instrument: str) -> 'TradingSuite': - """Create from environment variables.""" - -# Usage examples -# Simple initialization with defaults -suite = await TradingSuite.create("MNQ") - -# With specific features -suite = await TradingSuite.create( - "MNQ", - timeframes=["1min", "5min", "15min"], - features=["orderbook", "indicators", "risk_manager"] -) - -# From configuration -suite = await TradingSuite.from_config("config/trading.yaml") -``` - -#### Feature Flags -```python -class Features(Enum): - ORDERBOOK = "orderbook" - INDICATORS = "indicators" - RISK_MANAGER = "risk_manager" - TRADE_JOURNAL = "trade_journal" - PERFORMANCE_ANALYTICS = "performance_analytics" -``` - -### Implementation Steps -1. Create new TradingSuite class -2. Implement factory methods -3. Add configuration file support -4. Create feature flag system -5. Update all examples - -### Timeline: 1 week - ---- - -## 6. Strategy-Friendly Data Structures - -### Current State -- Basic data classes with minimal methods -- Manual calculation of common metrics -- No convenience properties - -### Proposed Solution: Enhanced Data Models - -#### Implementation Details -```python -# Enhanced Position class -@dataclass -class Position: - # Existing fields... - - @property - def pnl(self) -> float: - """Current P&L in currency.""" - - @property - def pnl_percent(self) -> float: - """Current P&L as percentage.""" - - @property - def time_in_position(self) -> timedelta: - """Time since position opened.""" - - @property - def is_profitable(self) -> bool: - """Whether position is currently profitable.""" - - def would_be_pnl(self, exit_price: float) -> float: - """Calculate P&L at given exit price.""" - -# Enhanced Order class -@dataclass -class Order: - # Existing fields... - - @property - def time_since_placed(self) -> timedelta: - """Time since order was placed.""" - - @property - def is_pending(self) -> bool: - """Whether order is still pending.""" - - @property - def fill_ratio(self) -> float: - """Percentage of order filled.""" -``` - -#### Trade Statistics -```python -class TradeStatistics: - """Real-time trade statistics.""" - - @property - def win_rate(self) -> float: - """Current win rate percentage.""" - - @property - def profit_factor(self) -> float: - """Gross profit / Gross loss.""" - - @property - def average_win(self) -> float: - """Average winning trade amount.""" - - @property - def average_loss(self) -> float: - """Average losing trade amount.""" - - @property - def sharpe_ratio(self) -> float: - """Current Sharpe ratio.""" -``` - -### Implementation Steps -1. Enhance Position class with properties -2. Enhance Order class with properties -3. Create TradeStatistics class -4. Add calculation utilities -5. Update type hints - -### Timeline: 1 week - ---- - -## 7. Built-in Risk Management Helpers - -### Current State -- Manual position sizing calculations -- No automatic stop-loss attachment -- Basic risk calculations - -### Proposed Solution: Risk Management Module - -#### Implementation Details -```python -# New risk_manager.py module -class RiskManager: - """Comprehensive risk management system.""" - - def __init__(self, account: Account, config: RiskConfig): - self.account = account - self.config = config - - async def calculate_position_size( - self, - entry_price: float, - stop_loss: float, - risk_amount: float = None, - risk_percent: float = None - ) -> int: - """Calculate position size based on risk.""" - - async def validate_trade(self, order: Order) -> tuple[bool, str]: - """Validate trade against risk rules.""" - - async def attach_risk_orders(self, position: Position) -> BracketOrderResponse: - """Automatically attach stop-loss and take-profit.""" - - async def adjust_stops(self, position: Position, new_stop: float) -> bool: - """Adjust stop-loss orders for position.""" - -# Risk configuration -@dataclass -class RiskConfig: - max_risk_per_trade: float = 0.01 # 1% per trade - max_daily_loss: float = 0.03 # 3% daily loss - max_position_size: int = 10 # Maximum contracts - max_positions: int = 3 # Maximum concurrent positions - use_trailing_stops: bool = True - trailing_stop_distance: float = 20 -``` - -#### Usage Example -```python -suite = await TradingSuite.create("MNQ", features=["risk_manager"]) - -# Automatic position sizing -size = await suite.risk.calculate_position_size( - entry_price=16000, - stop_loss=15950, - risk_percent=0.01 # Risk 1% of account -) - -# Place order with automatic risk management -async with suite.risk.managed_trade() as trade: - order = await trade.enter_long(size=size, stop_loss=15950, take_profit=16100) - # Risk orders automatically attached and managed -``` - -### Implementation Steps -1. Create RiskManager class -2. Implement position sizing algorithms -3. Add automatic stop-loss attachment -4. Create risk validation rules -5. Add risk analytics - -### Timeline: 2 weeks - ---- - -## 8. Better Type Hints and IDE Support βœ… COMPLETED (2025-08-04) - -### Previous State -- Many `dict[str, Any]` return types -- Magic numbers for enums -- Limited IDE autocomplete - -### Implemented Solution: Comprehensive Type System - -#### Implementation Details -```python -# New comprehensive types module with 50+ type definitions -from project_x_py.types import ( - # Response types for API operations - HealthStatusResponse, - PerformanceStatsResponse, - RiskAnalysisResponse, - OrderbookAnalysisResponse, - - # Configuration types - TradingSuiteConfig, - OrderManagerConfig, - PositionManagerConfig, - - # Statistics types - TradingSuiteStats, - OrderManagerStats, - ComponentStats, - - # Core types - OrderSide, - OrderStatus, - OrderType, - PositionType, -) - -# Example of TypedDict replacing dict[str, Any] -class TradingSuiteStats(TypedDict): - suite_id: str - instrument: str - uptime_seconds: int - connected: bool - components: dict[str, ComponentStats] - realtime_connected: bool - features_enabled: list[str] - # ... 15+ more structured fields -``` - -#### Type System Architecture -The comprehensive type system includes: - -1. **Response Types** (`response_types.py`): - - `HealthStatusResponse` - API health check responses - - `PerformanceStatsResponse` - Performance metrics - - `RiskAnalysisResponse` - Risk analysis results - - `OrderbookAnalysisResponse` - Market microstructure analysis - - 15+ additional response types - -2. **Configuration Types** (`config_types.py`): - - `TradingSuiteConfig` - Suite initialization configuration - - `OrderManagerConfig` - Order management settings - - `RealtimeConfig` - WebSocket connection settings - - `CacheConfig`, `RateLimitConfig` - Performance settings - - 12+ additional configuration types - -3. **Statistics Types** (`stats_types.py`): - - `TradingSuiteStats` - Comprehensive suite statistics - - `OrderManagerStats` - Order execution statistics - - `PositionManagerStats` - Position tracking metrics - - `ConnectionStats` - Real-time connection metrics - - 10+ additional statistics types - -#### Practical Implementation -```python -# Before: dict[str, Any] everywhere -def get_stats(self) -> dict[str, Any]: - return {"connected": True, "data": {...}} - -# After: Structured types with full IDE support -def get_stats(self) -> TradingSuiteStats: - return { - "suite_id": self.suite_id, - "instrument": self.instrument, - "connected": self.is_connected, - "components": self._get_component_stats(), - "uptime_seconds": self._calculate_uptime(), - # ... all fields properly typed - } -``` - -### Implementation Steps Completed -1. βœ… Created comprehensive types module with 4 sub-modules -2. βœ… Replaced `dict[str, Any]` with structured TypedDict definitions -3. βœ… Added 50+ TypedDict definitions covering all major data structures -4. βœ… Updated TradingSuite.get_stats() to use proper typing -5. βœ… Integrated types into main SDK exports - -### Results Achieved -- **50+ new TypedDict definitions** providing complete type safety -- **Zero remaining `dict[str, Any]`** in core public APIs -- **100% IDE autocomplete support** for all structured data -- **Compile-time type checking** for all major operations -- **Comprehensive documentation** in type definitions - ---- - -## Implementation Priority and Timeline - -### Phase 1 (Week 1): Foundation βœ… COMPLETED (2025-08-04) -1. **Simplified Initialization** βœ… COMPLETED (Day 1: 2025-08-04) - - Created new TradingSuite class - - Implemented factory methods (create, from_config, from_env) - - Added feature flags system - - Tested and verified functionality - - Updated all 9 examples to use TradingSuite - - Deleted old factory functions from `__init__.py` -2. **Better Type Hints** βœ… COMPLETED (Day 1: 2025-08-04) - - Created comprehensive types module with 50+ TypedDict definitions - - Replaced all dict[str, Any] with proper structured types - - Added response types, configuration types, and statistics types - - Updated TradingSuite and other components to use proper typing - - Enhanced IDE support with full autocomplete - -### Phase 2 (Week 2): Type System Implementation βœ… COMPLETED (2025-08-04) -1. **Implement New Type Definitions Throughout Project** βœ… COMPLETED (Day 2: 2025-08-04) - - βœ… Replace all remaining dict[str, Any] with proper TypedDict definitions - - βœ… Update OrderManager methods to return OrderManagerStats - - βœ… Update PositionManager methods to return PositionManagerStats - - βœ… Update RealtimeDataManager methods to return RealtimeDataManagerStats - - βœ… Update OrderBook methods to return OrderbookStats - - βœ… Update HTTP client methods to return HealthStatusResponse, PerformanceStatsResponse - - βœ… Replace all analysis methods with structured response types - - βœ… Update position analytics methods with structured responses - - βœ… Update risk calculation methods with RiskAnalysisResponse and PositionSizingResponse - - βœ… Removed all legacy compatibility code from analytics methods - - βœ… Test all type implementations for correctness - -### Phase 3 (Week 3): Event-Driven Architecture βœ… COMPLETED (2025-08-04) -1. **Event-Driven Architecture** βœ… FULLY IMPLEMENTED - - βœ… Created EventBus with full async support - - βœ… Added EventType enum with comprehensive event types - - βœ… Integrated EventBus into TradingSuite with unified API - - βœ… Made EventBus mandatory in all components (RealtimeDataManager, OrderManager, PositionManager, OrderBook) - - βœ… Removed all hasattr checks - EventBus is now required - - βœ… Removed legacy callback systems completely from all components - - βœ… Updated all examples to use EventBus pattern - - βœ… Removed outdated examples that used old patterns - - βœ… Fixed all linting errors and type checking issues - - βœ… Deprecated legacy add_callback methods with warnings - - **Completed Changes:** - - EventBus is now mandatory in all component constructors - - All components emit events through EventBus.emit() - - Legacy callback dictionaries removed from all components - - TradingSuite provides unified on()/off() methods for event handling - - Clean migration path: use suite.on(EventType.EVENT_NAME, handler) - - **Architecture Benefits:** - - Single unified event system across all components - - Type-safe event handling with EventType enum - - Reduced complexity and cleaner codebase - - Better separation of concerns - - Easier to test and maintain - - Fire-and-forget pattern for better performance - -### Phase 4 (Week 4): Data and Orders βœ… COMPLETED (2025-08-04) -1. **Simplified Data Access** βœ… COMPLETED - - βœ… Added convenience methods to RealtimeDataManager: - - `get_latest_bars()` - Get recent N bars without verbose parameters - - `get_latest_price()` - Clear alias for current price - - `get_ohlc()` - Get OHLC as simple dictionary - - `get_price_range()` - Calculate price statistics easily - - `get_volume_stats()` - Quick volume analysis - - `is_data_ready()` - Check if enough data is loaded - - `get_bars_since()` - Get data since specific time - - `get_data_or_none()` - Get data only if min bars available - - βœ… Removed verbose data access patterns - - βœ… Created examples demonstrating simplified access (examples 11, 12) - -2. **Strategy-Friendly Data Structures** βœ… COMPLETED - - βœ… Enhanced Position model with properties: - - `is_long`, `is_short` - Boolean position type checks - - `direction` - String representation ("LONG"/"SHORT") - - `symbol` - Extract symbol from contract ID - - `signed_size` - Size with sign for calculations - - `total_cost` - Position value calculation - - `unrealized_pnl()` - P&L calculation method - - βœ… Enhanced Order model with properties: - - `is_open`, `is_filled`, `is_cancelled`, etc. - Status checks - - `is_buy`, `is_sell` - Side checks - - `side_str`, `type_str`, `status_str` - String representations - - `filled_percent` - Fill percentage calculation - - `remaining_size` - Unfilled size - - `symbol` - Extract symbol from contract ID - - βœ… Created comprehensive examples (examples 13, 14) - - **Results:** - - 80% reduction in data access code complexity - - 67% reduction in position checking code - - 63% reduction in order filtering code - - Cleaner, more intuitive strategy code - -### Phase 5 (Week 5): Advanced Features βœ… COMPLETED (2025-08-04) -1. **Order Lifecycle Management** βœ… COMPLETED - - βœ… Implemented OrderTracker with context manager for automatic cleanup - - βœ… Added async waiting mechanisms (wait_for_fill, wait_for_status) - - βœ… Created OrderChainBuilder for fluent API order construction - - βœ… Added common order templates (RiskReward, ATR, Breakout, Scalping) - - βœ… Integrated into TradingSuite with track_order() and order_chain() methods - - βœ… Removed need for manual order tracking in strategies - - βœ… Created comprehensive example demonstrating all features - -### Phase 6 (Week 6): Risk and Recovery βœ… COMPLETED (2025-08-04) -1. **Built-in Risk Management** βœ… COMPLETED - - βœ… Created comprehensive RiskManager with position sizing algorithms - - βœ… Implemented trade validation against configurable risk rules - - βœ… Added automatic stop-loss and take-profit attachment - - βœ… Created ManagedTrade context manager for simplified trading - - βœ… Integrated RiskManager into TradingSuite with feature flag - - βœ… Added Kelly Criterion position sizing support - - βœ… Implemented daily loss and trade limits - - βœ… Created trailing stop monitoring -2. **Better Error Recovery** (Future Enhancement) - - ConnectionManager for automatic reconnection - - StateManager for persistence and recovery - - Operation queuing during disconnections - -## Type System Implementation Roadmap - -### Components Requiring Type Implementation - -#### OrderManager Package (`order_manager/`) -- **Methods to Update**: - - `get_order_statistics()` β†’ return `OrderManagerStats` - - `get_performance_metrics()` β†’ return `OrderStatsResponse` - - `validate_order_config()` β†’ accept `OrderManagerConfig` - - All bracket/OCO order methods β†’ return structured responses -- **Configuration Integration**: - - Accept `OrderManagerConfig` in initialization - - Use typed configuration for validation settings - - Replace internal dict configs with proper types - -#### PositionManager Package (`position_manager/`) -- **Methods to Update**: - - `get_portfolio_statistics()` β†’ return `PortfolioMetricsResponse` - - `get_position_analytics()` β†’ return `PositionAnalysisResponse` - - `calculate_risk_metrics()` β†’ return `RiskAnalysisResponse` - - `get_performance_stats()` β†’ return `PositionManagerStats` -- **Configuration Integration**: - - Accept `PositionManagerConfig` in initialization - - Use typed risk configuration settings - - Replace internal dict configs with proper types - -#### RealtimeDataManager Package (`realtime_data_manager/`) -- **Methods to Update**: - - `get_memory_stats()` β†’ return `RealtimeDataManagerStats` - - `get_connection_status()` β†’ return `RealtimeConnectionStats` - - `get_data_quality_metrics()` β†’ return structured response -- **Configuration Integration**: - - Accept `DataManagerConfig` in initialization - - Use typed memory and buffer configurations - -#### OrderBook Package (`orderbook/`) -- **Methods to Update**: - - `get_memory_stats()` β†’ return `OrderbookStats` - - `analyze_market_microstructure()` β†’ return `OrderbookAnalysisResponse` - - `analyze_liquidity()` β†’ return `LiquidityAnalysisResponse` - - `estimate_market_impact()` β†’ return `MarketImpactResponse` - - `detect_icebergs()` β†’ return `list[IcebergDetectionResponse]` - - `detect_spoofing()` β†’ return `list[SpoofingDetectionResponse]` - - `get_volume_profile()` β†’ return `VolumeProfileListResponse` -- **Configuration Integration**: - - Accept `OrderbookConfig` in initialization - -#### HTTP Client (`client/http.py`) -- **Methods to Update**: - - `get_health_status()` β†’ return `HealthStatusResponse` - - `get_performance_stats()` β†’ return `PerformanceStatsResponse` - - All API response methods β†’ return proper response types -- **Configuration Integration**: - - Accept `HTTPConfig` in initialization - - Use typed timeout and retry configurations - -#### Realtime Client (`realtime/`) -- **Methods to Update**: - - `get_connection_stats()` β†’ return `RealtimeConnectionStats` - - `get_stats()` β†’ return structured connection metrics -- **Configuration Integration**: - - Accept `RealtimeConfig` in initialization - - Use typed WebSocket configurations - -### Implementation Strategy - -#### Phase 2.1: Core Component Stats βœ… COMPLETED (2025-08-04) -1. βœ… Update OrderManager to return OrderManagerStats -2. βœ… Update PositionManager to return PositionManagerStats -3. βœ… Update RealtimeDataManager to return RealtimeDataManagerStats -4. βœ… Update OrderBook to return OrderbookStats -5. βœ… Test all statistics methods for correctness - -#### Phase 2.2: Response Type Implementation βœ… COMPLETED (2025-08-04) -1. βœ… Update OrderBook analysis methods with proper response types - - get_advanced_market_metrics() β†’ OrderbookAnalysisResponse - - get_market_imbalance() β†’ LiquidityAnalysisResponse - - get_orderbook_depth() β†’ MarketImpactResponse - - get_orderbook_snapshot() β†’ OrderbookSnapshot - - get_spread_analysis() β†’ LiquidityAnalysisResponse -2. βœ… Update HTTP client with HealthStatusResponse/PerformanceStatsResponse - - get_health_status() β†’ PerformanceStatsResponse -3. βœ… Update position analysis methods with structured responses - - calculate_position_pnl() β†’ PositionAnalysisResponse - - calculate_portfolio_pnl() β†’ PortfolioMetricsResponse - - get_portfolio_pnl() β†’ PortfolioMetricsResponse -4. βœ… Update risk calculation methods with RiskAnalysisResponse - - get_risk_metrics() β†’ RiskAnalysisResponse - - calculate_position_size() β†’ PositionSizingResponse -5. βœ… Test all response type implementations -6. βœ… **BONUS**: Cleaned up all legacy compatibility code from analytics methods - -#### Phase 2.3: Configuration Type Integration βœ… COMPLETED (2025-08-04) -1. βœ… Update all component initialization to accept typed configs - - OrderManager accepts OrderManagerConfig parameter - - PositionManager accepts PositionManagerConfig parameter - - RealtimeDataManager accepts DataManagerConfig parameter - - OrderBook accepts OrderbookConfig parameter -2. βœ… Replace internal dict configurations with proper types - - Added _apply_config_defaults() methods to all components - - Configuration values now use proper TypedDict types -3. βœ… Add configuration validation using type hints - - All config parameters are properly typed and validated -4. βœ… Update TradingSuite to pass typed configs to components - - Added factory methods to TradingSuiteConfig for component configs - - TradingSuite passes typed configs to all components during initialization -5. βœ… Test configuration type integration - - All configuration factory methods tested and working - - Type safety verified with mypy - -#### Phase 2.4: Testing and Validation βœ… COMPLETED (2025-08-04) -1. βœ… Comprehensive testing of all new type implementations - - Fixed ComponentStats type mismatch in TradingSuite.get_stats() - - Resolved import conflicts between TradingSuiteConfig classes -2. βœ… Verify IDE autocomplete works for all new types - - All TypedDict types provide full autocomplete support -3. βœ… Check for any remaining dict[str, Any] usage - - Zero dict[str, Any] remaining in public APIs -4. βœ… Performance testing to ensure no regressions - - Configuration integration adds minimal overhead - - All components work correctly with typed configs -5. βœ… Update documentation and examples - - Type definitions include comprehensive documentation - -### Aggressive Timeline Benefits -- 6 weeks instead of 13 weeks (adjusted for type implementation) -- Breaking changes made immediately -- No time wasted on compatibility -- Clean code from day one -- Complete type safety throughout entire SDK - -## Code Removal Plan - -### Phase 1 Removals -- Delete all factory functions from `__init__.py` after TradingSuite implementation - - `create_trading_suite()` - 340 lines (OBSOLETE) - - `create_initialized_trading_suite()` - wrapper function (OBSOLETE) - - `create_order_manager()` - manual instantiation (OBSOLETE) - - `create_position_manager()` - manual wiring (OBSOLETE) - - `create_realtime_client()` - internal to TradingSuite now (OBSOLETE) - - `create_data_manager()` - automatic in TradingSuite (OBSOLETE) -- Remove all `dict[str, Any]` type hints -- Delete magic numbers throughout codebase -- See FACTORY_REMOVAL_PLAN.md for detailed removal strategy - -### Phase 2 Removals -- Remove individual callback systems from each component -- Delete redundant event handling code -- Remove callback registration from mixins - -### Phase 3 Removals -- Delete verbose data access patterns -- Remove redundant calculation utilities -- Delete manual metric calculations - -### Phase 4 Removals -- Remove manual order tracking logic -- Delete order state management code -- Remove complex order monitoring patterns - -### Phase 5 Removals -- Delete manual position sizing calculations -- Remove scattered risk management code -- Delete manual reconnection handling - -## Testing Strategy - -### Unit Tests -- Test each new component in isolation -- Mock external dependencies -- Aim for >90% coverage of new code - -### Integration Tests -- Test interaction between components -- Use real market data for realistic scenarios -- Test error conditions and recovery - -### Example Updates -- Update all examples to use new features -- Create migration guide for existing users -- Add performance comparison examples - -## Documentation Requirements - -### API Documentation -- Complete docstrings for all new methods -- Type hints for all parameters and returns -- Usage examples in docstrings - -### User Guide -- Getting started with new features -- Migration guide from current API -- Best practices guide - -### Tutorial Series -1. Building Your First Strategy -2. Risk Management Essentials -3. Advanced Order Management -4. Real-time Data Processing -5. Error Handling and Recovery - -## Development Phase Approach - -### Clean Code Priority -- **Maintain compatibility layers** - keep old APIs working with deprecation warnings -- **Add deprecation warnings** - provide clear migration paths -- **Direct refactoring** - update all code to use new patterns -- **Remove unused code** - delete anything not actively used - -### Benefits of This Approach -- Cleaner, more maintainable codebase -- Faster development without compatibility constraints -- Easier to understand without legacy code -- Smaller package size and better performance - -### Code Cleanup Strategy -```python -# When implementing new features: -1. Implement new clean API -2. Update all examples and tests immediately -3. Delete old implementation completely -4. No compatibility shims or adapters -``` - -## Success Metrics - -### Developer Experience -- Reduce lines of code for common tasks by 50% -- Improve IDE autocomplete coverage to 95% -- Reduce time to first working strategy to <30 minutes - -### Performance -- No regression in execution speed -- Memory usage optimization for long-running strategies -- Improved startup time with lazy loading - -### Reliability -- 99.9% uptime with automatic recovery -- <1 second recovery from disconnection -- Zero data loss during disconnections - -## Current Status (2025-08-04) - -### Completed βœ… -βœ… **Phase 1: TradingSuite Implementation** -- Single-line initialization: `suite = await TradingSuite.create("MNQ")` -- Automatic authentication and connection management -- Feature flags for optional components -- Context manager support for automatic cleanup -- Full type safety with mypy compliance -- Tested with real API connections - -βœ… **Phase 2: Complete Type System Implementation** -- **Phase 2.1**: Core Component Stats - All managers return structured stats -- **Phase 2.2**: Response Type Implementation - All analysis methods use TypedDict responses -- **Phase 2.3**: Configuration Type Integration - All components accept typed configs -- **Phase 2.4**: Testing and Validation - All type implementations tested and verified -- **Bonus**: Complete removal of legacy compatibility code -- **Result**: 100% structured types and type-safe configuration throughout the SDK - -βœ… **Phase 3: Event-Driven Architecture** -- **EventBus Mandatory**: Central event system fully integrated in all components -- **EventType Enum**: Comprehensive event types for type-safe event handling -- **Full Integration**: All components require EventBus and emit events through it -- **Legacy Removed**: Old callback systems completely removed -- **Clean API**: TradingSuite provides unified on()/off() methods -- **Result**: Simplified architecture with single event handling system - -βœ… **Phase 4: Data and Orders** -- **Simplified Data Access**: Added 8+ convenience methods to RealtimeDataManager -- **Enhanced Models**: Position and Order models now have intuitive properties -- **Code Reduction**: 60-80% reduction in common data access patterns -- **Strategy-Friendly**: Properties like `is_long`, `direction`, `symbol` make code cleaner -- **Result**: Much more intuitive and less error-prone strategy development - -βœ… **Phase 5: Order Lifecycle Management** -- **OrderTracker**: Context manager for comprehensive order lifecycle tracking -- **Async Waiting**: wait_for_fill() and wait_for_status() eliminate polling -- **OrderChainBuilder**: Fluent API for complex order structures -- **Order Templates**: Pre-configured templates for common trading patterns -- **Result**: 90% reduction in order management complexity - -βœ… **Phase 6: Risk Management** -- **RiskManager**: Comprehensive risk management system with position sizing -- **ManagedTrade**: Context manager for risk-controlled trade execution -- **Risk Validation**: Automatic validation against configurable risk rules -- **Position Sizing**: Fixed risk and Kelly Criterion algorithms -- **Auto Risk Orders**: Automatic stop-loss and take-profit attachment -- **Result**: Professional-grade risk management built into the SDK - -### Future Enhancements -- **Error Recovery**: ConnectionManager for automatic reconnection -- **State Persistence**: StateManager for saving/restoring trading state -- **Trade Journal**: Automatic trade logging and analysis -- **Performance Analytics**: Real-time strategy performance metrics - -### Achievements So Far -- **80% reduction** in initialization code (from ~50 lines to 1 line) -- **100% type safety** throughout entire SDK with 50+ TypedDict definitions -- **Zero dict[str, Any]** remaining in any public APIs -- **Complete structured responses** for all analysis and statistics methods -- **Type-safe configuration system** - all components accept properly typed configs -- **Configuration factory pattern** - TradingSuiteConfig provides typed configs for all components -- **No legacy compatibility code** - pure v3.0.0 implementation -- **Automatic resource management** with context managers -- **Simplified API** that's intuitive for new users -- **Complete factory function removal** - eliminated 340+ lines of obsolete code -- **Full IDE support** with comprehensive autocomplete and type checking -- **Modern codebase** with maintained backward compatibility -- **Unified event system** - EventBus mandatory in all components -- **Single event API** - TradingSuite.on() replaces all callback systems -- **Clean architecture** - no dual systems or legacy code -- **Updated examples** - all examples use new EventBus pattern -- **Simplified data access** - 8+ new convenience methods in RealtimeDataManager -- **Enhanced models** - Position and Order models with 15+ new properties -- **60-80% code reduction** in common trading patterns -- **Intuitive property names** - no more magic numbers or verbose checks -- **Strategy-friendly design** - properties like is_long, direction, symbol -- **Comprehensive order lifecycle management** - OrderTracker eliminates manual state tracking -- **Fluent order API** - OrderChainBuilder for complex order structures -- **Pre-configured templates** - 11 order templates for common trading patterns -- **90% reduction** in order management complexity -- **Professional risk management** - RiskManager with position sizing algorithms -- **Risk-controlled trading** - ManagedTrade context manager for automatic risk management -- **Trade validation** - Automatic validation against configurable risk rules -- **Multiple position sizing methods** - Fixed risk, Kelly Criterion, and more -- **Automatic protective orders** - Stop-loss and take-profit attachment -- **17 comprehensive examples** demonstrating all v3.0.0 features - -## Conclusion - -These improvements will transform the ProjectX SDK from a powerful but complex toolkit into a developer-friendly platform that makes strategy implementation intuitive and efficient. The aggressive 5-week timeline with no backward compatibility ensures we deliver a clean, modern SDK ready for production use as v3.0.0. \ No newline at end of file diff --git a/docs/_reference_docs/TEST_REFACTORING_ISSUE.md b/docs/_reference_docs/TEST_REFACTORING_ISSUE.md deleted file mode 100644 index d33e39b..0000000 --- a/docs/_reference_docs/TEST_REFACTORING_ISSUE.md +++ /dev/null @@ -1,111 +0,0 @@ -# Test Suite Refactoring Issue - -## Overview -The current test suite has significant issues that prevent tests from running properly. Out of 27 test files with 226 tests collected, there are 8 import errors preventing test execution. Additionally, there are major gaps in test coverage and outdated test implementations. - -## Critical Issues Found - -### 1. Import Errors (8 files affected) -- Tests are importing non-existent classes/functions: - - `RealtimeClient` should be `ProjectXRealtimeClient` - - `ProjectXConfigError` doesn't exist in exceptions.py - - Multiple tests using outdated async class names - -### 2. Outdated Test References -- 9 test files still reference old async classes: - - `AsyncProjectX` (now `ProjectX`) - - `AsyncOrderManager` (now `OrderManager`) - - `AsyncPositionManager` (now `PositionManager`) - - `create_async_trading_suite` (now `create_trading_suite`) - -### 3. Missing Test Coverage -Critical components with no test coverage: -- **Indicators module** (9 modules, 0 tests) - - momentum indicators - - overlap indicators - - volatility indicators - - volume indicators - - base classes -- **Client module components** (refactored into submodules) -- **Realtime module components** (refactored into submodules) -- **Utils module components** (refactored into submodules) - -### 4. Duplicate and Redundant Tests -- Multiple versions of same tests (async and sync) -- Test files for both old and new implementations -- Comprehensive test files that duplicate basic test files - -## Specific Files Requiring Fixes - -### Files with Import Errors: -1. `test_async_order_manager_comprehensive.py` - RealtimeClient import -2. `test_async_realtime.py` - RealtimeClient import -3. `test_config.py` - ProjectXConfigError import -4. `test_async_integration_comprehensive.py` - RealtimeClient import -5. `test_async_orderbook.py` - RealtimeClient import -6. `test_async_realtime_data_manager.py` - RealtimeClient import -7. `test_integration.py` - RealtimeClient import -8. `test_order_manager_init.py` - RealtimeClient import -9. `test_position_manager_init.py` - RealtimeClient import - -### Files with Outdated References: -All async test files need updating to use new non-async class names. - -## Proposed Action Plan - -### Phase 1: Fix Import Errors -1. Update all `RealtimeClient` imports to `ProjectXRealtimeClient` -2. Remove or fix `ProjectXConfigError` references -3. Update all async class imports to new names - -### Phase 2: Remove Redundant Tests -1. Consolidate duplicate async/sync test files -2. Remove tests for deprecated functionality -3. Merge comprehensive test files with basic ones - -### Phase 3: Add Missing Test Coverage -1. Create test suite for indicators module: - - Test each indicator category - - Test class-based and function interfaces - - Test Polars DataFrame operations -2. Add tests for refactored modules: - - Client submodules - - Realtime submodules - - Utils submodules - -### Phase 4: Modernize Test Structure -1. Use pytest fixtures consistently -2. Add proper mocking for external API calls -3. Implement test markers properly (unit, integration, slow) -4. Add async test support where needed - -### Phase 5: Test Organization -1. Restructure tests to mirror source code structure: - ``` - tests/ - β”œβ”€β”€ unit/ - β”‚ β”œβ”€β”€ client/ - β”‚ β”œβ”€β”€ indicators/ - β”‚ β”œβ”€β”€ order_manager/ - β”‚ β”œβ”€β”€ position_manager/ - β”‚ └── utils/ - β”œβ”€β”€ integration/ - └── conftest.py - ``` - -## Success Criteria -- [ ] All tests can be collected without import errors -- [ ] Test coverage > 80% for all modules -- [ ] No duplicate or redundant tests -- [ ] Clear separation between unit and integration tests -- [ ] All tests pass in CI/CD pipeline -- [ ] Tests follow modern pytest patterns - -## Priority -**High** - The test suite is currently broken and preventing proper validation of code changes. - -## Labels -- bug -- testing -- refactoring -- technical-debt \ No newline at end of file diff --git a/docs/_reference_docs/V3_API_COMPARISON.md b/docs/_reference_docs/V3_API_COMPARISON.md deleted file mode 100644 index a5a0285..0000000 --- a/docs/_reference_docs/V3_API_COMPARISON.md +++ /dev/null @@ -1,214 +0,0 @@ -# V3 API Comparison - -This document shows the dramatic simplification achieved in v3.0.0 of the ProjectX SDK. - -## Basic Connection and Setup - -### V2 (Old Way) -```python -# Multiple steps required -async with ProjectX.from_env() as client: - await client.authenticate() - - # Create realtime client - realtime_client = ProjectXRealtimeClient( - jwt_token=client.session_token, - account_id=str(client.account_info.id), - config=client.config, - ) - - # Create data manager - data_manager = RealtimeDataManager( - instrument="MNQ", - project_x=client, - realtime_client=realtime_client, - timeframes=["1min", "5min"], - ) - - # Create managers - order_manager = OrderManager(client) - position_manager = PositionManager(client) - - # Connect and initialize everything - await realtime_client.connect() - await realtime_client.subscribe_user_updates() - await data_manager.initialize(initial_days=5) - await data_manager.start_realtime_feed() - await position_manager.initialize(realtime_client, order_manager) -``` - -### V3 (New Way) -```python -# One line does it all! -suite = await TradingSuite.create("MNQ") - -# Everything is connected and ready to use -current_price = await suite.data.get_current_price() -positions = await suite.positions.get_all_positions() -``` - -## Configuration Options - -### V2 (Old Way) -```python -# Manual configuration assembly -config = ProjectXConfig( - api_url="https://api.projectx.com", - timeout_seconds=30, - timezone="America/Chicago" -) -client = ProjectX(config) -# ... many more setup steps ... -``` - -### V3 (New Way) -```python -# Configuration built in -suite = await TradingSuite.create( - "MNQ", - timeframes=["1min", "5min", "15min"], - features=["orderbook", "risk_manager"], - initial_days=10 -) -``` - -## Resource Cleanup - -### V2 (Old Way) -```python -# Manual cleanup of each component -await data_manager.stop_realtime_feed() -await data_manager.cleanup() -await realtime_client.disconnect() -await orderbook.cleanup() -# Easy to forget steps! -``` - -### V3 (New Way) -```python -# Automatic cleanup with context manager -async with await TradingSuite.create("MNQ") as suite: - # Use suite... - pass # Automatic cleanup on exit - -# Or manual if needed -await suite.disconnect() # Cleans up everything -``` - -## Feature Enablement - -### V2 (Old Way) -```python -# Conditional creation of components -if enable_orderbook: - orderbook = OrderBook( - instrument="MNQ", - timezone_str=config.timezone, - project_x=client, - ) - await orderbook.initialize( - realtime_client=realtime_client, - subscribe_to_depth=True, - subscribe_to_quotes=True, - ) -``` - -### V3 (New Way) -```python -# Feature flags -suite = await TradingSuite.create( - "MNQ", - features=["orderbook", "risk_manager"] -) - -# Components created and initialized automatically -if suite.orderbook: - stats = suite.orderbook.get_stats() -``` - -## Error Handling - -### V2 (Old Way) -```python -# Multiple try-catch blocks needed -try: - async with ProjectX.from_env() as client: - try: - await client.authenticate() - except AuthenticationError: - # Handle auth error - pass - - try: - realtime_client = ProjectXRealtimeClient(...) - await realtime_client.connect() - except ConnectionError: - # Handle connection error - pass - - # More error handling... -``` - -### V3 (New Way) -```python -# Single error boundary -try: - suite = await TradingSuite.create("MNQ") - # Everything handled internally -except Exception as e: - logger.error(f"Failed to create trading suite: {e}") -``` - -## Configuration File Support - -### V2 (Old Way) -```python -# Manual config file loading -with open("config.json") as f: - config_data = json.load(f) - -config = ProjectXConfig(**config_data) -client = ProjectX(config) -# ... continue setup ... -``` - -### V3 (New Way) -```python -# Built-in config file support -suite = await TradingSuite.from_config("config/trading.yaml") -``` - -## Multi-Instrument Support - -### V2 (Old Way) -```python -# Create everything for each instrument -mnq_data = RealtimeDataManager("MNQ", client, realtime) -mgc_data = RealtimeDataManager("MGC", client, realtime) -es_data = RealtimeDataManager("ES", client, realtime) - -# Initialize each one -await mnq_data.initialize() -await mgc_data.initialize() -await es_data.initialize() -``` - -### V3 (New Way) -```python -# Create multiple suites easily -suites = {} -for symbol in ["MNQ", "MGC", "ES"]: - suites[symbol] = await TradingSuite.create(symbol) -``` - -## Summary - -The v3.0.0 API reduces complexity by **80%** while providing: -- **Single-line initialization** replacing 20+ lines of setup -- **Automatic dependency management** eliminating manual wiring -- **Built-in error handling** reducing boilerplate -- **Feature flags** for optional components -- **Configuration file support** out of the box -- **Proper cleanup** with context managers - -This makes the SDK truly production-ready with a developer-friendly API. \ No newline at end of file diff --git a/docs/_reference_docs/V3_DEVELOPMENT.md b/docs/_reference_docs/V3_DEVELOPMENT.md deleted file mode 100644 index 582bd81..0000000 --- a/docs/_reference_docs/V3_DEVELOPMENT.md +++ /dev/null @@ -1,207 +0,0 @@ -# V3.0.0 Development Process - -## Branch Strategy - -This document outlines the development process for v3.0.0 of the ProjectX Python SDK. - -### Current Setup -- **Branch**: `refactor_v3` -- **Target**: v3.0.0 production-ready release -- **Base**: `main` (v2.x stable) -- **Started**: 2025-08-04 -- **Draft PR**: #30 (Created) - -## Development Workflow - -### 1. All v3 Development in `refactor_v3` Branch -```bash -# Always work in the refactor_v3 branch -git checkout refactor_v3 - -# Regular commits as you implement features -git add . -git commit -m "feat: implement feature X" -git push origin refactor_v3 -``` - -### 2. Keep Branch Updated with Main (Optional) -```bash -# Periodically sync with main if needed -git checkout refactor_v3 -git merge main -# Resolve any conflicts -git push origin refactor_v3 -``` - -### 3. Pull Request Strategy - -#### Option A: Draft PR (Recommended) -Create a single draft PR that remains open during development: -```bash -gh pr create --base main --head refactor_v3 --draft \ - --title "feat: v3.0.0 Major Refactor" \ - --body "See SDK_IMPROVEMENTS_PLAN.md" -``` - -Benefits: -- Track progress in one place -- Run CI/CD on each push -- Easy to review changes -- Convert to ready when done - -#### Option B: Feature PRs to `refactor_v3` -Create separate PRs for each major feature: -```bash -# Create feature branch from refactor_v3 -git checkout refactor_v3 -git checkout -b feat/event-system - -# Work on feature -git add . -git commit -m "feat: implement unified event system" - -# PR to refactor_v3, not main! -gh pr create --base refactor_v3 --head feat/event-system -``` - -Benefits: -- Smaller, focused reviews -- Better commit history -- Easier to revert features - -### 4. Final Merge to Main -When v3.0.0 is complete: -```bash -# Update version to remove -dev -# Update CHANGELOG.md -# Final testing - -# Convert draft to ready or create final PR -gh pr ready - -# Squash merge to main -# Tag as v3.0.0 -``` - -## Implementation Phases - -Following SDK_IMPROVEMENTS_PLAN.md: - -### Week 1: Foundation (Aug 4-10, 2025) -- [x] Simplified Initialization (TradingSuite class) βœ… COMPLETED Day 1 - - Created TradingSuite with single-line initialization - - Implemented factory methods (create, from_config, from_env) - - Added feature flags system - - Full async context manager support - - Tested with real API connections -- [ ] Better Type Hints (Enums, TypedDict) - IN PROGRESS - - Need to replace all dict[str, Any] occurrences - - Create comprehensive types module - - Convert magic numbers to enums - -### Week 2: Core Enhancements -- [ ] Event-Driven Architecture (EventBus) - -### Week 3: Data and Orders -- [ ] Simplified Data Access -- [ ] Strategy-Friendly Data Structures - -### Week 4: Advanced Features -- [ ] Order Lifecycle Management - -### Week 5: Risk and Recovery -- [ ] Built-in Risk Management -- [ ] Better Error Recovery - -## Testing Strategy - -### Continuous Testing in Branch -- All tests must pass in `refactor_v3` -- Update tests as you refactor -- Add new tests for new features - -### Integration Testing -```bash -# Run full test suite -uv run pytest - -# Run with coverage -uv run pytest --cov=project_x_py --cov-report=html -``` - -## Documentation Updates - -### During Development -- Update docstrings immediately -- Keep examples working -- Document breaking changes - -### Before Merge -- Complete API documentation -- Migration guide from v2 to v3 -- Update all examples - -## Version Management - -### During Development -- Version: `3.0.0-dev` -- Update both `pyproject.toml` and `__init__.py` - -### Pre-release Testing -- Version: `3.0.0-rc1`, `3.0.0-rc2`, etc. -- Tag pre-releases for testing - -### Final Release -- Version: `3.0.0` -- Tag and create GitHub release - -## Breaking Changes Documentation - -Track all breaking changes in `BREAKING_CHANGES_V3.md`: -- Old API vs New API -- Migration examples -- Removal list - -## Communication - -### Progress Updates -- Update PR description weekly -- Use PR comments for decisions -- Link related issues - -### Team Sync -- Regular reviews of refactor_v3 -- Discuss major decisions before implementation -- Test early and often - -## Current Status (2025-08-04) - -### Completed Today -βœ… **TradingSuite Implementation** -- Created `src/project_x_py/trading_suite.py` with unified initialization -- Reduced setup from ~50 lines to 1 line: `suite = await TradingSuite.create("MNQ")` -- Full type safety verified with mypy -- Tested with `./test.sh` - all functionality working -- Created comparison documentation in `V3_API_COMPARISON.md` -- Updated examples: `00_trading_suite_demo.py` and `01_basic_client_connection_v3.py` - -### Next Tasks -1. Update remaining 15+ examples to use TradingSuite -2. Delete old factory functions from `__init__.py` -3. Begin type hints improvements -4. Update documentation - -### Key Achievements -- **80% code reduction** for initialization -- **100% type safety** in new implementation -- **Automatic resource management** with context managers -- **Feature flags** for optional components (orderbook, risk_manager, etc.) - -### Files Modified -- `pyproject.toml` - Version bumped to 3.0.0-dev -- `src/project_x_py/__init__.py` - Added TradingSuite exports -- `src/project_x_py/trading_suite.py` - NEW: Main implementation -- `examples/00_trading_suite_demo.py` - NEW: Demo script -- `examples/01_basic_client_connection_v3.py` - NEW: V3 version of basic example -- `V3_API_COMPARISON.md` - NEW: Before/after comparison -- `tests/test_trading_suite.py` - NEW: Unit tests \ No newline at end of file diff --git a/docs/_reference_docs/async_refactoring_issue.md b/docs/_reference_docs/async_refactoring_issue.md deleted file mode 100644 index 08e71c7..0000000 --- a/docs/_reference_docs/async_refactoring_issue.md +++ /dev/null @@ -1,330 +0,0 @@ -# Async/Await Refactoring Plan for project-x-py - -## Summary - -This issue outlines a comprehensive plan to refactor the project-x-py SDK from synchronous to asynchronous operations, enabling better performance, resource utilization, and natural integration with real-time trading workflows. - -## Motivation - -The current synchronous architecture has several limitations: - -1. **Blocking I/O**: HTTP requests block the thread, preventing concurrent operations -2. **Inefficient for Real-time**: SignalR/WebSocket connections naturally fit async patterns -3. **Resource Utilization**: Can't efficiently handle multiple market data streams or order operations concurrently -4. **Modern Python Standards**: Async is the standard for I/O-heavy Python applications, especially in financial/trading contexts - -## Benefits of Async Migration - -- **Concurrent Operations**: Execute multiple API calls simultaneously (e.g., fetch positions while placing orders) -- **Non-blocking Real-time**: Process WebSocket events without blocking other operations -- **Better Resource Usage**: Single thread can handle many concurrent connections -- **Improved Responsiveness**: UI/strategy code won't freeze during API calls -- **Natural Event Handling**: Async/await patterns match event-driven trading systems - -## Technical Analysis - -### Current Architecture - -1. **HTTP Client**: Uses `requests` library with session pooling -2. **WebSocket**: Uses `signalrcore` for SignalR connections -3. **Blocking Pattern**: All API calls block until completion -4. **Managers**: OrderManager, PositionManager, etc. all use synchronous methods - -### Proposed Async Architecture - -1. **HTTP Client**: Migrate to `httpx` (supports both sync/async) -2. **WebSocket**: Use `python-signalrcore-async` or create async wrapper for SignalR -3. **Async Pattern**: All public APIs become `async def` methods -4. **Managers**: Convert to async with proper concurrency handling - -## Implementation Plan - -### Progress Summary - -**Phase 1 (Foundation) - COMPLETED on 2025-07-30** -- Created `AsyncProjectX` client with full async/await support -- Implemented HTTP/2 enabled httpx client with connection pooling -- Added comprehensive error handling with exponential backoff retry logic -- Created basic async methods: authenticate, get_positions, get_instrument, get_health_status -- Full test suite for async client with 9 passing tests - -**Phase 2 (Core Client Migration) - COMPLETED on 2025-07-30** -- Implemented async rate limiter with sliding window algorithm -- Added account management: list_accounts, search_open_positions -- Implemented market data retrieval: get_bars with timezone conversion -- Added instrument search: search_instruments with live filter -- Implemented trade history: search_trades with date range filtering -- Enhanced caching for market data (5-minute TTL) -- Comprehensive test suite expanded to 14 passing tests - -**Phase 3 (Manager Migration) - COMPLETED on 2025-07-31** -- Converted all managers to async: OrderManager, PositionManager, RealtimeDataManager, OrderBook -- Implemented proper async locking and thread safety -- Created comprehensive test suites for all managers (62 tests total) -- Ensured all managers can share ProjectXRealtimeClient instance - -**Phase 4 (SignalR/WebSocket Integration) - COMPLETED on 2025-07-31** -- Created ProjectXRealtimeClient with async wrapper around SignalR -- Implemented async event handling and callback system -- Added JWT token refresh and reconnection support -- Created async factory functions for all components -- Full integration with dependency injection patterns - -### Phase 1: Foundation (Week 1-2) βœ… COMPLETED - -- [x] Add async dependencies to `pyproject.toml`: - - `httpx[http2]` for async HTTP with HTTP/2 support - - `python-signalrcore-async` or evaluate alternatives - - Update `pytest-asyncio` for testing -- [x] Create async base client class (`AsyncProjectX`) -- [x] Implement async session management and connection pooling -- [x] Design async error handling and retry logic - -### Phase 2: Core Client Migration (Week 2-3) βœ… COMPLETED - -- [x] Convert authentication methods to async -- [x] Migrate account management endpoints -- [x] Convert market data methods (get_bars, get_instrument) -- [x] Implement async caching mechanisms -- [x] Add async rate limiting - -### Phase 3: Manager Migration (Week 3-4) βœ… COMPLETED - -- [x] Convert OrderManager to async βœ… COMPLETED on 2025-07-30 -- [x] Convert PositionManager to async βœ… COMPLETED on 2025-07-30 -- [x] Convert RealtimeDataManager to async βœ… COMPLETED on 2025-07-31 -- [x] Update OrderBook for async operations βœ… COMPLETED on 2025-07-31 -- [x] Ensure managers can share async ProjectXRealtimeClient βœ… COMPLETED on 2025-07-31 - -**OrderManager Async Conversion Summary:** -- Created AsyncOrderManager with full async/await support -- Implemented all order operations: market, limit, stop, bracket orders -- Added async-safe locking for thread safety -- Converted order search, modification, and cancellation to async -- Full test suite with 12 passing tests covering all functionality -- Fixed deadlock issues in bracket orders by removing nested locks -- Properly handles dataclass conversions and model structures - -**PositionManager Async Conversion Summary:** -- Created AsyncPositionManager with complete async/await support -- Implemented all position tracking and management operations -- Added async portfolio P&L calculation and risk metrics -- Converted position closure operations (direct, partial, bulk) to async -- Implemented async position monitoring with alerts -- Full test suite with 17 passing tests covering all functionality -- Proper validation for ProjectX position payload formats -- Async-safe operations with asyncio locks - -**RealtimeDataManager Async Conversion Summary:** -- Created AsyncRealtimeDataManager with full async/await support -- Implemented multi-timeframe OHLCV data management -- Converted tick processing and data aggregation to async -- Added async memory cleanup and optimization -- Full test suite with 16 passing tests -- Proper timezone handling with Polars DataFrames -- Supports both sync and async callbacks for flexibility - -**OrderBook Async Conversion Summary:** -- Created AsyncOrderBook with complete async functionality -- Implemented Level 2 market depth processing -- Converted iceberg detection and volume analysis to async -- Added async liquidity distribution analysis -- Full test suite with 17 passing tests -- Fixed timezone-aware datetime issues with Polars -- Proper memory management with sliding windows - -### Phase 4: SignalR/WebSocket Integration (Week 4-5) βœ… COMPLETED - -- [x] Research SignalR async options: βœ… COMPLETED on 2025-07-31 - - Option A: `python-signalrcore-async` (if mature enough) - Not available - - Option B: Create async wrapper around current `signalrcore` βœ… CHOSEN - - Option C: Use `aiohttp` with custom SignalR protocol implementation - Too complex -- [x] Implement async event handling βœ… COMPLETED on 2025-07-31 -- [x] Convert callback system to async-friendly pattern βœ… COMPLETED on 2025-07-31 -- [x] Test reconnection logic with async βœ… COMPLETED on 2025-07-31 - -**AsyncProjectXRealtimeClient Implementation Summary:** -- Created full async wrapper around synchronous SignalR client -- Implemented async connection management with asyncio locks -- Added support for both sync and async callbacks -- Created non-blocking event forwarding with asyncio.create_task() -- Full test suite with 20 passing tests -- Proper JWT token refresh and reconnection support -- Thread-safe operations using asyncio.Lock -- Runs synchronous SignalR operations in executor for compatibility - -**Async Factory Functions Created (now canonical, async-ready by default):** -- `create_client()` - Create ProjectX client -- `create_realtime_client()` - Create real-time WebSocket client -- `create_order_manager()` - Create order manager -- `create_position_manager()` - Create position manager -- `create_data_manager()` - Create OHLCV data manager -- `create_orderbook()` - Create market depth orderbook -- `create_trading_suite()` - Create complete async trading toolkit - -**Integration Features:** -- All async managers share single AsyncProjectXRealtimeClient instance -- Proper dependency injection throughout -- No duplicate WebSocket connections -- Efficient event routing to multiple managers -- Coordinated cleanup across all components - -### Phase 5: Testing & Documentation (Week 5-6) - -- [ ] Convert all tests to async using `pytest-asyncio` -- [ ] Add integration tests for concurrent operations -- [ ] Update all examples to use async/await -- [ ] Document migration guide for users -- [ ] Performance benchmarks (sync vs async) - -## API Design Decisions - -### Option 1: Pure Async (Recommended per CLAUDE.md) -```python -# All methods become async -client = AsyncProjectX(api_key, username) -await client.authenticate() -positions = await client.get_positions() -``` - -### Option 2: Dual API (Not recommended due to complexity) -```python -# Both sync and async clients -sync_client = ProjectX(api_key, username) -async_client = AsyncProjectX(api_key, username) -``` - -Given the CLAUDE.md directive for "No Backward Compatibility" and "Clean Code Priority", **Option 1 (Pure Async) is recommended**. - -## Breaking Changes - -This refactoring will introduce breaking changes: - -1. All public methods become `async` -2. Clients must use `async with` for proper cleanup -3. Event handlers must be async functions -4. Example code and integrations need updates - -## Migration Guide - -```python -# Old (Sync) -client = ProjectX(api_key, username) -client.authenticate() -positions = client.get_positions() - -# New (Async-ready, canonical names) -async with ProjectX(api_key, username) as client: - await client.authenticate() - positions = await client.get_positions() -``` - -## Technical Considerations - -### SignalR Compatibility - -ProjectX requires SignalR for real-time connections. Options: - -1. **python-signalrcore-async**: Check maturity and compatibility -2. **Async Wrapper**: Create async wrapper around sync signalrcore -3. **Custom Implementation**: Use aiohttp with SignalR protocol (complex but most control) - -### Connection Management - -- Use async context managers for resource cleanup -- Implement proper connection pooling for HTTP/2 -- Handle WebSocket reconnection in async context - -### Performance Targets - -- Concurrent API calls should show 3-5x throughput improvement -- WebSocket event processing latency < 1ms -- Memory usage should remain comparable to sync version - -## Dependencies to Add - -```toml -[project.dependencies] -httpx = ">=0.27.0" -# SignalR async solution (TBD based on research) - -[project.optional-dependencies.dev] -pytest-asyncio = ">=0.23.0" -aioresponses = ">=0.7.6" # For mocking async HTTP -``` - -## Example: Async Trading Bot - -```python -import asyncio -from project_x_py import ProjectX, create_trading_suite - -async def trading_bot(): - async with ProjectX(api_key, username) as client: - await client.authenticate() - - # Create trading suite (now async-ready by default) - suite = await create_trading_suite( - instrument="MGC", - project_x=client, - jwt_token=client.session_token, - account_id=client.account_info.id - ) - - # Concurrent operations - positions_task = asyncio.create_task( - suite["position_manager"].get_positions() - ) - market_data_task = asyncio.create_task( - suite["data_manager"].get_bars("1m", 100) - ) - - positions, market_data = await asyncio.gather( - positions_task, market_data_task - ) - - # Real-time event handling - async for tick in suite["data_manager"].stream_ticks(): - await process_tick(tick) - -async def process_tick(tick): - # Async tick processing - pass - -if __name__ == "__main__": - asyncio.run(trading_bot()) -``` - -## Timeline - -- **Total Duration**: 5-6 weeks -- **Testing Phase**: Additional 1 week -- **Documentation**: Ongoing throughout - -## Success Criteria - -1. All tests pass with async implementation -2. Performance benchmarks show improvement -3. Real-time SignalR connections work reliably -4. Clean async API without sync remnants -5. Comprehensive documentation and examples - -## Open Questions - -1. Which SignalR async library to use? -2. Should we provide any sync compatibility layer? -3. How to handle existing users during transition? -4. Performance benchmarking methodology? - -## References - -- [ProjectX SignalR Documentation](https://gateway.docs.projectx.com/docs/realtime/) -- [httpx Documentation](https://www.python-httpx.org/) -- [Python Async Best Practices](https://docs.python.org/3/library/asyncio-task.html) -- [SignalR Protocol Specification](https://github.com/dotnet/aspnetcore/tree/main/src/SignalR/docs/specs) - ---- - -**Note:** All public classes and factory functions are now async-ready by default. The Async* prefix is no longer usedβ€”simply use the canonical names shown in the latest examples above. - -**Note**: This refactoring aligns with the CLAUDE.md directive for "No Backward Compatibility" and "Clean Code Priority" during active development. \ No newline at end of file diff --git a/docs/_reference_docs/github_issue_12_metadata.md b/docs/_reference_docs/github_issue_12_metadata.md deleted file mode 100644 index 946a34c..0000000 --- a/docs/_reference_docs/github_issue_12_metadata.md +++ /dev/null @@ -1,20 +0,0 @@ -# GitHub Issue #12 Metadata - -**Title**: Async/Await Refactoring Plan for project-x-py -**Number**: 12 -**State**: Open -**URL**: https://github.com/TexasCoding/project-x-py/issues/12 -**Created**: 2025-07-30T22:46:46Z -**Updated**: 2025-07-30T22:48:42Z -**Author**: TexasCoding -**Assignee**: TexasCoding - -## Description -This issue contains a comprehensive plan to refactor the project-x-py SDK from synchronous to asynchronous operations. The main content has been saved to `async_refactoring_issue.md`. - -## Key Points -- Complete migration from sync to async architecture -- No backward compatibility (per CLAUDE.md directives) -- 5-6 week implementation timeline -- Uses httpx for async HTTP and needs async SignalR solution -- All public APIs will become async methods \ No newline at end of file diff --git a/docs/_reference_docs/measure_import_performance.py b/docs/_reference_docs/measure_import_performance.py deleted file mode 100755 index c1aa0bb..0000000 --- a/docs/_reference_docs/measure_import_performance.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python3 -""" -Measure import performance for the ProjectX SDK. - -This script helps track import time improvements from lazy loading optimizations. - -Author: TexasCoding -Date: January 2025 -""" - -import importlib -import subprocess -import sys -import time -from pathlib import Path - - -def measure_import_time(module_name: str, fresh: bool = True) -> float: - """ - Measure the time to import a module. - - Args: - module_name: Name of module to import - fresh: Whether to clear import cache first - - Returns: - Import time in seconds - """ - if fresh: - # Clear module from cache - if module_name in sys.modules: - del sys.modules[module_name] - - # Also clear any submodules - modules_to_clear = [ - mod for mod in sys.modules.keys() if mod.startswith(f"{module_name}.") - ] - for mod in modules_to_clear: - del sys.modules[mod] - - start_time = time.perf_counter() - importlib.import_module(module_name) - end_time = time.perf_counter() - - return end_time - start_time - - -def measure_subprocess_import(module_name: str) -> float: - """ - Measure import time in a fresh subprocess. - - This gives the most accurate measurement as it includes all dependencies. - """ - code = f""" -import time -start = time.perf_counter() -import {module_name} -end = time.perf_counter() -print(end - start) -""" - - result = subprocess.run( - [sys.executable, "-c", code], - capture_output=True, - text=True, - env={**subprocess.os.environ, "PYTHONPATH": str(Path.cwd() / "src")}, - ) - - if result.returncode != 0: - print(f"Error importing {module_name}:") - print(result.stderr) - return -1.0 - - return float(result.stdout.strip()) - - -def main() -> None: - """Run import performance measurements.""" - print("ProjectX SDK Import Performance Measurement") - print("=" * 50) - print() - - # Modules to test - test_modules = [ - # Core modules - ("project_x_py", "Full SDK"), - ("project_x_py.client", "Client module"), - ("project_x_py.exceptions", "Exceptions"), - ("project_x_py.models", "Data models"), - ("project_x_py.config", "Configuration"), - # Heavy modules - ("project_x_py.indicators", "All indicators"), - ("project_x_py.indicators.momentum", "Momentum indicators"), - ("project_x_py.orderbook", "Orderbook module"), - ("project_x_py.utils", "Utilities"), - # Managers - ("project_x_py.order_manager", "Order manager"), - ("project_x_py.position_manager", "Position manager"), - ("project_x_py.realtime_data_manager", "Realtime data manager"), - ] - - print("Testing import times (fresh subprocess for each)...") - print() - print(f"{'Module':<40} {'Time (ms)':<12} {'Description':<30}") - print("-" * 82) - - total_time = 0.0 - failed_modules = [] - - for module_name, description in test_modules: - import_time = measure_subprocess_import(module_name) - - if import_time < 0: - failed_modules.append(module_name) - print(f"{module_name:<40} {'FAILED':<12} {description:<30}") - else: - time_ms = import_time * 1000 - total_time += import_time - print(f"{module_name:<40} {time_ms:<12.1f} {description:<30}") - - print("-" * 82) - print(f"{'TOTAL':<40} {total_time * 1000:<12.1f} {'All modules':<30}") - print() - - if failed_modules: - print(f"Failed to import: {', '.join(failed_modules)}") - print() - - print("\nPerformance Tips:") - print("- Import only what you need (e.g., 'from project_x_py import ProjectX')") - print("- The SDK uses TYPE_CHECKING to minimize import overhead") - print("- Import times are dominated by dependencies (polars, httpx) not SDK code") - - -if __name__ == "__main__": - main() diff --git a/docs/advanced/architecture.rst b/docs/advanced/architecture.rst new file mode 100644 index 0000000..c6be250 --- /dev/null +++ b/docs/advanced/architecture.rst @@ -0,0 +1,23 @@ +Architecture Overview +===================== + +The project-x-py SDK uses a modern async architecture optimized for high-performance trading. + +Core Components +--------------- + +- **TradingSuite**: Unified interface for all trading operations +- **ProjectX Client**: Async HTTP client with connection pooling +- **RealtimeClient**: WebSocket connection manager +- **EventBus**: Centralized event handling system +- **Specialized Managers**: Order, Position, Risk, and Data managers + +Design Patterns +--------------- + +- **Dependency Injection**: Managers receive dependencies rather than creating them +- **Factory Functions**: Async factory methods for proper initialization +- **Protocol-based Design**: Type-safe interfaces using Python protocols +- **Context Managers**: Automatic resource cleanup + +See the source code for implementation details. \ No newline at end of file diff --git a/docs/advanced/contributing.rst b/docs/advanced/contributing.rst new file mode 100644 index 0000000..3775e0c --- /dev/null +++ b/docs/advanced/contributing.rst @@ -0,0 +1,23 @@ +Contributing Guide +================== + +We welcome contributions to project-x-py! + +Development Setup +----------------- + +1. Fork the repository +2. Clone your fork +3. Install with development dependencies:: + + uv sync + +4. Run tests:: + + uv run pytest + +5. Format code:: + + uv run ruff format . + +See CONTRIBUTING.md in the repository for full guidelines. \ No newline at end of file diff --git a/docs/advanced/debugging.rst b/docs/advanced/debugging.rst new file mode 100644 index 0000000..eaf835d --- /dev/null +++ b/docs/advanced/debugging.rst @@ -0,0 +1,22 @@ +Debugging Guide +=============== + +Tools and techniques for debugging trading applications. + +Logging +------- + +Enable debug logging:: + + from project_x_py import setup_logging + setup_logging(level='DEBUG') + +Performance Monitoring +---------------------- + +Check performance stats:: + + stats = await client.get_performance_stats() + memory = suite.data.get_memory_stats() + +See the source code for additional debugging utilities. \ No newline at end of file diff --git a/docs/advanced/performance.rst b/docs/advanced/performance.rst new file mode 100644 index 0000000..55798a2 --- /dev/null +++ b/docs/advanced/performance.rst @@ -0,0 +1,23 @@ +Performance Optimization +======================== + +The SDK includes numerous performance optimizations for production trading. + +Key Optimizations +------------------ + +- **Connection Pooling**: 50-70% reduction in connection overhead +- **Intelligent Caching**: 80% reduction in repeated API calls +- **WebSocket Batching**: Reduced message processing overhead +- **Memory Management**: Automatic sliding windows and cleanup +- **Async Architecture**: Non-blocking I/O for all operations + +Memory Limits +------------- + +- OrderBook: 10K trades, 1K depth entries +- DataManager: 1K bars per timeframe +- Tick Buffer: 1K tick circular buffer +- Cache: 100 entry LRU cache + +See :doc:`../user_guide/client` for configuration options. \ No newline at end of file diff --git a/docs/api/trading.rst b/docs/api/trading.rst index c52e92a..3dc8efc 100644 --- a/docs/api/trading.rst +++ b/docs/api/trading.rst @@ -46,7 +46,13 @@ Position Factory Functions Trading Suite ------------- -.. autofunction:: create_trading_suite +.. autoclass:: TradingSuite + :members: + :undoc-members: + :show-inheritance: + + The TradingSuite is the primary interface for trading operations, combining all managers + into a unified interface. Use the async :meth:`create` class method to initialize. Order Models ------------ diff --git a/docs/authentication.rst b/docs/authentication.rst index 74b4ffe..d257eaf 100644 --- a/docs/authentication.rst +++ b/docs/authentication.rst @@ -288,16 +288,10 @@ Example: Production Setup if not os.getenv('PROJECT_X_USERNAME'): raise ValueError("PROJECT_X_USERNAME environment variable not set") - # Create client - client = ProjectX.from_env() - - # Authenticate - await client.authenticate() - - # Verify authentication - print(f"Authenticated successfully: {client.account_info.name}") - - return client + # Note: This function should not be used with async context manager + # Instead, use ProjectX.from_env() directly with async with + # This is shown here for demonstration purposes only + return ProjectX.from_env() except ProjectXAuthenticationError as e: print(f"Authentication failed: {e}") @@ -307,10 +301,12 @@ Example: Production Setup raise async def main(): - # Usage with context manager - async with await create_authenticated_client() as client: + # Correct usage: ProjectX.from_env() returns a client directly + # No need to await before using as context manager + async with ProjectX.from_env() as client: + await client.authenticate() # Use client for operations - instruments = await client.search_instruments('MGC') + instruments = await client.search_instruments('MNQ') print(f"Found {len(instruments)} instruments") asyncio.run(main()) diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 0000000..997449d --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,34 @@ +Changelog +========= + +For the complete changelog, see the ``CHANGELOG.md`` file in the repository root. + +Recent Releases +--------------- + +v3.1.11 (Latest) +^^^^^^^^^^^^^^^^ + +- **Fixed**: ManagedTrade ``_get_market_price()`` implementation +- **Added**: Multi-timeframe price fallback for ManagedTrade +- **Updated**: TradingSuite passes data_manager to ManagedTrade + +v3.1.10 +^^^^^^^ + +- Minor updates and improvements + +v3.1.9 +^^^^^^ + +- **Fixed**: Tick price alignment in real-time data manager +- **Documented**: ProjectX volume data limitation + +v3.1.8 +^^^^^^ + +- **Fixed**: Real-time data processing for E-mini contracts +- **Added**: Bar timer mechanism for low-volume periods +- **Improved**: Symbol matching for contract resolution + +See ``CHANGELOG.md`` for complete version history. \ No newline at end of file diff --git a/docs/examples/basic_usage.rst b/docs/examples/basic_usage.rst new file mode 100644 index 0000000..8e25286 --- /dev/null +++ b/docs/examples/basic_usage.rst @@ -0,0 +1,16 @@ +Basic Usage Examples +==================== + +See the ``examples/`` directory in the repository for complete working examples. + +Quick Examples +-------------- + +For comprehensive examples, please refer to: + +- ``examples/01_basic_client_connection.py`` - Authentication and setup +- ``examples/02_order_management.py`` - Order placement +- ``examples/03_position_management.py`` - Position tracking +- ``examples/04_realtime_data.py`` - Real-time streaming + +See :doc:`../quickstart` for inline examples. \ No newline at end of file diff --git a/docs/examples/portfolio_management.rst b/docs/examples/portfolio_management.rst new file mode 100644 index 0000000..3165a44 --- /dev/null +++ b/docs/examples/portfolio_management.rst @@ -0,0 +1,14 @@ +Portfolio Management Examples +============================== + +See the ``examples/`` directory for portfolio management examples. + +Available Examples +------------------ + +- ``examples/03_position_management.py`` - Position tracking +- ``examples/08_order_and_position_tracking.py`` - Integrated monitoring +- ``examples/15_order_lifecycle_tracking.py`` - Order lifecycle +- ``examples/15_risk_management.py`` - Risk management + +See :doc:`../user_guide/trading` for detailed documentation. \ No newline at end of file diff --git a/docs/examples/real_time_data.rst b/docs/examples/real_time_data.rst new file mode 100644 index 0000000..e53cef0 --- /dev/null +++ b/docs/examples/real_time_data.rst @@ -0,0 +1,14 @@ +Real-Time Data Examples +======================= + +See the ``examples/`` directory for real-time data examples. + +Available Examples +------------------ + +- ``examples/04_realtime_data.py`` - Basic real-time streaming +- ``examples/05_orderbook_analysis.py`` - Level 2 orderbook +- ``examples/10_unified_event_system.py`` - Event-driven trading +- ``examples/realtime_data_manager/`` - Advanced real-time patterns + +See :doc:`../user_guide/real_time` for detailed documentation. \ No newline at end of file diff --git a/docs/examples/trading_strategies.rst b/docs/examples/trading_strategies.rst new file mode 100644 index 0000000..f09af03 --- /dev/null +++ b/docs/examples/trading_strategies.rst @@ -0,0 +1,14 @@ +Trading Strategy Examples +========================= + +See the ``examples/`` directory for complete strategy implementations. + +Available Examples +------------------ + +- ``examples/06_multi_timeframe_strategy.py`` - Multi-timeframe analysis +- ``examples/12_simplified_strategy.py`` - Simplified strategy patterns +- ``examples/15_risk_management.py`` - Risk management examples +- ``examples/16_managed_trades.py`` - ManagedTrade usage + +See :doc:`../user_guide/analysis` for technical analysis examples. \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index cf380a4..e17cd26 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,10 +20,10 @@ project-x-py Documentation **project-x-py** is a high-performance **async Python SDK** for the `ProjectX Trading Platform `_ Gateway API. This library enables developers to build sophisticated trading strategies and applications by providing comprehensive async access to futures trading operations, real-time market data, Level 2 orderbook analysis, and a complete technical analysis suite with 58+ TA-Lib compatible indicators including pattern recognition. .. note:: - **Version 3.1.0**: High-performance production suite with 2-5x performance improvements. Features memory-mapped overflow storage, orjson integration, WebSocket message batching, and advanced caching with compression. Complete async architecture with unified TradingSuite interface. + **Version 3.1.11**: High-performance production suite with 2-5x performance improvements. Features memory-mapped overflow storage, orjson integration, WebSocket message batching, and advanced caching with compression. Complete async architecture with unified TradingSuite interface. Latest update includes ManagedTrade automatic market price fetching for risk-managed trades. -.. warning:: - **Development Phase**: This project is under active development. New updates may introduce breaking changes without backward compatibility. During this development phase, we prioritize clean, modern code architecture over maintaining legacy implementations. +.. note:: + **Stable Production Release**: Since v3.1.1, this project maintains strict semantic versioning with backward compatibility between minor versions. Breaking changes only occur in major version releases (4.0.0+). Deprecation warnings are provided for at least 2 minor versions before removal. .. note:: **Important**: This is a **client library/SDK**, not a trading strategy. It provides the tools and infrastructure to help developers create their own trading strategies that integrate with the ProjectX platform. @@ -66,7 +66,7 @@ Start trading:: # Place an order using the integrated order manager response = await suite.orders.place_limit_order( - contract_id=suite.instrument.id, + contract_id=suite.instrument_id, side=0, size=1, limit_price=21050.0 diff --git a/docs/license.rst b/docs/license.rst new file mode 100644 index 0000000..bee30c5 --- /dev/null +++ b/docs/license.rst @@ -0,0 +1,25 @@ +License +======= + +MIT License +----------- + +Copyright (c) 2024 project-x-py + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/docs/quickstart.rst b/docs/quickstart.rst index b540dba..204ca65 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -89,7 +89,7 @@ Step 4: Place Your First Order # Place a limit order using the integrated order manager response = await suite.orders.place_limit_order( - contract_id=suite.instrument.id, # Use pre-loaded instrument ID + contract_id=suite.instrument_id, # Use instrument ID side=0, # 0=Buy, 1=Sell size=1, # 1 contract limit_price=21050.0 # Limit price @@ -166,43 +166,45 @@ Basic Trading Workflow .. code-block:: python - from project_x_py import ProjectX, create_order_manager, create_position_manager, create_realtime_client + from project_x_py import TradingSuite async def trading_workflow(): - # 1. Initialize client - async with ProjectX.from_env() as client: - await client.authenticate() - - # Get instrument details - instrument = await client.get_instrument('MNQ') # V3: actual symbol - - # 2. V3: Set up trading managers with JWT and account ID - realtime_client = await create_realtime_client( - jwt_token=client.jwt_token, - account_id=str(client.account_id) - ) - order_manager = create_order_manager(client, realtime_client) - position_manager = create_position_manager(client, realtime_client) - - # 3. Check account status - print(f"Account balance: ${client.account_info.balance:,.2f}") - - # 4. Get market data - data = await client.get_bars('MNQ', days=1, interval=5) # V3: actual symbol + # V3.1: Use TradingSuite for simplified initialization + suite = await TradingSuite.create('MNQ') + + # All managers are automatically initialized and connected + # suite.client - Authenticated ProjectX client + # suite.orders - Order manager + # suite.positions - Position manager + # suite.data - Real-time data manager + # suite.events - Event bus for notifications + + # Check account status + print(f"Account balance: ${suite.client.account_info.balance:,.2f}") + + # Get market data using integrated data manager + data = await suite.data.get_data('5min') + if data is not None and not data.is_empty(): + current_price = float(data['close'].tail(1).item()) + else: + # Fallback to client API if real-time data not available + data = await suite.client.get_bars('MNQ', days=1, interval=5) current_price = float(data.select('close').tail(1).item()) - # 5. Place bracket order (entry + stop + target) - bracket = await order_manager.place_bracket_order( - contract_id=instrument.id, - side=0, # Buy - size=1, - entry_price=current_price - 5.0, # Entry below market - stop_loss_price=current_price - 10.0, # $5 risk - take_profit_price=current_price + 5.0 # $10 profit target - ) + # Place bracket order (entry + stop + target) + bracket = await suite.orders.place_bracket_order( + contract_id=suite.instrument_id, + side=0, # Buy + size=1, + entry_price=current_price - 5.0, # Entry below market + stop_loss_price=current_price - 10.0, # $10 risk + take_profit_price=current_price + 10.0 # $10 profit target + ) - if bracket.success: - print("Bracket order placed successfully!") + if bracket.success: + print("Bracket order placed successfully!") + + await suite.disconnect() asyncio.run(trading_workflow()) @@ -253,28 +255,25 @@ Error Handling .. code-block:: python - from project_x_py import ProjectXError, ProjectXOrderError + from project_x_py import TradingSuite, ProjectXError, ProjectXOrderError async def place_order_with_error_handling(): try: - async with ProjectX.from_env() as client: - await client.authenticate() - - instrument = await client.get_instrument('MNQ') # V3: actual symbol - # V3: Create realtime client with JWT - realtime_client = await create_realtime_client( - jwt_token=client.jwt_token, - account_id=str(client.account_id) - ) - order_manager = create_order_manager(client, realtime_client) - - # Attempt to place order - response = await order_manager.place_limit_order( - contract_id=instrument.id, - side=0, - size=1, - limit_price=21050.0 # V3: realistic MNQ price - ) + # V3.1: Use TradingSuite for all trading operations + suite = await TradingSuite.create('MNQ') + + # Attempt to place order using integrated order manager + response = await suite.orders.place_limit_order( + contract_id=suite.instrument_id, + side=0, + size=1, + limit_price=21050.0 # Realistic MNQ price + ) + + if response.success: + print(f"Order placed: {response.orderId}") + + await suite.disconnect() except ProjectXOrderError as e: print(f"Order error: {e}") diff --git a/docs/support.rst b/docs/support.rst new file mode 100644 index 0000000..a5cbd9f --- /dev/null +++ b/docs/support.rst @@ -0,0 +1,36 @@ +Support +======= + +Getting Help +------------ + +If you need help with project-x-py: + +1. **Documentation**: Read the comprehensive documentation +2. **Examples**: Check the ``examples/`` directory for working code +3. **Issues**: Search existing issues on GitHub +4. **Community**: Join the discussion forums + +Reporting Issues +---------------- + +To report a bug or request a feature: + +1. Check if the issue already exists on GitHub +2. Create a new issue with: + + - Clear title and description + - Steps to reproduce (for bugs) + - Expected vs actual behavior + - Code snippets if relevant + - SDK version and Python version + +Contributing +------------ + +We welcome contributions! See :doc:`advanced/contributing` for guidelines. + +Commercial Support +------------------ + +For commercial support options, please contact the maintainers. \ No newline at end of file diff --git a/docs/user_guide/analysis.rst b/docs/user_guide/analysis.rst new file mode 100644 index 0000000..4b2542a --- /dev/null +++ b/docs/user_guide/analysis.rst @@ -0,0 +1,411 @@ +Technical Analysis Guide +======================== + +Comprehensive guide to using 58+ technical indicators and pattern recognition tools. + +Overview +-------- + +The SDK includes a full suite of TA-Lib compatible indicators built on Polars DataFrames for high performance. + +Indicator Categories +-------------------- + +Available Indicators +~~~~~~~~~~~~~~~~~~~~ + +**Momentum Indicators** +- RSI (Relative Strength Index) +- MACD (Moving Average Convergence Divergence) +- Stochastic Oscillator (STOCH) +- Williams %R (WILLR) +- Commodity Channel Index (CCI) +- Money Flow Index (MFI) +- Rate of Change (ROC) +- Ultimate Oscillator (ULTOSC) + +**Trend Indicators** +- SMA (Simple Moving Average) +- EMA (Exponential Moving Average) +- WMA (Weighted Moving Average) +- DEMA (Double Exponential Moving Average) +- TEMA (Triple Exponential Moving Average) +- KAMA (Kaufman Adaptive Moving Average) +- ADX (Average Directional Index) +- Aroon Indicator + +**Volatility Indicators** +- Bollinger Bands (BBANDS) +- Average True Range (ATR) +- Keltner Channels (KC) +- Donchian Channels (DC) +- Standard Deviation (STDDEV) + +**Volume Indicators** +- On-Balance Volume (OBV) +- Volume Weighted Average Price (VWAP) +- Accumulation/Distribution (AD) +- Chaikin Money Flow (CMF) + +**Pattern Recognition (v2.0.2+)** +- Fair Value Gap (FVG) +- Order Block (ORDERBLOCK) +- Waddah Attar Explosion (WAE) + +Basic Usage +----------- + +Function-Style Interface +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from project_x_py import TradingSuite + from project_x_py.indicators import RSI, SMA, MACD, BBANDS + import asyncio + + async def apply_indicators(): + suite = await TradingSuite.create("MNQ") + + # Get historical data + data = await suite.client.get_bars("MNQ", days=30, interval=60) + + # Apply indicators using pipe + data = (data + .pipe(RSI, period=14) + .pipe(SMA, period=20) + .pipe(SMA, period=50, column_name="sma_50") + .pipe(MACD, fast_period=12, slow_period=26, signal_period=9) + .pipe(BBANDS, period=20, std_dev=2.0) + ) + + # Access indicator values + latest = data.tail(1) + print(f"RSI: {latest['rsi_14'][0]:.2f}") + print(f"SMA(20): {latest['sma_20'][0]:.2f}") + print(f"Upper Band: {latest['bb_upper_20'][0]:.2f}") + + await suite.disconnect() + + asyncio.run(apply_indicators()) + +Class-Based Interface +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from project_x_py.indicators import RSI, BollingerBands, MACD + + async def class_based_indicators(): + suite = await TradingSuite.create("ES") + data = await suite.client.get_bars("ES", days=20, interval=15) + + # Create indicator instances + rsi = RSI(period=14) + bb = BollingerBands(period=20, std_dev=2.0) + macd_indicator = MACD() + + # Calculate indicators + data = rsi.calculate(data) + data = bb.calculate(data) + data = macd_indicator.calculate(data) + + # Get signals + rsi_signals = rsi.get_signals(data) + print(f"RSI Signal: {rsi_signals.tail(1)['signal'][0]}") + +Pattern Recognition +------------------- + +Fair Value Gap (FVG) +~~~~~~~~~~~~~~~~~~~~ + +Identifies price imbalances that may act as support/resistance: + +.. code-block:: python + + from project_x_py.indicators import FVG + + async def detect_fvg(): + suite = await TradingSuite.create("MNQ") + data = await suite.client.get_bars("MNQ", days=5, interval=15) + + # Detect Fair Value Gaps + data_with_fvg = FVG( + data, + min_gap_size=0.001, # Minimum 0.1% gap + check_mitigation=True, # Track if gaps are filled + use_shadows=True # Include wicks in analysis + ) + + # Find recent gaps + gaps = data_with_fvg.filter( + pl.col("fvg_bullish") | pl.col("fvg_bearish") + ) + + for row in gaps.tail(5).iter_rows(named=True): + gap_type = "Bullish" if row['fvg_bullish'] else "Bearish" + print(f"{row['timestamp']}: {gap_type} FVG") + print(f" Gap High: ${row['fvg_high']:.2f}") + print(f" Gap Low: ${row['fvg_low']:.2f}") + if row['fvg_mitigated']: + print(f" Mitigated at: {row['fvg_mitigation_time']}") + +Order Blocks +~~~~~~~~~~~~ + +Identifies institutional order zones: + +.. code-block:: python + + from project_x_py.indicators import OrderBlock + + async def find_order_blocks(): + suite = await TradingSuite.create("ES") + data = await suite.client.get_bars("ES", days=10, interval=60) + + # Detect order blocks + ob = OrderBlock( + lookback=10, # Bars to look back + min_volume_percentile=70, # High volume requirement + use_wicks=True, # Include wicks + filter_overlap=True # Remove overlapping blocks + ) + + data_with_blocks = ob.calculate(data) + + # Find active order blocks + blocks = data_with_blocks.filter( + pl.col("orderblock_bullish") | pl.col("orderblock_bearish") + ) + + for row in blocks.tail(3).iter_rows(named=True): + block_type = "Bullish" if row['orderblock_bullish'] else "Bearish" + print(f"{block_type} Order Block:") + print(f" Zone: ${row['orderblock_low']:.2f} - ${row['orderblock_high']:.2f}") + print(f" Volume: {row['orderblock_volume']:,}") + print(f" Strength: {row['orderblock_strength']:.2f}") + +Waddah Attar Explosion +~~~~~~~~~~~~~~~~~~~~~~ + +Detects strong trends and breakouts: + +.. code-block:: python + + from project_x_py.indicators import WAE + + async def waddah_attar(): + suite = await TradingSuite.create("MNQ") + data = await suite.client.get_bars("MNQ", days=5, interval=5) + + # Apply WAE indicator + data_with_wae = WAE( + data, + sensitivity=150, # Sensitivity to price changes + fast_period=20, # Fast EMA period + slow_period=40, # Slow EMA period + channel_period=20, # Bollinger Band period + channel_mult=2.0 # BB multiplier + ) + + # Find explosion signals + explosions = data_with_wae.filter( + pl.col("wae_explosion") > pl.col("wae_dead_zone") + ) + + for row in explosions.tail(5).iter_rows(named=True): + trend = "Bullish" if row['wae_trend'] == 1 else "Bearish" + print(f"{row['timestamp']}: {trend} Explosion") + print(f" Strength: {row['wae_explosion']:.4f}") + print(f" Above dead zone: {row['wae_explosion'] - row['wae_dead_zone']:.4f}") + +Strategy Development +-------------------- + +Multi-Indicator Strategy +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def multi_indicator_strategy(): + suite = await TradingSuite.create("ES") + + # Get data and apply indicators + data = await suite.client.get_bars("ES", days=20, interval=30) + + data = (data + .pipe(RSI, period=14) + .pipe(MACD) + .pipe(SMA, period=20) + .pipe(SMA, period=50, column_name="sma_50") + .pipe(BBANDS, period=20) + .pipe(ATR, period=14) + ) + + # Generate signals + latest = data.tail(1).to_dict(as_series=False) + + rsi = latest['rsi_14'][0] + macd = latest['macd'][0] + signal = latest['macd_signal'][0] + price = latest['close'][0] + sma20 = latest['sma_20'][0] + sma50 = latest['sma_50'][0] + bb_upper = latest['bb_upper_20'][0] + bb_lower = latest['bb_lower_20'][0] + atr = latest['atr_14'][0] + + # Trading logic + buy_signal = ( + rsi < 30 and # Oversold + macd > signal and # MACD bullish + price > sma20 > sma50 and # Uptrend + price <= bb_lower # At lower band + ) + + sell_signal = ( + rsi > 70 and # Overbought + macd < signal and # MACD bearish + price < sma20 < sma50 and # Downtrend + price >= bb_upper # At upper band + ) + + if buy_signal: + print("BUY Signal Generated") + print(f"Entry: ${price:.2f}") + print(f"Stop: ${price - 2*atr:.2f}") + print(f"Target: ${price + 3*atr:.2f}") + elif sell_signal: + print("SELL Signal Generated") + +Backtesting Support +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def backtest_strategy(): + suite = await TradingSuite.create("MNQ") + + # Get historical data + data = await suite.client.get_bars("MNQ", days=60, interval=60) + + # Apply indicators + data = (data + .pipe(RSI, period=14) + .pipe(SMA, period=20) + ) + + # Simulate trades + trades = [] + position = None + + for row in data.iter_rows(named=True): + if position is None: + # Check entry + if row['rsi_14'] < 30 and row['close'] > row['sma_20']: + position = { + 'entry_time': row['timestamp'], + 'entry_price': row['close'], + 'side': 'long' + } + else: + # Check exit + if row['rsi_14'] > 70: + position['exit_time'] = row['timestamp'] + position['exit_price'] = row['close'] + position['pnl'] = row['close'] - position['entry_price'] + trades.append(position) + position = None + + # Calculate statistics + if trades: + total_pnl = sum(t['pnl'] for t in trades) + win_rate = sum(1 for t in trades if t['pnl'] > 0) / len(trades) + + print(f"Total trades: {len(trades)}") + print(f"Win rate: {win_rate:.1%}") + print(f"Total P&L: ${total_pnl:.2f}") + +Custom Indicators +----------------- + +Creating Custom Indicators +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import polars as pl + + def custom_momentum_indicator( + data: pl.DataFrame, + period: int = 10, + smoothing: int = 3 + ) -> pl.DataFrame: + """Custom momentum indicator with smoothing.""" + + # Calculate raw momentum + momentum = data.with_columns([ + (pl.col("close") - pl.col("close").shift(period)) + .alias(f"momentum_{period}") + ]) + + # Apply smoothing + momentum = momentum.with_columns([ + pl.col(f"momentum_{period}") + .rolling_mean(window_size=smoothing) + .alias(f"momentum_smooth_{period}") + ]) + + # Generate signals + momentum = momentum.with_columns([ + pl.when(pl.col(f"momentum_smooth_{period}") > 0) + .then(1) + .when(pl.col(f"momentum_smooth_{period}") < 0) + .then(-1) + .otherwise(0) + .alias("momentum_signal") + ]) + + return momentum + + async def use_custom_indicator(): + suite = await TradingSuite.create("ES") + data = await suite.client.get_bars("ES", days=10, interval=15) + + # Apply custom indicator + data = custom_momentum_indicator(data, period=10, smoothing=3) + + # Check signals + latest = data.tail(1) + signal = latest['momentum_signal'][0] + + if signal == 1: + print("Bullish momentum") + elif signal == -1: + print("Bearish momentum") + +Performance Tips +---------------- + +1. **Chain operations**: Use `.pipe()` for efficient chaining +2. **Vectorized calculations**: Leverage Polars' columnar operations +3. **Avoid loops**: Use DataFrame operations instead of iterating +4. **Cache results**: Store calculated indicators for reuse +5. **Use appropriate periods**: Balance accuracy vs noise + +Best Practices +-------------- + +1. **Validate data**: Check for None/empty DataFrames +2. **Handle edge cases**: Insufficient data for indicator calculations +3. **Normalize inputs**: Ensure consistent data types +4. **Test thoroughly**: Validate indicators against known implementations +5. **Document parameters**: Clear descriptions of indicator settings + +Next Steps +---------- + +- :doc:`../api/indicators` - Complete indicator API reference +- :doc:`../examples/trading_strategies` - Strategy examples +- :doc:`trading` - Executing trades based on signals \ No newline at end of file diff --git a/docs/user_guide/client.rst b/docs/user_guide/client.rst new file mode 100644 index 0000000..211c065 --- /dev/null +++ b/docs/user_guide/client.rst @@ -0,0 +1,238 @@ +Client Usage Guide +================== + +The ProjectX client is the core component for interacting with the TopStepX API. + +Overview +-------- + +The ``ProjectX`` client provides async access to all API endpoints and manages authentication, session handling, and connection pooling. + +Creating a Client +----------------- + +Using Environment Variables (Recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import asyncio + from project_x_py import ProjectX + + async def main(): + async with ProjectX.from_env() as client: + await client.authenticate() + print(f"Connected: {client.account_info.name}") + + asyncio.run(main()) + +Direct Instantiation +~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def main(): + async with ProjectX( + username="your_username", + api_key="your_api_key" + ) as client: + await client.authenticate() + # Use client + +Using TradingSuite (Simplified) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from project_x_py import TradingSuite + + async def main(): + # TradingSuite handles client creation and authentication + suite = await TradingSuite.create("MNQ") + + # Access client through suite + account = suite.client.account_info + print(f"Account: {account.name}") + + await suite.disconnect() + + asyncio.run(main()) + +Core Operations +--------------- + +Account Information +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async with ProjectX.from_env() as client: + await client.authenticate() + + # Get account info + account = client.account_info + print(f"Balance: ${account.balance:,.2f}") + print(f"Can Trade: {account.can_trade}") + + # List all accounts + accounts = await client.list_accounts() + for acc in accounts: + print(f"{acc.name}: ${acc.balance:,.2f}") + +Instrument Search +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + # Search for instruments + instruments = await client.search_instruments("MNQ") + for inst in instruments: + print(f"{inst.name}: {inst.description}") + print(f" Tick size: {inst.tick_size}") + print(f" Contract ID: {inst.id}") + + # Get specific instrument + mnq = await client.get_instrument("MNQ") + print(f"MNQ Contract: {mnq.id}") + +Market Data +~~~~~~~~~~~ + +.. code-block:: python + + # Get historical bars + data = await client.get_bars("MNQ", days=5, interval=15) + print(f"Retrieved {len(data)} bars") + + # With specific time range (v3.1.5+) + from datetime import datetime + + start = datetime(2025, 1, 1, 9, 30) + end = datetime(2025, 1, 10, 16, 0) + data = await client.get_bars( + "MNQ", + start_time=start, + end_time=end, + interval=60 + ) + +Performance Features +-------------------- + +Connection Pooling +~~~~~~~~~~~~~~~~~~ + +The client automatically manages HTTP connection pooling for optimal performance: + +- Reuses connections across requests +- Automatic retry on network failures +- Configurable timeout and retry settings + +Caching +~~~~~~~ + +Intelligent caching reduces API calls: + +- Instrument data cached for session +- Account information cached with TTL +- Cache invalidation on updates + +JWT Token Management +~~~~~~~~~~~~~~~~~~~~ + +Automatic token handling: + +- Token refresh at 80% lifetime +- Seamless re-authentication +- No manual token management needed + +Error Handling +-------------- + +.. code-block:: python + + from project_x_py import ( + ProjectXAuthenticationError, + ProjectXOrderError, + ProjectXRateLimitError + ) + + try: + async with ProjectX.from_env() as client: + await client.authenticate() + # Operations + + except ProjectXAuthenticationError as e: + print(f"Auth failed: {e}") + except ProjectXRateLimitError as e: + print(f"Rate limited: {e}") + except Exception as e: + print(f"Unexpected error: {e}") + +Configuration +------------- + +.. code-block:: python + + from project_x_py import ProjectXConfig + + config = ProjectXConfig( + api_url="https://api.topstepx.com/api", + websocket_url="wss://api.topstepx.com", + timeout_seconds=30, + retry_attempts=3, + max_connections=10, + cache_ttl=300 + ) + + async with ProjectX.from_env(config=config) as client: + await client.authenticate() + +Best Practices +-------------- + +1. **Always use context managers**: Ensures proper cleanup +2. **Handle authentication errors**: Check credentials on failure +3. **Monitor rate limits**: Implement backoff strategies +4. **Enable logging for debugging**: Use ``setup_logging()`` +5. **Cache frequently used data**: Reduce API calls + +Advanced Usage +-------------- + +Custom Headers +~~~~~~~~~~~~~~ + +.. code-block:: python + + client = ProjectX.from_env() + client.session.headers.update({ + "X-Custom-Header": "value" + }) + +Health Checks +~~~~~~~~~~~~~ + +.. code-block:: python + + health = await client.get_health_status() + print(f"API Status: {health['status']}") + print(f"Authenticated: {health['authenticated']}") + +Performance Stats +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + stats = await client.get_performance_stats() + print(f"API Calls: {stats['api_calls']}") + print(f"Cache Hits: {stats['cache_hits']}") + print(f"Avg Response Time: {stats['avg_response_ms']}ms") + +Next Steps +---------- + +- :doc:`market_data` - Working with market data +- :doc:`trading` - Order and position management +- :doc:`real_time` - Real-time data streaming +- :doc:`analysis` - Technical analysis tools \ No newline at end of file diff --git a/docs/user_guide/market_data.rst b/docs/user_guide/market_data.rst new file mode 100644 index 0000000..9de1959 --- /dev/null +++ b/docs/user_guide/market_data.rst @@ -0,0 +1,288 @@ +Market Data Guide +================= + +Access historical and real-time market data through the ProjectX SDK. + +Historical Data +--------------- + +Basic Usage +~~~~~~~~~~~ + +.. code-block:: python + + from project_x_py import TradingSuite + import asyncio + + async def get_historical_data(): + suite = await TradingSuite.create("MNQ") + + # Get last 5 days of 15-minute bars + data = await suite.client.get_bars("MNQ", days=5, interval=15) + print(f"Retrieved {len(data)} bars") + + # Display OHLCV data + for row in data.head(5).iter_rows(named=True): + print(f"{row['timestamp']}: O={row['open']} H={row['high']} " + f"L={row['low']} C={row['close']} V={row['volume']}") + + await suite.disconnect() + + asyncio.run(get_historical_data()) + +Time Range Queries +~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from datetime import datetime, timedelta + + async def get_specific_range(): + suite = await TradingSuite.create("ES") + + # Get data for specific date range + end_time = datetime.now() + start_time = end_time - timedelta(days=30) + + data = await suite.client.get_bars( + "ES", + start_time=start_time, + end_time=end_time, + interval=60 # 1-hour bars + ) + + print(f"Data from {start_time} to {end_time}") + print(f"Total bars: {len(data)}") + +Multiple Timeframes +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def multi_timeframe_analysis(): + suite = await TradingSuite.create( + "MNQ", + timeframes=["1min", "5min", "15min", "1hr", "1day"] + ) + + # Data manager handles multiple timeframes + for timeframe in ["1min", "5min", "15min"]: + data = await suite.data.get_data(timeframe) + if data is not None: + print(f"{timeframe}: {len(data)} bars") + +Real-Time Data +-------------- + +Setting Up Real-Time Feeds +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from project_x_py import TradingSuite, EventType + + async def setup_realtime(): + suite = await TradingSuite.create( + "MNQ", + timeframes=["1min", "5min"], + initial_days=3 # Load 3 days of history + ) + + # Register callbacks for real-time updates + async def on_tick(event): + tick = event.data + print(f"Tick: ${tick['price']} @ {tick['timestamp']}") + + async def on_new_bar(event): + bar = event.data + print(f"New {bar['timeframe']} bar: ${bar['data']['close']}") + + await suite.on(EventType.TICK, on_tick) + await suite.on(EventType.NEW_BAR, on_new_bar) + + # Keep running to receive updates + await asyncio.sleep(60) + await suite.disconnect() + +Accessing Current Data +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def monitor_prices(): + suite = await TradingSuite.create("MNQ") + + while True: + # Get current price + current = await suite.data.get_current_price() + print(f"Current price: ${current:,.2f}") + + # Get latest bar + data = await suite.data.get_data("1min", bars=1) + if data is not None and not data.is_empty(): + latest = data.tail(1) + for row in latest.iter_rows(named=True): + print(f"Latest bar: {row['timestamp']} - ${row['close']}") + + await asyncio.sleep(5) + +Data Quality +------------ + +Tick Alignment +~~~~~~~~~~~~~~ + +All prices are automatically aligned to instrument tick size: + +.. code-block:: python + + # Prices are automatically aligned + # For MNQ (tick size 0.25): + # 23927.62 β†’ 23927.50 + # 23927.88 β†’ 23928.00 + +Volume Considerations +~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + Volume data represents trades executed through the ProjectX platform only, not full exchange volume from CME. + +.. code-block:: python + + async def analyze_volume(): + suite = await TradingSuite.create("MNQ") + data = await suite.client.get_bars("MNQ", days=1, interval=5) + + # Calculate volume metrics + total_volume = data['volume'].sum() + avg_volume = data['volume'].mean() + + print(f"Total volume: {total_volume:,}") + print(f"Average volume per bar: {avg_volume:.2f}") + +Data Processing +--------------- + +Working with Polars DataFrames +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import polars as pl + + async def process_data(): + suite = await TradingSuite.create("ES") + data = await suite.client.get_bars("ES", days=10, interval=60) + + # Polars operations + daily_stats = data.group_by( + pl.col("timestamp").dt.date() + ).agg([ + pl.col("high").max().alias("day_high"), + pl.col("low").min().alias("day_low"), + pl.col("volume").sum().alias("day_volume"), + (pl.col("close").last() - pl.col("open").first()).alias("day_change") + ]) + + print(daily_stats) + +Adding Custom Columns +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def add_custom_metrics(): + suite = await TradingSuite.create("MNQ") + data = await suite.client.get_bars("MNQ", days=5, interval=15) + + # Add custom calculations + data = data.with_columns([ + ((pl.col("high") - pl.col("low")) / pl.col("close") * 100).alias("range_pct"), + (pl.col("close") - pl.col("open")).alias("bar_change"), + (pl.col("volume").rolling_mean(window_size=20)).alias("vol_ma20") + ]) + + print(data.columns) + +Memory Management +----------------- + +The SDK automatically manages memory for large datasets: + +.. code-block:: python + + async def memory_efficient(): + suite = await TradingSuite.create( + "ES", + timeframes=["1min", "5min", "15min"], + initial_days=30 # Large dataset + ) + + # Data manager automatically manages memory + # - Sliding windows for real-time data + # - Maximum bars per timeframe: 1000 (configurable) + # - Automatic cleanup of old data + + stats = suite.data.get_memory_stats() + print(f"Memory usage: {stats['memory_mb']:.2f} MB") + print(f"Total bars: {stats['total_bars']:,}") + print(f"Ticks processed: {stats['ticks_processed']:,}") + +Caching Strategies +------------------ + +.. code-block:: python + + from project_x_py import TradingSuite + + async def cached_operations(): + suite = await TradingSuite.create("MNQ") + + # First call fetches from API + data1 = await suite.client.get_bars("MNQ", days=1, interval=5) + + # Subsequent calls may use cache (within TTL) + data2 = await suite.client.get_bars("MNQ", days=1, interval=5) + + # Force refresh by using different parameters + data3 = await suite.client.get_bars("MNQ", days=2, interval=5) + +Best Practices +-------------- + +1. **Use appropriate timeframes**: Don't request 1-minute data for months of history +2. **Implement data validation**: Check for None/empty responses +3. **Handle missing data**: Markets are closed on weekends/holidays +4. **Monitor memory usage**: Use sliding windows for long-running applications +5. **Cache frequently used data**: Reduce API calls + +Error Handling +-------------- + +.. code-block:: python + + from project_x_py import ProjectXDataError + + async def safe_data_fetch(): + try: + suite = await TradingSuite.create("MNQ") + data = await suite.client.get_bars("MNQ", days=5) + + if data is None or data.is_empty(): + print("No data available") + return + + # Process data + print(f"Processing {len(data)} bars") + + except ProjectXDataError as e: + print(f"Data error: {e}") + except Exception as e: + print(f"Unexpected error: {e}") + +Next Steps +---------- + +- :doc:`trading` - Place and manage orders +- :doc:`real_time` - Real-time data streaming +- :doc:`analysis` - Technical analysis \ No newline at end of file diff --git a/docs/user_guide/real_time.rst b/docs/user_guide/real_time.rst new file mode 100644 index 0000000..527b23f --- /dev/null +++ b/docs/user_guide/real_time.rst @@ -0,0 +1,386 @@ +Real-Time Data Guide +==================== + +Stream live market data and trading events through WebSocket connections. + +Overview +-------- + +The SDK provides real-time data through WebSocket connections with automatic reconnection, message batching, and event-driven architecture. + +Setting Up Real-Time Feeds +--------------------------- + +Basic Setup +~~~~~~~~~~~ + +.. code-block:: python + + from project_x_py import TradingSuite, EventType + import asyncio + + async def setup_realtime(): + # TradingSuite automatically connects to real-time feeds + suite = await TradingSuite.create( + "MNQ", + timeframes=["1min", "5min", "15min"], + initial_days=3 # Load historical data first + ) + + print(f"Connected: {suite.realtime.is_connected()}") + print(f"Subscribed to: {suite.instrument_id}") + + # Keep running to receive data + await asyncio.sleep(60) + await suite.disconnect() + + asyncio.run(setup_realtime()) + +Event Types +----------- + +Available Events +~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from project_x_py import EventType + + # Market data events + EventType.TICK # Individual price updates + EventType.QUOTE_UPDATE # Bid/ask changes + EventType.TRADE_TICK # Executed trades + EventType.NEW_BAR # New OHLCV bar created + EventType.BAR_UPDATE # Existing bar updated + + # Trading events + EventType.ORDER_PLACED # Order submitted + EventType.ORDER_FILLED # Order executed + EventType.ORDER_CANCELLED # Order cancelled + EventType.ORDER_REJECTED # Order rejected + + # Position events + EventType.POSITION_OPENED # New position created + EventType.POSITION_UPDATE # Position changed + EventType.POSITION_CLOSED # Position closed + + # System events + EventType.CONNECTION_ESTABLISHED + EventType.CONNECTION_LOST + EventType.RECONNECTING + EventType.ERROR + +Registering Event Handlers +--------------------------- + +Using Decorators +~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def setup_handlers(): + suite = await TradingSuite.create("ES") + + @suite.events.on(EventType.TICK) + async def handle_tick(event): + tick = event.data + print(f"Tick: ${tick['price']} Vol: {tick['volume']}") + + @suite.events.on(EventType.NEW_BAR) + async def handle_new_bar(event): + bar = event.data + timeframe = bar['timeframe'] + data = bar['data'] + print(f"New {timeframe} bar: ${data['close']}") + + await asyncio.sleep(60) + +Using await suite.on() +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def register_handlers(): + suite = await TradingSuite.create("MNQ") + + async def on_quote(event): + quote = event.data + spread = quote['ask'] - quote['bid'] + print(f"Bid: ${quote['bid']} Ask: ${quote['ask']} Spread: ${spread}") + + async def on_trade(event): + trade = event.data + print(f"Trade: {trade['size']} @ ${trade['price']}") + + await suite.on(EventType.QUOTE_UPDATE, on_quote) + await suite.on(EventType.TRADE_TICK, on_trade) + +Real-Time Data Access +--------------------- + +Current Market State +~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def monitor_market(): + suite = await TradingSuite.create("MNQ") + + while True: + # Get current price + price = await suite.data.get_current_price() + + # Get latest bars + bars_1m = await suite.data.get_data("1min", bars=1) + bars_5m = await suite.data.get_data("5min", bars=1) + + # Get tick data + ticks = await suite.data.get_recent_ticks(count=10) + + print(f"Price: ${price:,.2f}") + print(f"Ticks in last batch: {len(ticks)}") + + await asyncio.sleep(5) + +OrderBook (Level 2) +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def monitor_orderbook(): + suite = await TradingSuite.create( + "ES", + features=["orderbook"] + ) + + async def on_orderbook_update(event): + # Get best bid/ask + best = await suite.orderbook.get_best_bid_ask() + print(f"Best Bid: ${best['bid']:,.2f} Ask: ${best['ask']:,.2f}") + + # Get market depth + bids = await suite.orderbook.get_orderbook_bids(levels=5) + asks = await suite.orderbook.get_orderbook_asks(levels=5) + + # Analyze imbalance + imbalance = await suite.orderbook.get_market_imbalance() + print(f"Imbalance: {imbalance:.2%}") + + await suite.on(EventType.ORDERBOOK_UPDATE, on_orderbook_update) + +Performance Optimization +------------------------ + +Message Batching +~~~~~~~~~~~~~~~~ + +The SDK automatically batches WebSocket messages for efficiency: + +.. code-block:: python + + # Messages are batched every 100ms by default + # This reduces overhead while maintaining low latency + + suite = await TradingSuite.create( + "MNQ", + websocket_config={ + "batch_interval_ms": 50, # Faster batching + "max_batch_size": 100 # Maximum messages per batch + } + ) + +Memory Management +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def memory_efficient_streaming(): + suite = await TradingSuite.create( + "ES", + timeframes=["1min", "5min"], + config={ + "max_bars_per_timeframe": 500, # Limit bar storage + "tick_buffer_size": 1000, # Circular tick buffer + "enable_compression": True # Compress old data + } + ) + + # Monitor memory usage + stats = suite.data.get_memory_stats() + print(f"Memory usage: {stats['memory_mb']:.2f} MB") + print(f"Bars stored: {stats['total_bars']}") + print(f"Ticks processed: {stats['ticks_processed']}") + +Connection Management +--------------------- + +Monitoring Connection +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def monitor_connection(): + suite = await TradingSuite.create("MNQ") + + # Check connection status + print(f"Connected: {suite.realtime.is_connected()}") + print(f"User hub: {suite.realtime.user_connected}") + print(f"Market hub: {suite.realtime.market_connected}") + + # Handle connection events + async def on_disconnect(event): + print("Connection lost, will auto-reconnect...") + + async def on_reconnect(event): + print("Reconnected successfully") + + await suite.on(EventType.CONNECTION_LOST, on_disconnect) + await suite.on(EventType.CONNECTION_ESTABLISHED, on_reconnect) + +Manual Reconnection +~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def handle_reconnection(): + suite = await TradingSuite.create("MNQ") + + # Force reconnection if needed + if not suite.realtime.is_connected(): + await suite.realtime.disconnect() + await suite.realtime.connect() + + # Re-subscribe to market data + await suite.data.start_realtime_feed() + +Building Trading Strategies +--------------------------- + +Event-Driven Strategy +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def moving_average_strategy(): + suite = await TradingSuite.create("MNQ") + + async def on_new_bar(event): + if event.data['timeframe'] != "5min": + return + + # Get recent data + data = await suite.data.get_data("5min", bars=50) + if data is None or len(data) < 50: + return + + # Calculate moving averages + ma20 = data['close'].tail(20).mean() + ma50 = data['close'].tail(50).mean() + current = data['close'].tail(1)[0] + + # Trading logic + position = await suite.positions.get_position("MNQ") + + if ma20 > ma50 and current > ma20 and not position: + # Buy signal + await suite.orders.place_market_order( + contract_id=suite.instrument_id, + side=0, + size=1 + ) + elif ma20 < ma50 and current < ma20 and position and position.is_long: + # Sell signal + await suite.positions.close_position("MNQ") + + await suite.on(EventType.NEW_BAR, on_new_bar) + +Tick Scalping +~~~~~~~~~~~~~ + +.. code-block:: python + + async def tick_scalper(): + suite = await TradingSuite.create( + "ES", + features=["orderbook"] + ) + + position_size = 0 + entry_price = None + + async def on_tick(event): + nonlocal position_size, entry_price + + tick = event.data + price = tick['price'] + + # Get orderbook imbalance + imbalance = await suite.orderbook.get_market_imbalance() + + if position_size == 0: + # Entry logic + if imbalance > 0.7: # Strong buy pressure + result = await suite.orders.place_market_order( + contract_id=suite.instrument_id, + side=0, + size=1 + ) + if result.success: + position_size = 1 + entry_price = price + else: + # Exit logic + profit = price - entry_price + if profit >= 2 or profit <= -1: # 2 point target or 1 point stop + await suite.orders.place_market_order( + contract_id=suite.instrument_id, + side=1, + size=1 + ) + position_size = 0 + entry_price = None + + await suite.on(EventType.TICK, on_tick) + +Best Practices +-------------- + +1. **Handle disconnections gracefully**: Implement reconnection logic +2. **Process events asynchronously**: Don't block event handlers +3. **Use appropriate timeframes**: Balance between granularity and performance +4. **Monitor memory usage**: Clean up old data in long-running applications +5. **Implement error handling**: Catch and log exceptions in handlers +6. **Test with replay data**: Use historical data to test strategies + +Troubleshooting +--------------- + +.. code-block:: python + + from project_x_py import setup_logging + + # Enable debug logging for WebSocket + setup_logging(level='DEBUG') + + async def debug_connection(): + suite = await TradingSuite.create("MNQ") + + # Check what's happening + if not suite.realtime.is_connected(): + print("Not connected to WebSocket") + + # Check subscriptions + print(f"Subscribed instruments: {suite.realtime.subscriptions}") + + # Check data flow + await asyncio.sleep(5) + stats = suite.data.get_memory_stats() + if stats['ticks_processed'] == 0: + print("No ticks received - check market hours") + +Next Steps +---------- + +- :doc:`analysis` - Technical analysis tools +- :doc:`../examples/real_time_data` - Complete examples +- :doc:`../api/orderbook` - OrderBook API reference \ No newline at end of file diff --git a/docs/user_guide/trading.rst b/docs/user_guide/trading.rst new file mode 100644 index 0000000..1e6529a --- /dev/null +++ b/docs/user_guide/trading.rst @@ -0,0 +1,374 @@ +Trading Guide +============= + +Complete guide to order placement, position management, and risk control. + +Order Management +---------------- + +Basic Order Types +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from project_x_py import TradingSuite + import asyncio + + async def place_orders(): + suite = await TradingSuite.create("MNQ") + + # Market order + market_order = await suite.orders.place_market_order( + contract_id=suite.instrument_id, + side=0, # 0=Buy, 1=Sell + size=1 + ) + + # Limit order + current_price = await suite.data.get_current_price() + limit_order = await suite.orders.place_limit_order( + contract_id=suite.instrument_id, + side=0, + size=1, + limit_price=current_price - 10 # Buy 10 points below market + ) + + # Stop order + stop_order = await suite.orders.place_stop_order( + contract_id=suite.instrument_id, + side=1, # Sell + size=1, + stop_price=current_price - 20 # Stop loss 20 points below + ) + + await suite.disconnect() + + asyncio.run(place_orders()) + +Bracket Orders +~~~~~~~~~~~~~~ + +Place entry, stop loss, and take profit in one operation: + +.. code-block:: python + + async def place_bracket_order(): + suite = await TradingSuite.create("ES") + + current_price = await suite.data.get_current_price() + + # Bracket order with OCO (One-Cancels-Other) + bracket = await suite.orders.place_bracket_order( + contract_id=suite.instrument_id, + side=0, # Buy + size=1, + entry_price=current_price - 2, # Entry below market + stop_loss_price=current_price - 10, # Risk 8 points + take_profit_price=current_price + 10 # Target 12 points + ) + + if bracket.success: + print(f"Bracket order placed: {bracket}") + print(f"Entry ID: {bracket.entry_order_id}") + print(f"Stop ID: {bracket.stop_order_id}") + print(f"Target ID: {bracket.limit_order_id}") + +Order Modification +~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def modify_orders(): + suite = await TradingSuite.create("MNQ") + + # Get open orders + orders = await suite.orders.search_open_orders() + + for order in orders: + if order.type == 1: # Limit order + # Modify price + result = await suite.orders.modify_order( + order_id=order.id, + new_price=order.limit_price + 5 + ) + print(f"Modified order {order.id}: {result}") + +Order Cancellation +~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def cancel_orders(): + suite = await TradingSuite.create("MNQ") + + # Cancel specific order + result = await suite.orders.cancel_order(order_id=12345) + + # Cancel all orders for instrument + orders = await suite.orders.search_open_orders() + for order in orders: + if order.contract_id == suite.instrument_id: + await suite.orders.cancel_order(order.id) + +Position Management +------------------- + +Tracking Positions +~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def monitor_positions(): + suite = await TradingSuite.create("MNQ") + + # Get all positions + positions = await suite.positions.get_all_positions() + + for position in positions: + direction = "LONG" if position.is_long else "SHORT" + pnl = position.unrealized_pnl + + print(f"{position.contract_id}:") + print(f" Direction: {direction}") + print(f" Size: {position.size}") + print(f" Avg Price: ${position.average_price:,.2f}") + print(f" P&L: ${pnl:,.2f}") + + # Get specific position + mnq_position = await suite.positions.get_position("MNQ") + if mnq_position: + print(f"MNQ position: {mnq_position.size} @ ${mnq_position.average_price}") + +Portfolio Analytics +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def portfolio_analysis(): + suite = await TradingSuite.create("MNQ") + + # Portfolio P&L + portfolio = await suite.positions.get_portfolio_pnl() + print(f"Total P&L: ${portfolio['total_pnl']:,.2f}") + print(f"Open P&L: ${portfolio['unrealized_pnl']:,.2f}") + print(f"Closed P&L: ${portfolio['realized_pnl']:,.2f}") + print(f"Position count: {portfolio['position_count']}") + + # Performance metrics + metrics = await suite.positions.get_performance_metrics() + print(f"Win rate: {metrics['win_rate']:.1%}") + print(f"Profit factor: {metrics['profit_factor']:.2f}") + print(f"Average win: ${metrics['avg_win']:,.2f}") + print(f"Average loss: ${metrics['avg_loss']:,.2f}") + +Position Closing +~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def close_positions(): + suite = await TradingSuite.create("MNQ") + + # Close specific position + await suite.positions.close_position("MNQ") + + # Close all positions + await suite.positions.close_all_positions() + + # Partial close + position = await suite.positions.get_position("ES") + if position and position.size > 1: + await suite.orders.place_market_order( + contract_id="ES", + side=1 if position.is_long else 0, # Opposite side + size=1 # Close 1 contract + ) + +Risk Management +--------------- + +Using ManagedTrade (v3.1.11+) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def risk_managed_trade(): + suite = await TradingSuite.create( + "MNQ", + features=["risk_manager"] + ) + + current_price = await suite.data.get_current_price() + + # Managed trade with automatic risk control + async with suite.managed_trade(max_risk_percent=0.01) as trade: + # Entry price fetched automatically if not provided + result = await trade.enter_long( + stop_loss=current_price - 50, + take_profit=current_price + 100 + ) + + print(f"Trade entered: {result}") + + # Optional: Adjust stop to breakeven + if result['position']: + await trade.adjust_stop(current_price) + + # Automatic cleanup on exit + await suite.disconnect() + +Position Sizing +~~~~~~~~~~~~~~~ + +.. code-block:: python + + async def calculate_position_size(): + suite = await TradingSuite.create( + "MNQ", + features=["risk_manager"] + ) + + account = suite.client.account_info + risk_amount = account.balance * 0.01 # Risk 1% of account + + current_price = await suite.data.get_current_price() + stop_loss = current_price - 50 + + # Calculate position size based on risk + risk_per_contract = abs(current_price - stop_loss) + position_size = int(risk_amount / risk_per_contract) + + print(f"Account: ${account.balance:,.2f}") + print(f"Risk amount: ${risk_amount:,.2f}") + print(f"Position size: {position_size} contracts") + +Advanced Features +----------------- + +OCO Orders +~~~~~~~~~~ + +.. code-block:: python + + async def place_oco_orders(): + suite = await TradingSuite.create("ES") + + current_price = await suite.data.get_current_price() + + # One-Cancels-Other: Stop loss and take profit + oco = await suite.orders.place_oco_order( + contract_id=suite.instrument_id, + stop_price=current_price - 20, # Stop loss + stop_size=1, + limit_price=current_price + 30, # Take profit + limit_size=1 + ) + + print(f"OCO placed: {oco}") + +Trailing Stops +~~~~~~~~~~~~~~ + +.. code-block:: python + + async def trailing_stop(): + suite = await TradingSuite.create("MNQ") + + # Monitor position and adjust stop + position = await suite.positions.get_position("MNQ") + if position and position.is_long: + current_price = await suite.data.get_current_price() + trail_distance = 20 # Points + + # Find existing stop order + orders = await suite.orders.search_open_orders() + stop_order = next( + (o for o in orders + if o.type == 3 and o.contract_id == suite.instrument_id), + None + ) + + if stop_order: + new_stop = current_price - trail_distance + if new_stop > stop_order.stop_price: + # Trail the stop up + await suite.orders.modify_order( + order_id=stop_order.id, + new_price=new_stop + ) + +Order Templates +~~~~~~~~~~~~~~~ + +.. code-block:: python + + from project_x_py import OrderTemplate + + async def use_templates(): + suite = await TradingSuite.create("MNQ") + + # Create reusable templates + scalp_template = OrderTemplate( + side=0, + size=2, + order_type="limit", + offset_points=2 # Enter 2 points from market + ) + + # Use template + current_price = await suite.data.get_current_price() + order = await suite.orders.place_order_from_template( + template=scalp_template, + contract_id=suite.instrument_id, + base_price=current_price + ) + +Event-Driven Trading +-------------------- + +.. code-block:: python + + from project_x_py import EventType + + async def event_trading(): + suite = await TradingSuite.create("ES") + + # React to order fills + async def on_order_fill(event): + order = event.data + print(f"Order filled: {order['id']} at ${order['filled_price']}") + + # Place stop loss after fill + if order['side'] == 0: # Buy filled + await suite.orders.place_stop_order( + contract_id=order['contract_id'], + side=1, + size=order['size'], + stop_price=order['filled_price'] - 10 + ) + + await suite.on(EventType.ORDER_FILLED, on_order_fill) + + # React to position changes + async def on_position_update(event): + position = event.data + print(f"Position updated: {position['size']} @ ${position['average_price']}") + + await suite.on(EventType.POSITION_UPDATE, on_position_update) + +Best Practices +-------------- + +1. **Always use stop losses**: Protect capital with proper risk management +2. **Check order status**: Verify fills before assuming position +3. **Handle partial fills**: Orders may fill in multiple parts +4. **Monitor margin**: Ensure sufficient margin before placing orders +5. **Test with small sizes**: Start with minimum position sizes +6. **Use paper trading**: Test strategies in simulation first + +Next Steps +---------- + +- :doc:`real_time` - Real-time data and events +- :doc:`analysis` - Technical analysis tools +- :doc:`../api/trading` - Complete API reference \ No newline at end of file diff --git a/src/project_x_py/client/__init__.py b/src/project_x_py/client/__init__.py index 452ed29..2ba9a8d 100644 --- a/src/project_x_py/client/__init__.py +++ b/src/project_x_py/client/__init__.py @@ -63,28 +63,28 @@ class ProjectX(ProjectXBase): >>> await client.authenticate() >>> >>> # V3: Get account info with typed models - >>> account = client.get_account_info() + >>> account = client.account_info # After authentication >>> print(f"Account: {account.name}") >>> print(f"ID: {account.id}") >>> print(f"Balance: ${account.balance:,.2f}") >>> >>> # V3: Search for instruments with smart contract selection - >>> instruments = await client.search_instruments("gold") - >>> gold = instruments[0] if instruments else None - >>> if gold: - >>> print(f"Found: {gold.name} ({gold.symbol})") - >>> print(f"Contract ID: {gold.id}") + >>> instruments = await client.search_instruments("MNQ") + >>> mnq = instruments[0] if instruments else None + >>> if mnq: + >>> print(f"Found: {mnq.name} ({mnq.symbol})") + >>> print(f"Contract ID: {mnq.id}") >>> >>> # V3: Get historical data concurrently (returns Polars DataFrames) >>> tasks = [ - >>> client.get_bars("MGC", days=5, interval=5), # 5-min bars - >>> client.get_bars("MNQ", days=1, interval=1), # 1-min bars + >>> client.get_bars("MNQ", days=5, interval=5), # 5-min bars + >>> client.get_bars("ES", days=1, interval=1), # 1-min bars >>> ] - >>> gold_data, nasdaq_data = await asyncio.gather(*tasks) + >>> nasdaq_data, sp500_data = await asyncio.gather(*tasks) >>> - >>> print(f"Gold bars: {len(gold_data)} (Polars DataFrame)") >>> print(f"Nasdaq bars: {len(nasdaq_data)} (Polars DataFrame)") - >>> print(f"Columns: {gold_data.columns}") + >>> print(f"S&P 500 bars: {len(sp500_data)} (Polars DataFrame)") + >>> print(f"Columns: {nasdaq_data.columns}") >>> >>> asyncio.run(main()) @@ -105,7 +105,7 @@ class ProjectX(ProjectXBase): >>> >>> # Access integrated managers easily >>> order = await suite.orders.place_market_order( - ... contract_id=suite.instrument_info.id, + ... contract_id=suite.instrument_id, ... side=0, # Buy ... size=1 ... ) diff --git a/src/project_x_py/client/auth.py b/src/project_x_py/client/auth.py index c8212ff..f9b8248 100644 --- a/src/project_x_py/client/auth.py +++ b/src/project_x_py/client/auth.py @@ -146,7 +146,7 @@ async def authenticate(self: "ProjectXClientProtocol") -> None: >>> async with ProjectX.from_env() as client: >>> try: >>> await client.authenticate() - >>> account = client.get_account_info() + >>> account = client.account_info # Access account info after auth >>> print(f"Authenticated account: {account.name}") >>> print(f"Account ID: {account.id}") >>> print(f"Balance: ${account.balance:,.2f}") diff --git a/src/project_x_py/client/market_data.py b/src/project_x_py/client/market_data.py index d5767ab..e570a39 100644 --- a/src/project_x_py/client/market_data.py +++ b/src/project_x_py/client/market_data.py @@ -92,7 +92,7 @@ async def get_instrument( Get detailed instrument information with caching. Args: - symbol: Trading symbol (e.g., 'NQ', 'ES', 'MGC') or full contract ID + symbol: Trading symbol (e.g., 'MNQ', 'ES', 'NQ') or full contract ID (e.g., 'CON.F.US.MNQ.U25') live: If True, only return live/active contracts (default: False) @@ -284,7 +284,7 @@ async def search_instruments( Example: >>> # V3: Search for instruments by symbol or name - >>> instruments = await client.search_instruments("gold") + >>> instruments = await client.search_instruments("MNQ") >>> for inst in instruments: >>> print(f"{inst.symbol}: {inst.name}") >>> print(f" Contract ID: {inst.id}") @@ -339,7 +339,7 @@ async def get_bars( for financial analysis and technical indicator calculations. Args: - symbol: Symbol of the instrument (e.g., "MGC", "MNQ", "ES") + symbol: Symbol of the instrument (e.g., "MNQ", "ES", "NQ") days: Number of days of historical data (default: 8, ignored if start_time/end_time provided) interval: Interval between bars in the specified unit (default: 5) unit: Time unit for the interval (default: 2 for minutes) @@ -360,8 +360,8 @@ async def get_bars( Example: >>> # V3: Get historical OHLCV data as Polars DataFrame - >>> # Get 5 days of 15-minute gold data - >>> data = await client.get_bars("MGC", days=5, interval=15) + >>> # Get 5 days of 15-minute Nasdaq futures data + >>> data = await client.get_bars("MNQ", days=5, interval=15) >>> print(f"Retrieved {len(data)} bars") >>> print(f"Columns: {data.columns}") >>> print( diff --git a/src/project_x_py/models.py b/src/project_x_py/models.py index 6cb496e..8e684bd 100644 --- a/src/project_x_py/models.py +++ b/src/project_x_py/models.py @@ -38,11 +38,11 @@ # Create instrument model instrument = Instrument( - id="CON.F.US.MGC.M25", - name="MGCH25", - description="Mini Gold Futures March 2025", - tickSize=0.1, - tickValue=1.0, + id="CON.F.US.MNQ.U25", + name="MNQU25", + description="E-mini NASDAQ-100 Futures September 2025", + tickSize=0.25, + tickValue=0.50, activeContract=True, ) @@ -50,7 +50,7 @@ order = Order( id=12345, accountId=1001, - contractId="CON.F.US.MGC.M25", + contractId="CON.F.US.MNQ.U25", creationTimestamp="2024-01-01T10:00:00Z", updateTimestamp="2024-01-01T10:00:05Z", status=OrderStatus.OPEN, @@ -64,7 +64,7 @@ position = Position( id=67890, accountId=1001, - contractId="CON.F.US.MGC.M25", + contractId="CON.F.US.MNQ.U25", creationTimestamp="2024-01-01T10:00:00Z", type=PositionType.LONG, size=5, @@ -137,7 +137,7 @@ class Instrument: Attributes: id (str): Unique contract identifier used in API calls - name (str): Contract name/symbol (e.g., "MGCH25") + name (str): Contract name/symbol (e.g., "MNQU25", "ESH25") description (str): Human-readable description of the contract tickSize (float): Minimum price movement (e.g., 0.1) tickValue (float): Dollar value per tick movement diff --git a/src/project_x_py/order_manager/__init__.py b/src/project_x_py/order_manager/__init__.py index ec5dfdb..efae07b 100644 --- a/src/project_x_py/order_manager/__init__.py +++ b/src/project_x_py/order_manager/__init__.py @@ -35,59 +35,51 @@ Example Usage: ```python - # V3: Async order management with event bus integration + # V3.1: Order management with TradingSuite import asyncio - from project_x_py import ( - ProjectX, - create_realtime_client, - create_order_manager, - EventBus, - ) + from project_x_py import TradingSuite async def main(): - async with ProjectX.from_env() as client: - await client.authenticate() - - # V3: Create event bus and realtime client - event_bus = EventBus() - realtime_client = await create_realtime_client( - client.get_session_token(), str(client.get_account_info().id) - ) - - # V3: Create order manager with dependencies - om = create_order_manager(client, realtime_client, event_bus) - await om.initialize(realtime_client) - - # V3: Place a market order - response = await om.place_market_order( - "MNQ", - side=0, - size=1, # Buy 1 contract - ) - print(f"Market order placed: {response.orderId}") - - # V3: Place a bracket order with automatic risk management - bracket = await om.place_bracket_order( - contract_id="MGC", - side=0, # Buy - size=1, - entry_price=2050.0, - stop_loss_price=2040.0, - take_profit_price=2070.0, - ) - print(f"Bracket order IDs:") - print(f" Entry: {bracket.entry_order_id}") - print(f" Stop: {bracket.stop_order_id}") - print(f" Target: {bracket.target_order_id}") - - # V3: Add stop loss to existing position - await om.add_stop_loss_to_position("MGC", stop_price=2040.0) - - # V3: Check order statistics - stats = await om.get_order_statistics() - print(f"Orders placed: {stats['orders_placed']}") - print(f"Fill rate: {stats['fill_rate']:.1%}") + # Create suite with all managers integrated + suite = await TradingSuite.create("MNQ") + + # V3.1: Place a market order using integrated order manager + response = await suite.orders.place_market_order( + contract_id=suite.instrument_id, + side=0, # Buy + size=1, # 1 contract + ) + print(f"Market order placed: {response.orderId}") + + # V3.1: Place a bracket order with automatic risk management + # Get current price for realistic entry + current_price = await suite.data.get_current_price() + + bracket = await suite.orders.place_bracket_order( + contract_id=suite.instrument_id, + side=0, # Buy + size=1, + entry_price=current_price - 10.0, # Limit entry below market + stop_loss_price=current_price - 25.0, # Stop loss $25 below entry + take_profit_price=current_price + 25.0, # Take profit $25 above entry + ) + print(f"Bracket order IDs:") + print(f" Entry: {bracket.entry_order_id}") + print(f" Stop: {bracket.stop_order_id}") + print(f" Target: {bracket.target_order_id}") + + # V3.1: Add stop loss to existing position + await suite.orders.add_stop_loss_to_position( + suite.instrument_id, stop_price=current_price - 20.0 + ) + + # V3.1: Check order statistics + stats = await suite.orders.get_order_statistics() + print(f"Orders placed: {stats['orders_placed']}") + print(f"Fill rate: {stats['fill_rate']:.1%}") + + await suite.disconnect() asyncio.run(main()) diff --git a/src/project_x_py/order_manager/bracket_orders.py b/src/project_x_py/order_manager/bracket_orders.py index 0b9772f..921d8ba 100644 --- a/src/project_x_py/order_manager/bracket_orders.py +++ b/src/project_x_py/order_manager/bracket_orders.py @@ -27,49 +27,45 @@ Example Usage: ```python - # V3: Place bracket orders with automatic risk management + # V3.1: Place bracket orders with TradingSuite import asyncio - from project_x_py import ProjectX, create_realtime_client, EventBus - from project_x_py.order_manager import OrderManager + from project_x_py import TradingSuite async def main(): - async with ProjectX.from_env() as client: - await client.authenticate() - - # V3: Initialize order manager with dependencies - event_bus = EventBus() - realtime_client = await create_realtime_client( - client.get_session_token(), str(client.get_account_info().id) - ) - om = OrderManager(client, event_bus) - await om.initialize(realtime_client) - - # V3: Place a bullish bracket order (buy with stop below, target above) - bracket = await om.place_bracket_order( - contract_id="MGC", - side=0, # Buy - size=1, - entry_price=2050.0, - stop_loss_price=2040.0, # Risk: $10 per contract - take_profit_price=2070.0, # Reward: $20 per contract - entry_type="limit", # Can also use "market" - ) - - print(f"Bracket order placed successfully:") - print(f" Entry Order ID: {bracket.entry_order_id}") - print(f" Stop Loss ID: {bracket.stop_order_id}") - print(f" Take Profit ID: {bracket.target_order_id}") - - # V3: Place a bearish bracket order (sell with stop above, target below) - short_bracket = await om.place_bracket_order( - contract_id="MNQ", - side=1, # Sell - size=2, - entry_price=18500.0, - stop_loss_price=18550.0, # Stop above for short - take_profit_price=18400.0, # Target below for short - ) + # Initialize suite with integrated order manager + suite = await TradingSuite.create("MNQ") + + # Get current market price for realistic order placement + current_price = await suite.data.get_current_price() + + # V3.1: Place a bullish bracket order (buy with stop below, target above) + bracket = await suite.orders.place_bracket_order( + contract_id=suite.instrument_id, + side=0, # Buy + size=1, + entry_price=current_price - 10.0, # Enter below market + stop_loss_price=current_price - 30.0, # Risk: $30 per contract + take_profit_price=current_price + 20.0, # Reward: $30 per contract + entry_type="limit", # Can also use "market" + ) + + print(f"Bracket order placed successfully:") + print(f" Entry Order ID: {bracket.entry_order_id}") + print(f" Stop Loss ID: {bracket.stop_order_id}") + print(f" Take Profit ID: {bracket.target_order_id}") + + # V3.1: Place a bearish bracket order (sell with stop above, target below) + short_bracket = await suite.orders.place_bracket_order( + contract_id=suite.instrument_id, + side=1, # Sell + size=2, + entry_price=current_price + 10.0, # Enter above market for short + stop_loss_price=current_price + 30.0, # Stop above for short + take_profit_price=current_price - 20.0, # Target below for short + ) + + await suite.disconnect() asyncio.run(main()) diff --git a/src/project_x_py/order_manager/core.py b/src/project_x_py/order_manager/core.py index aa26157..e38e3ff 100644 --- a/src/project_x_py/order_manager/core.py +++ b/src/project_x_py/order_manager/core.py @@ -19,32 +19,31 @@ Example Usage: ```python - # V3: Initialize order manager with event bus and real-time support + # V3.1: Order manager is integrated in TradingSuite import asyncio - from project_x_py import ProjectX, create_realtime_client, EventBus - from project_x_py.order_manager import OrderManager + from project_x_py import TradingSuite async def main(): - async with ProjectX.from_env() as client: - await client.authenticate() + # All managers are automatically initialized + suite = await TradingSuite.create("ES") - # V3: Create dependencies - event_bus = EventBus() - realtime_client = await create_realtime_client( - client.get_session_token(), str(client.get_account_info().id) - ) + # V3.1: Access instrument ID for orders + contract_id = suite.instrument_id - # V3: Initialize order manager - om = OrderManager(client, event_bus) - await om.initialize(realtime_client) + # V3.1: Place orders with automatic price alignment + await suite.orders.place_limit_order( + contract_id=contract_id, + side=0, # Buy + size=1, + limit_price=5000.0, + ) - # V3: Place orders with automatic price alignment - await om.place_limit_order("ES", side=0, size=1, limit_price=5000.0) + # V3.1: Monitor order statistics + stats = await suite.orders.get_order_statistics() + print(f"Fill rate: {stats['fill_rate']:.1%}") - # V3: Monitor order statistics - stats = await om.get_order_statistics() - print(f"Fill rate: {stats['fill_rate']:.1%}") + await suite.disconnect() asyncio.run(main()) diff --git a/src/project_x_py/order_manager/order_types.py b/src/project_x_py/order_manager/order_types.py index 639704f..485d1ae 100644 --- a/src/project_x_py/order_manager/order_types.py +++ b/src/project_x_py/order_manager/order_types.py @@ -30,13 +30,20 @@ Example Usage: ```python - # Assuming om is an instance of OrderManager - await om.place_limit_order("MES", 1, 2, 5000.0) - await om.place_market_order("MGC", 0, 1) - await om.place_stop_order("MGC", 1, 1, 2040.0) - await om.place_trailing_stop_order("MGC", 1, 1, 5.0) - await om.place_join_bid_order("MGC", 1) # Join bid side - await om.place_join_ask_order("MGC", 1) # Join ask side + # V3.1: Using TradingSuite's integrated order manager + suite = await TradingSuite.create("MNQ") + + # Get current price for realistic order placement + current_price = await suite.data.get_current_price() + + await suite.orders.place_limit_order( + suite.instrument_id, 1, 2, current_price - 10.0 + ) + await suite.orders.place_market_order(suite.instrument_id, 0, 1) + await suite.orders.place_stop_order(suite.instrument_id, 1, 1, current_price - 20.0) + await suite.orders.place_trailing_stop_order(suite.instrument_id, 1, 1, 15.0) + await suite.orders.place_join_bid_order(suite.instrument_id, 1) # Join bid + await suite.orders.place_join_ask_order(suite.instrument_id, 1) # Join ask ``` See Also: @@ -87,7 +94,10 @@ async def place_market_order( OrderPlaceResponse: Response containing order ID and status Example: - >>> response = await order_manager.place_market_order("MGC", 0, 1) + >>> # V3.1: Place market order + >>> response = await suite.orders.place_market_order( + ... suite.instrument_id, 0, 1 + ... ) """ return await self.place_order( contract_id=contract_id, @@ -119,7 +129,11 @@ async def place_limit_order( OrderPlaceResponse: Response containing order ID and status Example: - >>> response = await order_manager.place_limit_order("MGC", 1, 1, 2050.0) + >>> # V3.1: Place limit order with realistic price + >>> current_price = await suite.data.get_current_price() + >>> response = await suite.orders.place_limit_order( + ... suite.instrument_id, 1, 1, current_price - 10.0 + ... ) """ return await self.place_order( contract_id=contract_id, @@ -152,8 +166,11 @@ async def place_stop_order( OrderPlaceResponse: Response containing order ID and status Example: - >>> # Stop loss for long position - >>> response = await order_manager.place_stop_order("MGC", 1, 1, 2040.0) + >>> # V3.1: Stop loss for long position + >>> current_price = await suite.data.get_current_price() + >>> response = await suite.orders.place_stop_order( + ... suite.instrument_id, 1, 1, current_price - 20.0 + ... ) """ return await self.place_order( contract_id=contract_id, @@ -186,8 +203,12 @@ async def place_trailing_stop_order( OrderPlaceResponse: Response containing order ID and status Example: - >>> response = await order_manager.place_trailing_stop_order( - ... "MGC", 1, 1, 5.0 + >>> # V3.1: Trailing stop order + >>> response = await suite.orders.place_trailing_stop_order( + ... suite.instrument_id, + ... 1, + ... 1, + ... 15.0, # Trail by $15 ... ) """ return await self.place_order( @@ -222,8 +243,10 @@ async def place_join_bid_order( OrderPlaceResponse: Response containing order ID and status Example: - >>> # Join the bid to provide liquidity - >>> response = await order_manager.place_join_bid_order("MGC", 1) + >>> # V3.1: Join the bid to provide liquidity + >>> response = await suite.orders.place_join_bid_order( + ... suite.instrument_id, 1 + ... ) """ return await self.place_order( contract_id=contract_id, @@ -256,8 +279,10 @@ async def place_join_ask_order( OrderPlaceResponse: Response containing order ID and status Example: - >>> # Join the ask to provide liquidity - >>> response = await order_manager.place_join_ask_order("MGC", 1) + >>> # V3.1: Join the ask to provide liquidity + >>> response = await suite.orders.place_join_ask_order( + ... suite.instrument_id, 1 + ... ) """ return await self.place_order( contract_id=contract_id, diff --git a/src/project_x_py/order_manager/position_orders.py b/src/project_x_py/order_manager/position_orders.py index 9ee3a8b..492cefc 100644 --- a/src/project_x_py/order_manager/position_orders.py +++ b/src/project_x_py/order_manager/position_orders.py @@ -27,42 +27,47 @@ Example Usage: ```python - # V3: Position-based order management + # V3.1: Position-based order management with TradingSuite import asyncio - from project_x_py import ProjectX, create_realtime_client, EventBus - from project_x_py.order_manager import OrderManager + from project_x_py import TradingSuite async def main(): - async with ProjectX.from_env() as client: - await client.authenticate() + # Initialize suite with integrated managers + suite = await TradingSuite.create("MNQ") - # V3: Initialize order manager - event_bus = EventBus() - realtime_client = await create_realtime_client( - client.get_session_token(), str(client.get_account_info().id) - ) - om = OrderManager(client, event_bus) - await om.initialize(realtime_client) + # Get current price for realistic order placement + current_price = await suite.data.get_current_price() - # V3: Close an existing position at market - await om.close_position("MNQ", method="market") + # V3.1: Close an existing position at market + await suite.orders.close_position(suite.instrument_id, method="market") - # V3: Close position with limit order - await om.close_position("MGC", method="limit", limit_price=2055.0) + # V3.1: Close position with limit order + await suite.orders.close_position( + suite.instrument_id, method="limit", limit_price=current_price + 5.0 + ) - # V3: Add protective orders to existing position - await om.add_stop_loss("MNQ", stop_price=18400.0) - await om.add_take_profit("MNQ", limit_price=18600.0) + # V3.1: Add protective orders to existing position + await suite.orders.add_stop_loss( + suite.instrument_id, stop_price=current_price - 25.0 + ) + await suite.orders.add_take_profit( + suite.instrument_id, limit_price=current_price + 25.0 + ) - # V3: Cancel specific order types for a position - await om.cancel_position_orders("MNQ", ["stop"]) # Cancel stops only - await om.cancel_position_orders("MNQ") # Cancel all orders + # V3.1: Cancel specific order types for a position + await suite.orders.cancel_position_orders( + suite.instrument_id, + ["stop"], # Cancel stops only + ) + await suite.orders.cancel_position_orders(suite.instrument_id) # Cancel all - # V3: Sync orders with position size after partial fill - await om.sync_orders_with_position( - "MGC", target_size=2, cancel_orphaned=True - ) + # V3.1: Sync orders with position size after partial fill + await suite.orders.sync_orders_with_position( + suite.instrument_id, target_size=2, cancel_orphaned=True + ) + + await suite.disconnect() asyncio.run(main()) @@ -117,16 +122,19 @@ async def close_position( OrderPlaceResponse: Response from closing order Example: - >>> # V3: Close position at market price - >>> response = await om.close_position("MGC", method="market") + >>> # V3.1: Close position at market price + >>> response = await suite.orders.close_position( + ... suite.instrument_id, method="market" + ... ) >>> print( ... f"Closing order ID: {response.orderId if response else 'No position'}" ... ) - >>> # V3: Close position with limit order for better price - >>> response = await om.close_position( - ... "MGC", method="limit", limit_price=2050.0 + >>> # V3.1: Close position with limit order for better price + >>> current_price = await suite.data.get_current_price() + >>> response = await suite.orders.close_position( + ... suite.instrument_id, method="limit", limit_price=current_price + 5.0 ... ) - >>> # V3: The method automatically determines the correct side + >>> # V3.1: The method automatically determines the correct side >>> # For long position: sells to close >>> # For short position: buys to cover """ @@ -179,14 +187,19 @@ async def add_stop_loss( OrderPlaceResponse if successful, None if no position Example: - >>> # V3: Add stop loss to protect existing position - >>> response = await om.add_stop_loss("MGC", stop_price=2040.0) + >>> # V3.1: Add stop loss to protect existing position + >>> current_price = await suite.data.get_current_price() + >>> response = await suite.orders.add_stop_loss( + ... suite.instrument_id, stop_price=current_price - 20.0 + ... ) >>> print( ... f"Stop order ID: {response.orderId if response else 'No position'}" ... ) - >>> # V3: Add partial stop (protect only part of position) - >>> response = await om.add_stop_loss("MGC", stop_price=2040.0, size=1) - >>> # V3: Stop is automatically placed on opposite side of position + >>> # V3.1: Add partial stop (protect only part of position) + >>> response = await suite.orders.add_stop_loss( + ... suite.instrument_id, stop_price=current_price - 20.0, size=1 + ... ) + >>> # V3.1: Stop is automatically placed on opposite side of position >>> # Long position: stop sell order below current price >>> # Short position: stop buy order above current price """ @@ -239,14 +252,19 @@ async def add_take_profit( OrderPlaceResponse if successful, None if no position Example: - >>> # V3: Add take profit target to existing position - >>> response = await om.add_take_profit("MGC", limit_price=2060.0) + >>> # V3.1: Add take profit target to existing position + >>> current_price = await suite.data.get_current_price() + >>> response = await suite.orders.add_take_profit( + ... suite.instrument_id, limit_price=current_price + 25.0 + ... ) >>> print( ... f"Target order ID: {response.orderId if response else 'No position'}" ... ) - >>> # V3: Add partial take profit (scale out strategy) - >>> response = await om.add_take_profit("MGC", limit_price=2060.0, size=1) - >>> # V3: Target is automatically placed on opposite side of position + >>> # V3.1: Add partial take profit (scale out strategy) + >>> response = await suite.orders.add_take_profit( + ... suite.instrument_id, limit_price=current_price + 25.0, size=1 + ... ) + >>> # V3.1: Target is automatically placed on opposite side of position >>> # Long position: limit sell order above current price >>> # Short position: limit buy order below current price """ @@ -369,17 +387,19 @@ async def cancel_position_orders( Dict with counts of cancelled orders by type Example: - >>> # V3: Cancel only stop orders for a position - >>> results = await om.cancel_position_orders("MGC", ["stop"]) + >>> # V3.1: Cancel only stop orders for a position + >>> results = await suite.orders.cancel_position_orders( + ... suite.instrument_id, ["stop"] + ... ) >>> print(f"Cancelled {results['stop']} stop orders") - >>> # V3: Cancel all orders for position (stops, targets, entries) - >>> results = await om.cancel_position_orders("MGC") + >>> # V3.1: Cancel all orders for position (stops, targets, entries) + >>> results = await suite.orders.cancel_position_orders(suite.instrument_id) >>> print( ... f"Cancelled: {results['stop']} stops, {results['target']} targets" ... ) - >>> # V3: Cancel specific order types - >>> results = await om.cancel_position_orders( - ... "MGC", order_types=["stop", "target"] + >>> # V3.1: Cancel specific order types + >>> results = await suite.orders.cancel_position_orders( + ... suite.instrument_id, order_types=["stop", "target"] ... ) """ if order_types is None: diff --git a/src/project_x_py/order_manager/utils.py b/src/project_x_py/order_manager/utils.py index 45ba81b..86f0b38 100644 --- a/src/project_x_py/order_manager/utils.py +++ b/src/project_x_py/order_manager/utils.py @@ -91,11 +91,11 @@ async def align_price_to_tick_size( float: Price aligned to tick size, or None if input price is None Example: - >>> # Align a price for MGC (tick size 0.1) - >>> aligned = await align_price_to_tick_size(2052.17, "MGC", client) - >>> print(aligned) # 2052.2 + >>> # V3.1: Align a price for MNQ (tick size 0.25) + >>> aligned = await align_price_to_tick_size(20052.17, "MNQ", client) + >>> print(aligned) # 20052.25 - >>> # Align a price for ES (tick size 0.25) + >>> # V3.1: Align a price for ES (tick size 0.25) >>> aligned = await align_price_to_tick_size(5000.17, "ES", client) >>> print(aligned) # 5000.25 """ diff --git a/src/project_x_py/orderbook/__init__.py b/src/project_x_py/orderbook/__init__.py index 1303ee7..f826b9f 100644 --- a/src/project_x_py/orderbook/__init__.py +++ b/src/project_x_py/orderbook/__init__.py @@ -36,51 +36,35 @@ Example Usage: ```python - # V3: Uses EventBus and factory functions - from project_x_py import ProjectX, create_orderbook, create_realtime_client - from project_x_py.events import EventBus, EventType + # V3.1: Using TradingSuite with integrated orderbook + from project_x_py import TradingSuite, EventType import asyncio async def main(): - # V3: ProjectX client with context manager - async with ProjectX.from_env() as client: - await client.authenticate() - - # V3: Create realtime client with factory function - realtime_client = await create_realtime_client( - jwt_token=client.jwt_token, account_id=str(client.account_id) - ) - - # V3: EventBus for unified event handling - event_bus = EventBus() - - # V3: Create orderbook with EventBus - orderbook = create_orderbook( - "MNQ", # V3: Using actual contract symbols - event_bus=event_bus, - project_x=client, - ) - await orderbook.initialize(realtime_client=realtime_client) + # V3.1: Create suite with orderbook feature + suite = await TradingSuite.create( + "MNQ", features=["orderbook"], timeframes=["1min"] + ) - # V3: Register event handlers - @event_bus.on(EventType.MARKET_DEPTH_UPDATE) - async def on_depth_update(data): - print(f"Depth update: {data['timestamp']}") + # V3.1: Register event handlers via integrated EventBus + @suite.events.on(EventType.MARKET_DEPTH_UPDATE) + async def on_depth_update(event): + print(f"Depth update: {event.timestamp}") - # Get basic orderbook snapshot - snapshot = await orderbook.get_orderbook_snapshot(levels=10) - print(f"Best bid: {snapshot['best_bid']}, Spread: {snapshot['spread']}") + # Get basic orderbook snapshot + snapshot = await suite.orderbook.get_orderbook_snapshot(levels=10) + print(f"Best bid: {snapshot['best_bid']}, Spread: {snapshot['spread']}") - # Advanced analytics - imbalance = await orderbook.get_market_imbalance(levels=5) - print(f"Market imbalance: {imbalance['imbalance_ratio']:.2f}") + # Advanced analytics + imbalance = await suite.orderbook.get_market_imbalance(levels=5) + print(f"Market imbalance: {imbalance['imbalance_ratio']:.2f}") - # Detection algorithms - icebergs = await orderbook.detect_iceberg_orders() - print(f"Detected {len(icebergs['iceberg_levels'])} iceberg orders") + # Detection algorithms + icebergs = await suite.orderbook.detect_iceberg_orders() + print(f"Detected {len(icebergs['iceberg_levels'])} iceberg orders") - await orderbook.cleanup() + await suite.disconnect() asyncio.run(main()) @@ -200,34 +184,33 @@ class OrderBook(OrderBookBase): - Hidden liquidity and volume pattern recognition Example: - >>> # V3: Create orderbook with EventBus - >>> event_bus = EventBus() - >>> orderbook = OrderBook("MNQ", event_bus, project_x_client) - >>> await orderbook.initialize(realtime_client) + >>> # V3.1: Using TradingSuite's integrated orderbook + >>> suite = await TradingSuite.create("MNQ", features=["orderbook"]) >>> - >>> # V3: Register event handlers - >>> @event_bus.on(EventType.MARKET_DEPTH_UPDATE) - >>> async def handle_depth(data): + >>> # V3.1: Register event handlers via suite's EventBus + >>> @suite.events.on(EventType.MARKET_DEPTH_UPDATE) + >>> async def handle_depth(event): + ... data = event.data ... print(f"Depth: {data['bids'][0]['price']} @ {data['bids'][0]['size']}") >>> >>> # Get basic orderbook data - >>> snapshot = await orderbook.get_orderbook_snapshot() + >>> snapshot = await suite.orderbook.get_orderbook_snapshot() >>> print(f"Spread: {snapshot['spread']}") >>> >>> # Advanced analytics - >>> imbalance = await orderbook.get_market_imbalance() - >>> liquidity = await orderbook.get_liquidity_levels() + >>> imbalance = await suite.orderbook.get_market_imbalance() + >>> liquidity = await suite.orderbook.get_liquidity_levels() >>> >>> # Detection algorithms - >>> icebergs = await orderbook.detect_iceberg_orders() - >>> clusters = await orderbook.detect_order_clusters() + >>> icebergs = await suite.orderbook.detect_iceberg_orders() + >>> clusters = await suite.orderbook.detect_order_clusters() >>> >>> # Volume profiling - >>> profile = await orderbook.get_volume_profile() - >>> support_resistance = await orderbook.get_support_resistance_levels() + >>> profile = await suite.orderbook.get_volume_profile() + >>> support_resistance = await suite.orderbook.get_support_resistance_levels() >>> - >>> # Cleanup when done - >>> await orderbook.cleanup() + >>> # Cleanup handled automatically + >>> await suite.disconnect() """ def __init__( @@ -293,22 +276,13 @@ async def initialize( initialization failed. Example: - >>> # V3: Initialize with EventBus and realtime client - >>> event_bus = EventBus() - >>> orderbook = OrderBook("MNQ", event_bus, client) - >>> # V3: Create realtime client with factory - >>> realtime_client = await create_realtime_client( - ... jwt_token=client.jwt_token, account_id=str(client.account_id) - ... ) - >>> success = await orderbook.initialize( - ... realtime_client=realtime_client, - ... subscribe_to_depth=True, - ... subscribe_to_quotes=True, - ... ) - >>> if success: + >>> # V3.1: Initialization handled by TradingSuite + >>> suite = await TradingSuite.create("MNQ", features=["orderbook"]) + >>> # Orderbook is automatically initialized with real-time connection + >>> if suite.orderbook: ... print("Orderbook initialized and receiving real-time data") ... else: - ... print("Failed to initialize orderbook") + ... print("Orderbook not available") """ try: # Start memory manager @@ -526,23 +500,20 @@ def create_orderbook( to initialize() before use. Example: - >>> # V3: Create an orderbook with EventBus - >>> event_bus = EventBus() - >>> orderbook = create_orderbook( - ... instrument="MNQ", # V3: Using actual contract symbols - ... event_bus=event_bus, - ... project_x=client, - ... timezone_str="America/Chicago", # V3: Using CME timezone + >>> # V3.1: Use TradingSuite instead of factory function + >>> # This function is deprecated - use TradingSuite.create() + >>> suite = await TradingSuite.create( + ... instrument="MNQ", + ... features=["orderbook"], + ... timezone_str="America/Chicago", # CME timezone ... ) >>> - >>> # V3: Initialize with factory-created realtime client - >>> realtime_client = await create_realtime_client( - ... jwt_token=client.jwt_token, account_id=str(client.account_id) - ... ) - >>> await orderbook.initialize(realtime_client=realtime_client) + >>> # Orderbook is automatically initialized + >>> # Access via suite.orderbook + >>> snapshot = await suite.orderbook.get_orderbook_snapshot() >>> - >>> # Start using the orderbook - >>> snapshot = await orderbook.get_orderbook_snapshot() + >>> # Note: create_orderbook is maintained for backward compatibility + >>> # but TradingSuite is the recommended approach """ # Note: realtime_client is passed to initialize() separately to allow # for async initialization diff --git a/src/project_x_py/orderbook/analytics.py b/src/project_x_py/orderbook/analytics.py index 4f6b9b5..d18a5ac 100644 --- a/src/project_x_py/orderbook/analytics.py +++ b/src/project_x_py/orderbook/analytics.py @@ -29,29 +29,26 @@ Example Usage: ```python - # V3: Using analytics with EventBus-enabled orderbook - from project_x_py import create_orderbook - from project_x_py.events import EventBus - - event_bus = EventBus() - orderbook = create_orderbook( - "MNQ", event_bus, project_x=client - ) # V3: actual symbol - await orderbook.initialize(realtime_client) - - # V3: Market imbalance analysis - imbalance = await orderbook.get_market_imbalance(levels=10) + # V3.1: Using analytics with TradingSuite's orderbook + from project_x_py import TradingSuite + + suite = await TradingSuite.create("MNQ", features=["orderbook"]) + + # V3.1: Market imbalance analysis + imbalance = await suite.orderbook.get_market_imbalance(levels=10) print(f"Imbalance: {imbalance['imbalance_ratio']:.2%}") print(f"Analysis: {imbalance['analysis']}") - # V3: Depth analysis with actual contract - depth = await orderbook.get_orderbook_depth(price_range=5.0) + # V3.1: Depth analysis + depth = await suite.orderbook.get_orderbook_depth(price_range=5.0) print(f"Bid depth: {depth['bid_depth']['total_volume']} contracts") print(f"Ask depth: {depth['ask_depth']['total_volume']} contracts") - # V3: Trade flow analysis - delta = await orderbook.get_cumulative_delta(time_window_minutes=60) + # V3.1: Trade flow analysis + delta = await suite.orderbook.get_cumulative_delta(time_window_minutes=60) print(f"Cumulative delta: {delta['cumulative_delta']}") + + await suite.disconnect() ``` See Also: diff --git a/src/project_x_py/orderbook/detection.py b/src/project_x_py/orderbook/detection.py index c10ed7a..3683b14 100644 --- a/src/project_x_py/orderbook/detection.py +++ b/src/project_x_py/orderbook/detection.py @@ -28,31 +28,28 @@ Example Usage: ```python - # V3: Advanced detection with EventBus-enabled orderbook - from project_x_py import create_orderbook - from project_x_py.events import EventBus, EventType - - event_bus = EventBus() - orderbook = create_orderbook( - "MNQ", event_bus, project_x=client - ) # V3: actual symbol - await orderbook.initialize(realtime_client) - - # V3: Detect iceberg orders with confidence scoring - icebergs = await orderbook.detect_iceberg_orders(min_refreshes=5) + # V3.1: Advanced detection with TradingSuite's orderbook + from project_x_py import TradingSuite + + suite = await TradingSuite.create("MNQ", features=["orderbook"]) + + # V3.1: Detect iceberg orders with confidence scoring + icebergs = await suite.orderbook.detect_iceberg_orders(min_refreshes=5) for level in icebergs["iceberg_levels"]: print(f"Iceberg at {level['price']:.2f}: confidence {level['confidence']:.1%}") - # V3: Order clustering analysis - clusters = await orderbook.detect_order_clusters(min_cluster_size=3) + # V3.1: Order clustering analysis + clusters = await suite.orderbook.detect_order_clusters(min_cluster_size=3) for cluster in clusters: print( f"Cluster at {cluster['center_price']:.2f}: {cluster['total_volume']} contracts" ) - # Advanced market metrics - metrics = await orderbook.get_advanced_market_metrics() + # V3.1: Advanced market metrics + metrics = await suite.orderbook.get_advanced_market_metrics() print(f"Book pressure ratio: {metrics['book_pressure']['pressure_ratio']}") + + await suite.disconnect() ``` See Also: diff --git a/src/project_x_py/orderbook/memory.py b/src/project_x_py/orderbook/memory.py index 51e45a3..2e4912c 100644 --- a/src/project_x_py/orderbook/memory.py +++ b/src/project_x_py/orderbook/memory.py @@ -27,34 +27,27 @@ Example Usage: ```python - # V3: Memory management with EventBus-enabled orderbook - from project_x_py import create_orderbook - from project_x_py.events import EventBus + # V3.1: Memory management with TradingSuite's orderbook + from project_x_py import TradingSuite - event_bus = EventBus() - orderbook = create_orderbook( - "MNQ", event_bus, project_x=client - ) # V3: actual symbol - await orderbook.initialize(realtime_client) + suite = await TradingSuite.create("MNQ", features=["orderbook"]) - # V3: Memory manager auto-starts with orderbook + # V3.1: Memory manager auto-starts with orderbook # Manual cleanup if needed - await orderbook.memory_manager.cleanup_old_data() + await suite.orderbook.memory_manager.cleanup_old_data() - # V3: Get comprehensive memory statistics - stats = await orderbook.memory_manager.get_memory_stats() + # V3.1: Get comprehensive memory statistics + stats = await suite.orderbook.memory_manager.get_memory_stats() print(f"Trades in memory: {stats['recent_trades_count']}") print(f"Bid levels: {stats['orderbook_bids_count']}") print(f"Ask levels: {stats['orderbook_asks_count']}") print(f"Memory usage: {stats['memory_usage_mb']:.1f} MB") - # V3: Configure memory limits - orderbook.memory_config.max_trades = 5000 - orderbook.memory_config.max_depth_entries = 200 - print(f"Recent trades: {memory_stats['recent_trades_count']}") - print( - f"Items cleaned: {memory_stats['trades_cleaned'] + memory_stats['depth_cleaned']}" - ) + # V3.1: Configure memory limits + suite.orderbook.memory_config.max_trades = 5000 + suite.orderbook.memory_config.max_depth_entries = 200 + + await suite.disconnect() ``` See Also: diff --git a/src/project_x_py/orderbook/profile.py b/src/project_x_py/orderbook/profile.py index f196c81..a5ef74e 100644 --- a/src/project_x_py/orderbook/profile.py +++ b/src/project_x_py/orderbook/profile.py @@ -17,26 +17,23 @@ Example Usage: ```python - # V3: Volume profiling with EventBus-enabled orderbook - from project_x_py import create_orderbook - from project_x_py.events import EventBus - - event_bus = EventBus() - orderbook = create_orderbook( - "MNQ", event_bus, project_x=client - ) # V3: actual symbol - await orderbook.initialize(realtime_client) - - # V3: Get volume profile with POC and value areas - vp = await orderbook.get_volume_profile(time_window_minutes=60) + # V3.1: Volume profiling with TradingSuite's orderbook + from project_x_py import TradingSuite + + suite = await TradingSuite.create("MNQ", features=["orderbook"]) + + # V3.1: Get volume profile with POC and value areas + vp = await suite.orderbook.get_volume_profile(time_window_minutes=60) print(f"POC: {vp['poc']:.2f}") print(f"Value Area: {vp['value_area_low']:.2f} - {vp['value_area_high']:.2f}") print(f"Volume at POC: {vp['poc_volume']} contracts") - # V3: Support/resistance levels - levels = await orderbook.get_support_resistance_levels() + # V3.1: Support/resistance levels + levels = await suite.orderbook.get_support_resistance_levels() for support in levels["support_levels"]: print(f"Support at {support['price']:.2f}: {support['strength']} touches") + + await suite.disconnect() ``` See Also: diff --git a/src/project_x_py/orderbook/realtime.py b/src/project_x_py/orderbook/realtime.py index 33d6a7a..3ca5270 100644 --- a/src/project_x_py/orderbook/realtime.py +++ b/src/project_x_py/orderbook/realtime.py @@ -17,32 +17,24 @@ Example Usage: ```python - # V3: Real-time handler with EventBus integration - from project_x_py import create_orderbook, create_realtime_client - from project_x_py.events import EventBus, EventType + # V3.1: Real-time handler integrated in TradingSuite + from project_x_py import TradingSuite, EventType - # V3: Create components with EventBus - event_bus = EventBus() - orderbook = create_orderbook( - "MNQ", event_bus, project_x=client - ) # V3: actual symbol + # V3.1: Create suite with orderbook feature + suite = await TradingSuite.create("MNQ", features=["orderbook"]) - # V3: Create realtime client with factory - realtime_client = await create_realtime_client( - jwt_token=client.jwt_token, account_id=str(client.account_id) - ) - # V3: Initialize orderbook with realtime (handler is internal) - await orderbook.initialize(realtime_client) - - - # V3: Real-time updates flow through EventBus - @event_bus.on(EventType.MARKET_DEPTH_UPDATE) - async def on_depth(data): + # V3.1: Real-time connection is automatically established + # Register handlers via suite's EventBus + @suite.events.on(EventType.MARKET_DEPTH_UPDATE) + async def on_depth(event): + data = event.data print(f"Depth: Best bid {data['bids'][0]['price']} @ {data['bids'][0]['size']}") - # Real-time updates are now handled automatically through EventBus + # Real-time updates flow automatically through the suite's EventBus + + await suite.disconnect() ``` See Also: diff --git a/src/project_x_py/position_manager/__init__.py b/src/project_x_py/position_manager/__init__.py index ed7622e..836c759 100644 --- a/src/project_x_py/position_manager/__init__.py +++ b/src/project_x_py/position_manager/__init__.py @@ -37,61 +37,54 @@ Example Usage: ```python - # V3: Comprehensive position management with EventBus integration + # V3.1: Comprehensive position management with TradingSuite import asyncio - from project_x_py import ( - ProjectX, - create_realtime_client, - create_position_manager, - EventBus, - ) + from project_x_py import TradingSuite async def main(): - async with ProjectX.from_env() as client: - await client.authenticate() - - # V3: Create dependencies - event_bus = EventBus() - realtime_client = await create_realtime_client( - client.get_session_token(), str(client.get_account_info().id) - ) - - # V3: Create position manager with dependency injection - pm = create_position_manager(client, realtime_client, event_bus) - await pm.initialize(realtime_client) - - # V3: Get current positions with detailed info - positions = await pm.get_all_positions() - for pos in positions: - print(f"Contract: {pos.contractId}") - print(f" Size: {pos.netPos}") - print(f" Avg Price: ${pos.buyAvgPrice:.2f}") - print(f" Unrealized P&L: ${pos.unrealizedPnl:.2f}") - - # V3: Calculate portfolio P&L with current market prices - market_prices = {"MGC": 2050.0, "MNQ": 18500.0} - pnl = await pm.calculate_portfolio_pnl(market_prices) - print(f"Total P&L: ${pnl['total_pnl']:.2f}") - print(f"Unrealized: ${pnl['unrealized_pnl']:.2f}") - print(f"Realized: ${pnl['realized_pnl']:.2f}") - - # V3: Risk analysis with comprehensive metrics - risk = await pm.get_risk_metrics() - print(f"Portfolio Risk: {risk['portfolio_risk']:.2%}") - print(f"Max Drawdown: ${risk['max_drawdown']:.2f}") - print(f"VaR (95%): ${risk['var_95']:.2f}") - - # V3: Position sizing with risk management - sizing = await pm.calculate_position_size( - "MGC", risk_amount=500.0, entry_price=2050.0, stop_price=2040.0 - ) - print(f"Suggested size: {sizing['suggested_size']} contracts") - print(f"Position risk: ${sizing['position_risk']:.2f}") - - # V3: Set up position monitoring with alerts - await pm.add_position_alert("MGC", max_loss=-500.0, min_profit=1000.0) - await pm.start_monitoring(interval_seconds=5) + # V3.1: Create suite with integrated position manager + suite = await TradingSuite.create("MNQ", timeframes=["1min"]) + + # V3.1: Get current positions with detailed info + positions = await suite.positions.get_all_positions() + for pos in positions: + print(f"Contract: {pos.contractId}") + print(f" Size: {pos.netPos}") + print(f" Avg Price: ${pos.buyAvgPrice:.2f}") + print(f" Unrealized P&L: ${pos.unrealizedPnl:.2f}") + + # V3.1: Calculate portfolio P&L with current market prices + current_price = await suite.data.get_current_price() + market_prices = {"MNQ": current_price, "ES": 4500.0} + pnl = await suite.positions.calculate_portfolio_pnl(market_prices) + print(f"Total P&L: ${pnl['total_pnl']:.2f}") + print(f"Unrealized: ${pnl['unrealized_pnl']:.2f}") + print(f"Realized: ${pnl['realized_pnl']:.2f}") + + # V3.1: Risk analysis with comprehensive metrics + risk = await suite.positions.get_risk_metrics() + print(f"Portfolio Risk: {risk['portfolio_risk']:.2%}") + print(f"Max Drawdown: ${risk['max_drawdown']:.2f}") + print(f"VaR (95%): ${risk['var_95']:.2f}") + + # V3.1: Position sizing with risk management + sizing = await suite.positions.calculate_position_size( + suite.instrument_id, + risk_amount=500.0, + entry_price=current_price, + stop_price=current_price - 10.0, + ) + print(f"Suggested size: {sizing['suggested_size']} contracts") + print(f"Position risk: ${sizing['position_risk']:.2f}") + + # V3.1: Set up position monitoring with alerts + await suite.positions.add_position_alert( + suite.instrument_id, max_loss=-500.0, min_profit=1000.0 + ) + await suite.positions.start_monitoring(interval_seconds=5) + + await suite.disconnect() asyncio.run(main()) diff --git a/src/project_x_py/position_manager/analytics.py b/src/project_x_py/position_manager/analytics.py index 8c1108b..27afa68 100644 --- a/src/project_x_py/position_manager/analytics.py +++ b/src/project_x_py/position_manager/analytics.py @@ -26,16 +26,18 @@ Example Usage: ```python - # Calculate individual position P&L - position = await position_manager.get_position("MGC") - pnl = await position_manager.calculate_position_pnl( - position, current_price=2050.0, point_value=10.0 + # V3.1: Calculate individual position P&L with TradingSuite + suite = await TradingSuite.create("MNQ", timeframes=["1min"]) + position = await suite.positions.get_position(suite.instrument_id) + current_price = await suite.data.get_current_price() + pnl = await suite.positions.calculate_position_pnl( + position, current_price=current_price, point_value=2.0 ) print(f"P&L: ${pnl['unrealized_pnl']:.2f}") - # Portfolio P&L with current market prices - prices = {"MGC": 2050.0, "NQ": 15500.0, "ES": 4400.0} - portfolio_pnl = await position_manager.calculate_portfolio_pnl(prices) + # V3.1: Portfolio P&L with current market prices + prices = {"MNQ": current_price, "ES": 4500.0, "NQ": 15500.0} + portfolio_pnl = await suite.positions.calculate_portfolio_pnl(prices) print(f"Total P&L: ${portfolio_pnl['total_pnl']:.2f}") ``` @@ -105,7 +107,7 @@ async def calculate_position_pnl( current_price (float | None): Current market price of the contract. If None, returns a graceful response with zero P&L and an error message. point_value (float, optional): Dollar value per point movement. - For futures, this is the contract multiplier (e.g., 10 for MGC). + For futures, this is the contract multiplier (e.g., 2 for MNQ). If None, P&L is returned in points rather than dollars. Defaults to None. @@ -121,15 +123,18 @@ async def calculate_position_pnl( - price_change (float): Favorable price movement amount Example: - >>> # Calculate P&L in points - >>> position = await position_manager.get_position("MGC") - >>> pnl = await position_manager.calculate_position_pnl(position, 2050.0) + >>> # V3.1: Calculate P&L in points with TradingSuite + >>> position = await suite.positions.get_position(suite.instrument_id) + >>> current_price = await suite.data.get_current_price() + >>> pnl = await suite.positions.calculate_position_pnl( + ... position, current_price + ... ) >>> print(f"Unrealized P&L: {pnl['unrealized_pnl']:.2f} points") - >>> # Calculate P&L in dollars with contract multiplier - >>> pnl = await position_manager.calculate_position_pnl( + >>> # V3.1: Calculate P&L in dollars with contract multiplier + >>> pnl = await suite.positions.calculate_position_pnl( ... position, - ... 2050.0, - ... point_value=10.0, # MGC = $10/point + ... current_price, + ... point_value=2.0, # MNQ = $2/point ... ) >>> print(f"Unrealized P&L: ${pnl['unrealized_pnl']:.2f}") >>> print(f"Per contract: ${pnl['pnl_per_contract']:.2f}") @@ -245,7 +250,7 @@ async def calculate_portfolio_pnl( Args: current_prices (dict[str, float]): Dictionary mapping contract IDs to - their current market prices. Example: {"MGC": 2050.0, "NQ": 15500.0} + their current market prices. Example: {"MNQ": 18500.0, "ES": 4500.0} account_id (int, optional): The account ID to calculate P&L for. If None, uses the default account from authentication. Defaults to None. @@ -263,9 +268,10 @@ async def calculate_portfolio_pnl( - last_updated (str): ISO timestamp Example: - >>> # Get current prices from market data - >>> prices = {"MGC": 2050.0, "NQ": 15500.0, "ES": 4400.0} - >>> portfolio = await position_manager.calculate_portfolio_pnl(prices) + >>> # V3.1: Get current prices from market data with TradingSuite + >>> current_price = await suite.data.get_current_price() + >>> prices = {"MNQ": current_price, "ES": 4500.0, "NQ": 15500.0} + >>> portfolio = await suite.positions.calculate_portfolio_pnl(prices) >>> print(f"Total P&L: ${portfolio['total_pnl']:.2f}") >>> print(f"Total Value: ${portfolio['total_value']:.2f}") >>> print(f"Win Rate: {portfolio['win_rate']:.1%}") diff --git a/src/project_x_py/position_manager/core.py b/src/project_x_py/position_manager/core.py index 7df3961..b8ac793 100644 --- a/src/project_x_py/position_manager/core.py +++ b/src/project_x_py/position_manager/core.py @@ -28,42 +28,34 @@ Example Usage: ```python - # V3: Initialize position manager with EventBus and real-time support + # V3.1: Initialize position manager with TradingSuite import asyncio - from project_x_py import ProjectX, create_realtime_client, EventBus - from project_x_py.position_manager import PositionManager + from project_x_py import TradingSuite async def main(): - async with ProjectX.from_env() as client: - await client.authenticate() + # V3.1: Create suite with integrated position manager + suite = await TradingSuite.create("MNQ", timeframes=["1min"]) - # V3: Create dependencies - event_bus = EventBus() - realtime_client = await create_realtime_client( - client.get_session_token(), str(client.get_account_info().id) - ) - - # V3: Initialize position manager - pm = PositionManager(client, event_bus) - await pm.initialize(realtime_client) + # V3.1: Get current positions with detailed fields + positions = await suite.positions.get_all_positions() + for pos in positions: + print(f"{pos.contractId}: {pos.netPos} @ ${pos.buyAvgPrice}") - # V3: Get current positions with detailed fields - positions = await pm.get_all_positions() - for pos in positions: - print(f"{pos.contractId}: {pos.netPos} @ ${pos.buyAvgPrice}") + # V3.1: Calculate P&L with market prices + current_price = await suite.data.get_current_price() + prices = {"MNQ": current_price, "ES": 4500.0} + pnl = await suite.positions.calculate_portfolio_pnl(prices) + print(f"Total P&L: ${pnl['total_pnl']:.2f}") - # V3: Calculate P&L with market prices - prices = {"MGC": 2050.0, "MNQ": 18500.0} - pnl = await pm.calculate_portfolio_pnl(prices) - print(f"Total P&L: ${pnl['total_pnl']:.2f}") + # V3.1: Risk analysis + risk = await suite.positions.get_risk_metrics() + print(f"Portfolio risk: {risk['portfolio_risk']:.2%}") - # V3: Risk analysis - risk = await pm.get_risk_metrics() - print(f"Portfolio risk: {risk['portfolio_risk']:.2%}") + # V3.1: Position operations + await suite.positions.close_position_direct(suite.instrument_id) - # V3: Position operations - await pm.close_position_direct("MGC") + await suite.disconnect() asyncio.run(main()) @@ -142,12 +134,9 @@ class PositionManager( - Diversification scoring and portfolio health metrics Example Usage: - >>> # V3: Create position manager with EventBus integration - >>> event_bus = EventBus() - >>> position_manager = PositionManager(project_x_client, event_bus) - >>> # V3: Initialize with real-time client for WebSocket updates - >>> realtime_client = await create_realtime_client( - ... client.get_session_token(), str(client.get_account_info().id) + >>> # V3.1: Create position manager with TradingSuite + >>> suite = await TradingSuite.create("MNQ", timeframes=["1min"]) + >>> # V3.1: Position manager is automatically initialized with real-time updates ... ) >>> await position_manager.initialize(realtime_client=realtime_client) >>> # V3: Get current positions with actual field names @@ -162,12 +151,18 @@ class PositionManager( ... market_prices ... ) >>> risk_metrics = await position_manager.get_risk_metrics() - >>> # V3: Position monitoring with alerts - >>> await position_manager.add_position_alert("MGC", max_loss=-500.0) - >>> await position_manager.start_monitoring(interval_seconds=5) - >>> # V3: Position sizing with risk management - >>> suggested_size = await position_manager.calculate_position_size( - ... "MGC", risk_amount=100.0, entry_price=2045.0, stop_price=2040.0 + >>> # V3.1: Position monitoring with alerts via TradingSuite + >>> await suite.positions.add_position_alert( + ... suite.instrument_id, max_loss=-500.0 + ... ) + >>> await suite.positions.start_monitoring(interval_seconds=5) + >>> # V3.1: Position sizing with risk management + >>> current_price = await suite.data.get_current_price() + >>> suggested_size = await suite.positions.calculate_position_size( + ... suite.instrument_id, + ... risk_amount=100.0, + ... entry_price=current_price, + ... stop_price=current_price - 5.0, ... ) """ @@ -205,17 +200,15 @@ def __init__( risk_settings (dict): Risk management configuration Example: - >>> # V3: Initialize with EventBus for unified event handling - >>> async with ProjectX.from_env() as client: - ... await client.authenticate() - ... event_bus = EventBus() - ... position_manager = PositionManager(client, event_bus) - ... - ... # V3: Optional - add order manager for synchronization - ... order_manager = OrderManager(client, event_bus) - ... await position_manager.initialize( - ... realtime_client=realtime_client, order_manager=order_manager - ... ) + >>> # V3.1: Initialize with TradingSuite for unified management + >>> suite = await TradingSuite.create("MNQ", timeframes=["1min"]) + >>> + >>> # V3.1: Position manager is automatically initialized + >>> # Access via suite.positions + >>> positions = await suite.positions.get_all_positions() + >>> + >>> # V3.1: Real-time and order sync are automatically configured + >>> # EventBus integration is handled by the suite """ # Initialize all mixins PositionTrackingMixin.__init__(self) @@ -337,18 +330,16 @@ async def initialize( Exception: Logged but not raised - returns False on failure Example: - >>> # V3: Initialize with real-time tracking - >>> rt_client = await create_realtime_client( - ... client.get_session_token(), str(client.get_account_info().id) - ... ) - >>> success = await position_manager.initialize(realtime_client=rt_client) + >>> # V3.1: Initialize with TradingSuite (automatic setup) + >>> suite = await TradingSuite.create("MNQ", timeframes=["1min"]) + >>> + >>> # V3.1: Position manager is automatically initialized with: + >>> # - Real-time tracking via WebSocket + >>> # - Order synchronization with suite.orders + >>> # - EventBus integration via suite.events >>> - >>> # V3: Initialize with both real-time and order sync - >>> event_bus = EventBus() - >>> order_mgr = OrderManager(client, event_bus) - >>> success = await position_manager.initialize( - ... realtime_client=rt_client, order_manager=order_mgr - ... ) + >>> # V3.1: Access the initialized position manager + >>> positions = await suite.positions.get_all_positions() Note: - Real-time mode provides instant position updates via WebSocket @@ -457,7 +448,7 @@ async def get_position( to an API call. Args: - contract_id (str): The contract ID to search for (e.g., "MGC", "NQ") + contract_id (str): The contract ID to search for (e.g., "MNQ", "ES") account_id (int, optional): The account ID to search within. If None, uses the default account from authentication. Defaults to None. @@ -468,16 +459,16 @@ async def get_position( exists for the contract. Example: - >>> # V3: Check if we have a Gold position - >>> mgc_position = await position_manager.get_position("MGC") - >>> if mgc_position: - ... print(f"MGC position: {mgc_position.netPos} contracts") - ... print(f"Buy Avg Price: ${mgc_position.buyAvgPrice:.2f}") - ... print(f"Sell Avg Price: ${mgc_position.sellAvgPrice:.2f}") - ... print(f"Unrealized P&L: ${mgc_position.unrealizedPnl:.2f}") - ... print(f"Realized P&L: ${mgc_position.realizedPnl:.2f}") + >>> # V3.1: Check if we have a position with TradingSuite + >>> position = await suite.positions.get_position(suite.instrument_id) + >>> if position: + ... print(f"{suite.instrument} position: {position.netPos} contracts") + ... print(f"Buy Avg Price: ${position.buyAvgPrice:.2f}") + ... print(f"Sell Avg Price: ${position.sellAvgPrice:.2f}") + ... print(f"Unrealized P&L: ${position.unrealizedPnl:.2f}") + ... print(f"Realized P&L: ${position.realizedPnl:.2f}") ... else: - ... print("No MGC position found") + ... print(f"No {suite.instrument} position found") Performance: - Real-time mode: O(1) cache lookup, falls back to API if miss @@ -552,7 +543,7 @@ async def is_position_open( specific contract without retrieving the full position details. Args: - contract_id (str): The contract ID to check (e.g., "MGC", "NQ") + contract_id (str): The contract ID to check (e.g., "MNQ", "ES") account_id (int, optional): The account ID to check within. If None, uses the default account from authentication. Defaults to None. @@ -561,12 +552,12 @@ async def is_position_open( bool: True if an open position exists (size != 0), False otherwise Example: - >>> # Check before placing an order - >>> if await position_manager.is_position_open("MGC"): - ... print("Already have MGC position") + >>> # V3.1: Check before placing an order with TradingSuite + >>> if await suite.positions.is_position_open(suite.instrument_id): + ... print(f"Already have {suite.instrument} position") ... else: ... # Safe to open new position - ... await order_manager.place_market_order("MGC", 0, 1) + ... await suite.orders.place_market_order(suite.instrument_id, 0, 1) Note: A position with size=0 is considered closed and returns False. diff --git a/src/project_x_py/position_manager/monitoring.py b/src/project_x_py/position_manager/monitoring.py index ee9220a..5d1afc3 100644 --- a/src/project_x_py/position_manager/monitoring.py +++ b/src/project_x_py/position_manager/monitoring.py @@ -26,20 +26,21 @@ Example Usage: ```python - # Add position alerts - await position_manager.add_position_alert("MGC", max_loss=-500.0) - await position_manager.add_position_alert("NQ", max_gain=1000.0) + # V3.1: Add position alerts with TradingSuite + await suite.positions.add_position_alert(suite.instrument_id, max_loss=-500.0) + await suite.positions.add_position_alert("ES", max_gain=1000.0) - # Start monitoring - await position_manager.start_monitoring(refresh_interval=30) + # V3.1: Start monitoring + await suite.positions.start_monitoring(refresh_interval=30) - # Register alert callbacks - async def on_alert(data): - print(f"Alert triggered: {data['message']}") + # V3.1: Register alert callbacks via EventBus + from project_x_py import EventType - await position_manager.add_callback("position_alert", on_alert) + @suite.events.on(EventType.POSITION_ALERT) + async def on_alert(event): + print(f"Alert triggered: {event.data['message']}") ``` See Also: @@ -101,10 +102,12 @@ async def add_position_alert( pnl_threshold: Absolute P&L change threshold Example: - >>> # Alert if MGC loses more than $500 - >>> await position_manager.add_position_alert("MGC", max_loss=-500.0) - >>> # Alert if NQ gains more than $1000 - >>> await position_manager.add_position_alert("NQ", max_gain=1000.0) + >>> # V3.1: Alert if position loses more than $500 + >>> await suite.positions.add_position_alert( + ... suite.instrument_id, max_loss=-500.0 + ... ) + >>> # V3.1: Alert if ES gains more than $1000 + >>> await suite.positions.add_position_alert("ES", max_gain=1000.0) """ async with self.position_lock: self.position_alerts[contract_id] = { @@ -125,7 +128,8 @@ async def remove_position_alert(self, contract_id: str) -> None: contract_id: Contract ID to remove alert for Example: - >>> await position_manager.remove_position_alert("MGC") + >>> # V3.1: Remove position alert with TradingSuite + >>> await suite.positions.remove_position_alert(suite.instrument_id) """ async with self.position_lock: if contract_id in self.position_alerts: diff --git a/src/project_x_py/position_manager/operations.py b/src/project_x_py/position_manager/operations.py index e3623b0..39ee077 100644 --- a/src/project_x_py/position_manager/operations.py +++ b/src/project_x_py/position_manager/operations.py @@ -25,20 +25,22 @@ Example Usage: ```python - # Close entire position - result = await position_manager.close_position_direct("MGC") + # V3.1: Close entire position with TradingSuite + result = await suite.positions.close_position_direct(suite.instrument_id) if result["success"]: print(f"Position closed: {result.get('orderId')}") - # Partial close for profit taking - result = await position_manager.partially_close_position("NQ", 5) + # V3.1: Partial close for profit taking + result = await suite.positions.partially_close_position("ES", 5) - # Bulk close all positions - result = await position_manager.close_all_positions() + # V3.1: Bulk close all positions + result = await suite.positions.close_all_positions() print(f"Closed {result['closed']}/{result['total_positions']} positions") - # Smart close with size detection - result = await position_manager.close_position_by_contract("MGC", close_size=3) + # V3.1: Smart close with size detection + result = await suite.positions.close_position_by_contract( + suite.instrument_id, close_size=3 + ) ``` See Also: @@ -85,7 +87,7 @@ async def close_position_direct( market price. This is the fastest way to exit a position completely. Args: - contract_id (str): Contract ID of the position to close (e.g., "MGC") + contract_id (str): Contract ID of the position to close (e.g., "MNQ") account_id (int, optional): Account ID holding the position. If None, uses the default account from authentication. Defaults to None. @@ -106,15 +108,17 @@ async def close_position_direct( - May trigger order synchronization if enabled Example: - >>> # Close entire Gold position - >>> result = await position_manager.close_position_direct("MGC") + >>> # V3.1: Close entire position with TradingSuite + >>> result = await suite.positions.close_position_direct( + ... suite.instrument_id + ... ) >>> if result["success"]: ... print(f"Position closed with order: {result.get('orderId')}") ... else: ... print(f"Failed: {result.get('errorMessage')}") - >>> # Close position in specific account - >>> result = await position_manager.close_position_direct( - ... "NQ", account_id=12345 + >>> # V3.1: Close position in specific account + >>> result = await suite.positions.close_position_direct( + ... "ES", account_id=12345 ... ) Note: @@ -224,13 +228,15 @@ async def partially_close_position( - May trigger order synchronization if enabled Example: - >>> # Take profit on half of a 10 contract position - >>> result = await position_manager.partially_close_position("MGC", 5) + >>> # V3.1: Take profit on half of a 10 contract position + >>> result = await suite.positions.partially_close_position( + ... suite.instrument_id, 5 + ... ) >>> if result["success"]: ... print(f"Partially closed with order: {result.get('orderId')}") - >>> # Scale out of position in steps + >>> # V3.1: Scale out of position in steps >>> for size in [3, 2, 1]: - ... result = await position_manager.partially_close_position("NQ", size) + ... result = await suite.positions.partially_close_position("ES", size) ... if not result["success"]: ... break ... await asyncio.sleep(60) # Wait between scales @@ -344,18 +350,20 @@ async def close_all_positions( - errors (list[str]): Error messages for failed closures Example: - >>> # Emergency close all positions - >>> result = await position_manager.close_all_positions() + >>> # V3.1: Emergency close all positions with TradingSuite + >>> result = await suite.positions.close_all_positions() >>> print( ... f"Closed {result['closed']}/{result['total_positions']} positions" ... ) >>> if result["errors"]: ... for error in result["errors"]: ... print(f"Error: {error}") - >>> # Close all Gold positions only - >>> result = await position_manager.close_all_positions(contract_id="MGC") - >>> # Close positions in specific account - >>> result = await position_manager.close_all_positions(account_id=12345) + >>> # V3.1: Close all MNQ positions only + >>> result = await suite.positions.close_all_positions( + ... contract_id=suite.instrument_id + ... ) + >>> # V3.1: Close positions in specific account + >>> result = await suite.positions.close_all_positions(account_id=12345) Warning: - Uses market orders - no price control @@ -421,7 +429,7 @@ async def close_position_by_contract( partial position closure based on the requested size. Args: - contract_id (str): Contract ID of position to close (e.g., "MGC") + contract_id (str): Contract ID of position to close (e.g., "MNQ") close_size (int, optional): Number of contracts to close. If None or >= position size, closes entire position. If less than position size, closes partially. @@ -438,11 +446,13 @@ async def close_position_by_contract( - error (str): Error details or "No open position found" Example: - >>> # Close entire position (auto-detect size) - >>> result = await position_manager.close_position_by_contract("MGC") - >>> # Close specific number of contracts - >>> result = await position_manager.close_position_by_contract( - ... "MGC", close_size=3 + >>> # V3.1: Close entire position (auto-detect size) + >>> result = await suite.positions.close_position_by_contract( + ... suite.instrument_id + ... ) + >>> # V3.1: Close specific number of contracts + >>> result = await suite.positions.close_position_by_contract( + ... suite.instrument_id, close_size=3 ... ) >>> # Smart scaling - close half of any position >>> position = await position_manager.get_position("NQ") diff --git a/src/project_x_py/position_manager/reporting.py b/src/project_x_py/position_manager/reporting.py index 6bdc564..378e48c 100644 --- a/src/project_x_py/position_manager/reporting.py +++ b/src/project_x_py/position_manager/reporting.py @@ -26,23 +26,23 @@ Example Usage: ```python - # Get system statistics - stats = position_manager.get_position_statistics() + # V3.1: Get system statistics with TradingSuite + stats = suite.positions.get_position_statistics() print(f"Health: {stats['health_status']}") print(f"Tracking {stats['tracked_positions']} positions") - # Get position history - history = await position_manager.get_position_history("MGC", limit=50) + # V3.1: Get position history + history = await suite.positions.get_position_history(suite.instrument_id, limit=50) for entry in history[-5:]: print(f"{entry['timestamp']}: Size {entry['position']['size']}") - # Generate comprehensive report - report = await position_manager.export_portfolio_report() + # V3.1: Generate comprehensive report + report = await suite.positions.export_portfolio_report() print(f"Portfolio Report - {report['report_timestamp']}") print(f"Positions: {report['portfolio_summary']['total_positions']}") - # Check real-time validation - status = position_manager.get_realtime_validation_status() + # V3.1: Check real-time validation + status = suite.positions.get_realtime_validation_status() print(f"Real-time enabled: {status['realtime_enabled']}") ``` @@ -179,7 +179,7 @@ async def get_position_history( timestamps, and position snapshots for analysis and debugging. Args: - contract_id (str): Contract ID to retrieve history for (e.g., "MGC") + contract_id (str): Contract ID to retrieve history for (e.g., "MNQ") limit (int, optional): Maximum number of history entries to return. Returns most recent entries if history exceeds limit. Defaults to 100. @@ -191,10 +191,12 @@ async def get_position_history( - size_change (int): Change in position size from previous Example: - >>> # Get recent history for Gold position - >>> history = await position_manager.get_position_history("MGC", limit=50) + >>> # V3.1: Get recent history with TradingSuite + >>> history = await suite.positions.get_position_history( + ... suite.instrument_id, limit=50 + ... ) >>> print(f"Found {len(history)} historical entries") - >>> # Analyze recent changes + >>> # V3.1: Analyze recent changes >>> for entry in history[-5:]: # Last 5 changes ... ts = entry["timestamp"].strftime("%H:%M:%S") ... size = entry["position"]["size"] diff --git a/src/project_x_py/position_manager/risk.py b/src/project_x_py/position_manager/risk.py index 878b694..41c95fd 100644 --- a/src/project_x_py/position_manager/risk.py +++ b/src/project_x_py/position_manager/risk.py @@ -26,19 +26,23 @@ Example Usage: ```python - # Analyze portfolio risk - risk_metrics = await position_manager.get_risk_metrics() + # V3.1: Analyze portfolio risk with TradingSuite + risk_metrics = await suite.positions.get_risk_metrics() print(f"Portfolio risk: {risk_metrics['portfolio_risk']:.2%}") print(f"Diversification: {risk_metrics['diversification_score']:.2f}") - # Check for risk warnings + # V3.1: Check for risk warnings if risk_metrics["risk_warnings"]: for warning in risk_metrics["risk_warnings"]: print(f"⚠️ {warning}") - # Calculate position size - sizing = await position_manager.calculate_position_size( - "MGC", risk_amount=500.0, entry_price=2050.0, stop_price=2040.0 + # V3.1: Calculate position size + current_price = await suite.data.get_current_price() + sizing = await suite.positions.calculate_position_size( + suite.instrument_id, + risk_amount=500.0, + entry_price=current_price, + stop_price=current_price - 10.0, ) print(f"Suggested size: {sizing['suggested_size']} contracts") print(f"Risk: ${sizing['total_risk']:.2f} ({sizing['risk_percentage']:.1f}%)") @@ -261,7 +265,7 @@ async def calculate_position_size( risk amount if the stop loss is hit. Args: - contract_id (str): Contract to size position for (e.g., "MGC") + contract_id (str): Contract to size position for (e.g., "MNQ") risk_amount (float): Maximum dollar amount to risk on the trade entry_price (float): Planned entry price for the position stop_price (float): Stop loss price for risk management @@ -284,9 +288,13 @@ async def calculate_position_size( - error (str): Error message if calculation fails Example: - >>> # Size position for $500 risk on Gold - >>> sizing = await position_manager.calculate_position_size( - ... "MGC", risk_amount=500.0, entry_price=2050.0, stop_price=2040.0 + >>> # V3.1: Size position for $500 risk with TradingSuite + >>> current_price = await suite.data.get_current_price() + >>> sizing = await suite.positions.calculate_position_size( + ... suite.instrument_id, + ... risk_amount=500.0, + ... entry_price=current_price, + ... stop_price=current_price - 10.0, ... ) >>> print(f"Trade {sizing['suggested_size']} contracts") >>> print( diff --git a/src/project_x_py/position_manager/tracking.py b/src/project_x_py/position_manager/tracking.py index 6081075..a5b20c9 100644 --- a/src/project_x_py/position_manager/tracking.py +++ b/src/project_x_py/position_manager/tracking.py @@ -47,8 +47,8 @@ async def on_position_closed(data): print(f"Position closed: {data.get('contractId')}") - # Get position history - # history = await position_manager.get_position_history("MGC", limit=10) + # V3.1: Get position history with TradingSuite + # history = await suite.positions.get_position_history(suite.instrument_id, limit=10) # for entry in history: # print(f"{entry['timestamp']}: {entry['size_change']:+d} contracts") ``` diff --git a/src/project_x_py/realtime/__init__.py b/src/project_x_py/realtime/__init__.py index 631f8ec..2b71f91 100644 --- a/src/project_x_py/realtime/__init__.py +++ b/src/project_x_py/realtime/__init__.py @@ -33,61 +33,65 @@ offering a simpler and more robust development experience. Example Usage: - The example below demonstrates the low-level usage of the `ProjectXRealtimeClient`. + For most applications, use TradingSuite which handles the real-time client automatically. + The example below shows low-level direct usage of `ProjectXRealtimeClient`. ```python - # V3: Real-time WebSocket client with async callbacks + # V3.1: TradingSuite manages real-time client automatically import asyncio - from project_x_py import ProjectX, create_realtime_client + from project_x_py import TradingSuite, EventType async def main(): - async with ProjectX.from_env() as client: - await client.authenticate() - - # V3: Create real-time client with factory function - realtime_client = await create_realtime_client( - jwt_token=client.get_session_token(), - account_id=str(client.get_account_info().id), - ) - - # V3: Register async callbacks for event handling - async def on_position_update(data): - print(f"Position update: {data}") - # V3: Position data includes actual fields - if "netPos" in data: - print(f" Net Position: {data['netPos']}") - print(f" Unrealized P&L: ${data.get('unrealizedPnl', 0):.2f}") - - async def on_quote_update(data): - # V3: Handle ProjectX quote format - if isinstance(data, dict) and "contractId" in data: - contract = data["contractId"] - bid = data.get("bid", 0) - ask = data.get("ask", 0) - print(f"{contract}: {bid} x {ask}") - - # V3: Add callbacks for various event types - await realtime_client.add_callback("position_update", on_position_update) - await realtime_client.add_callback("quote_update", on_quote_update) - - # V3: Connect and subscribe to data streams - if await realtime_client.connect(): - print(f"User Hub connected: {realtime_client.user_connected}") - print(f"Market Hub connected: {realtime_client.market_connected}") - - # V3: Subscribe to user events (positions, orders, trades) - await realtime_client.subscribe_user_updates() - - # V3: Subscribe to market data for specific contracts - await realtime_client.subscribe_market_data(["MGC", "MNQ"]) - - # V3: Process events for 60 seconds - await asyncio.sleep(60) - - # V3: Clean up connections - await realtime_client.disconnect() - + # V3.1: TradingSuite creates and manages real-time client internally + suite = await TradingSuite.create( + "MNQ", + timeframes=["1min", "5min"], + initial_days=1, + ) + + # V3.1: Register event handlers via suite's event bus + async def on_position_update(event): + data = event.data + print(f"Position update: {data}") + if "netPos" in data: + print(f" Net Position: {data['netPos']}") + print(f" Unrealized P&L: ${data.get('unrealizedPnl', 0):.2f}") + + async def on_quote_update(event): + data = event.data + if "bid" in data and "ask" in data: + print(f"{suite.instrument}: {data['bid']} x {data['ask']}") + + # V3.1: Add event handlers via suite's event bus + await suite.on(EventType.POSITION_UPDATE, on_position_update) + await suite.on(EventType.QUOTE, on_quote_update) + + # V3.1: Real-time connection and subscriptions are automatic + print(f"Connected to {suite.instrument} real-time feeds") + print(f"Account: {suite.client.account_info.name}") + + # V3.1: Process events for 60 seconds + await asyncio.sleep(60) + + # V3.1: Clean up is automatic with context manager + await suite.disconnect() + + + # V3.1: Low-level direct usage (advanced users only) + # from project_x_py import ProjectX + # from project_x_py.realtime import ProjectXRealtimeClient + # + # async def low_level_example(): + # async with ProjectX.from_env() as client: + # await client.authenticate() + # # Create real-time client directly + # realtime = ProjectXRealtimeClient( + # jwt_token=client.session_token, + # account_id=str(client.account_info.id), + # ) + # await realtime.connect() + # await realtime.subscribe_market_data(["MNQ", "ES"]) asyncio.run(main()) ``` diff --git a/src/project_x_py/realtime/connection_management.py b/src/project_x_py/realtime/connection_management.py index 713b7da..e78680c 100644 --- a/src/project_x_py/realtime/connection_management.py +++ b/src/project_x_py/realtime/connection_management.py @@ -242,14 +242,13 @@ async def connect(self: "ProjectXRealtimeClientProtocol") -> bool: 6. Updates connection statistics Example: - >>> client = ProjectXRealtimeClient(jwt_token, account_id) - >>> if await client.connect(): - ... print("Connected to ProjectX Gateway") - ... # Subscribe to updates - ... await client.subscribe_user_updates() - ... await client.subscribe_market_data(["MGC", "NQ"]) - ... else: - ... print("Connection failed") + >>> # V3.1: TradingSuite handles connection automatically + >>> suite = await TradingSuite.create("MNQ", timeframes=["1min"]) + >>> print(f"Connected: {suite.realtime_client.is_connected()}") + >>> # V3.1: Direct usage (advanced) + >>> # client = ProjectXRealtimeClient(jwt_token, account_id) + >>> # if await client.connect(): + >>> # await client.subscribe_market_data(["MNQ", "ES"]) Side Effects: - Sets self.user_connected and self.market_connected flags diff --git a/src/project_x_py/realtime/core.py b/src/project_x_py/realtime/core.py index 41fd3e4..3eb98a9 100644 --- a/src/project_x_py/realtime/core.py +++ b/src/project_x_py/realtime/core.py @@ -37,42 +37,48 @@ management of this client, its connections, and its events. Example Usage: - The example below shows how to use the client directly. In a real application, - the `position_manager`, `order_manager`, and `data_manager` would be instances - of the SDK's manager classes. + For most applications, use TradingSuite which manages the real-time client + automatically. The example below shows low-level direct usage. ```python - # V3: Create async client with ProjectX Gateway URLs + # V3.1: TradingSuite manages real-time client internally import asyncio - from project_x_py import create_realtime_client + from project_x_py import TradingSuite async def main(): - # V3: Use factory function for proper initialization - client = await create_realtime_client(jwt_token, account_id) - - # V3: Register async managers for event handling - # await client.add_callback("position_update", position_manager.handle_update) - # await client.add_callback("order_update", order_manager.handle_update) - # await client.add_callback("quote_update", data_manager.handle_quote) - - # V3: Connect and check both hub connections - if await client.connect(): - print(f"User Hub: {client.user_connected}") - print(f"Market Hub: {client.market_connected}") - - # V3: Subscribe to user and market data - await client.subscribe_user_updates() - await client.subscribe_market_data(["MGC", "MNQ"]) - - # V3: Process events - await asyncio.sleep(60) - - # V3: Clean disconnect - await client.disconnect() - - - # asyncio.run(main()) + # V3.1: TradingSuite creates and configures real-time client + suite = await TradingSuite.create( + "MNQ", + timeframes=["1min", "5min"], + initial_days=1, + ) + + # V3.1: Real-time client is accessible if needed + print(f"User Hub: {suite.realtime_client.user_connected}") + print(f"Market Hub: {suite.realtime_client.market_connected}") + + # V3.1: Subscriptions are handled automatically + # suite.realtime_client already subscribed to user updates + # and market data for the configured instrument + + # V3.1: Process events through suite's managers + await asyncio.sleep(60) + + # V3.1: Clean disconnect + await suite.disconnect() + + + # V3.1: Low-level direct usage (advanced users only) + # from project_x_py.realtime import ProjectXRealtimeClient + # realtime = ProjectXRealtimeClient( + # jwt_token=client.session_token, + # account_id=str(client.account_info.id), + # ) + # await realtime.connect() + # await realtime.subscribe_market_data(["MNQ", "ES"]) + + asyncio.run(main()) ``` Event Types (per ProjectX Gateway docs): @@ -145,25 +151,26 @@ class ProjectXRealtimeClient( - Event statistics and flow monitoring Example: - >>> # V3: Create async client with factory function - >>> client = await create_realtime_client(jwt_token, account_id) - >>> # V3: Register async callbacks for event handling - >>> async def handle_position(data): + >>> # V3.1: Use TradingSuite for automatic real-time management + >>> suite = await TradingSuite.create("MNQ", timeframes=["1min"]) + >>> # V3.1: Access real-time client if needed + >>> print(f"Connected: {suite.realtime_client.is_connected()}") + >>> + >>> # V3.1: Register callbacks via suite's event bus + >>> from project_x_py import EventType + >>> async def handle_position(event): + ... data = event.data ... print(f"Position: {data.get('contractId')} - {data.get('netPos')}") - >>> async def handle_order(data): - ... print(f"Order {data.get('id')}: {data.get('status')}") - >>> async def handle_quote(data): - ... print(f"Quote: {data.get('bid')} x {data.get('ask')}") - >>> await client.add_callback("position_update", handle_position) - >>> await client.add_callback("order_update", handle_order) - >>> await client.add_callback("quote_update", handle_quote) + >>> await suite.on(EventType.POSITION_UPDATE, handle_position) >>> - >>> # V3: Connect and subscribe with error handling - >>> if await client.connect(): - ... await client.subscribe_user_updates() - ... await client.subscribe_market_data(["MGC", "MNQ"]) - ... else: - ... print("Connection failed") + >>> # V3.1: Direct low-level usage (advanced) + >>> # from project_x_py.realtime import ProjectXRealtimeClient + >>> # realtime = ProjectXRealtimeClient( + >>> # jwt_token=client.session_token, + >>> # account_id=str(client.account_info.id), + >>> # ) + >>> # await realtime.connect() + >>> # await realtime.subscribe_market_data(["MNQ", "ES"]) Event Types (per ProjectX Gateway docs): User Hub: GatewayUserAccount, GatewayUserPosition, GatewayUserOrder, GatewayUserTrade diff --git a/src/project_x_py/realtime/event_handling.py b/src/project_x_py/realtime/event_handling.py index ae84ad2..fd33b27 100644 --- a/src/project_x_py/realtime/event_handling.py +++ b/src/project_x_py/realtime/event_handling.py @@ -470,8 +470,8 @@ async def _forward_event_async( - Triggers all registered callbacks Example Data Flow: - >>> # SignalR sends: ["MGC", {"bid": 2050, "ask": 2051}] - >>> # Callbacks receive: {"contract_id": "MGC", "data": {"bid": 2050, "ask": 2051}} + >>> # SignalR sends: ["MNQ", {"bid": 18500, "ask": 18501}] + >>> # Callbacks receive: {"contract_id": "MNQ", "data": {"bid": 18500, "ask": 18501}} Note: This method runs in the asyncio event loop, ensuring thread safety diff --git a/src/project_x_py/realtime/subscriptions.py b/src/project_x_py/realtime/subscriptions.py index 64ef29f..2b5d379 100644 --- a/src/project_x_py/realtime/subscriptions.py +++ b/src/project_x_py/realtime/subscriptions.py @@ -237,10 +237,10 @@ async def subscribe_market_data( Example: >>> # Subscribe to single contract - >>> await client.subscribe_market_data(["MGC"]) + >>> await client.subscribe_market_data(["MNQ"]) >>> >>> # Subscribe to multiple contracts - >>> contracts = ["MGC", "NQ", "ES", "YM"] + >>> contracts = ["MNQ", "ES", "NQ", "YM"] >>> if await client.subscribe_market_data(contracts): ... print(f"Subscribed to {len(contracts)} contracts") >>> # With data handling @@ -488,7 +488,7 @@ async def unsubscribe_market_data( Example: >>> # Unsubscribe specific contracts - >>> await client.unsubscribe_market_data(["MGC", "SI"]) + >>> await client.unsubscribe_market_data(["MNQ", "ES"]) >>> >>> # Dynamic subscription management >>> active_contracts = ["ES", "NQ", "YM", "RTY"] diff --git a/src/project_x_py/realtime_data_manager/__init__.py b/src/project_x_py/realtime_data_manager/__init__.py index 3baab2f..be726b0 100644 --- a/src/project_x_py/realtime_data_manager/__init__.py +++ b/src/project_x_py/realtime_data_manager/__init__.py @@ -36,58 +36,62 @@ Example Usage: ```python - # V3: Uses factory functions and EventBus integration - from project_x_py import ProjectX, EventBus - from project_x_py.realtime import create_realtime_client - from project_x_py.realtime_data_manager import RealtimeDataManager - - async with ProjectX.from_env() as client: - await client.authenticate() - - # V3: Create real-time client with factory - realtime_client = await create_realtime_client( - jwt_token=client.jwt_token, account_id=str(client.account_id) - ) - - # V3: Initialize with EventBus for unified events - event_bus = EventBus() - - # Create data manager for multiple timeframes - data_manager = RealtimeDataManager( - instrument="MNQ", # V3: Using actual contract symbols - project_x=client, - realtime_client=realtime_client, - timeframes=["1min", "5min", "15min", "1hr"], - timezone="America/Chicago", - event_bus=event_bus, # V3: EventBus integration - ) - - # Initialize with historical data - if await data_manager.initialize(initial_days=5): - # Start real-time feed - if await data_manager.start_realtime_feed(): - # V3: Register callbacks for new bars with actual field names - async def on_new_bar(data): - bar = data["data"] - print(f"New {data['timeframe']} bar:") - print(f" Open: {bar['open']}, High: {bar['high']}") - print(f" Low: {bar['low']}, Close: {bar['close']}") - print(f" Volume: {bar['volume']}") - - await data_manager.add_callback("new_bar", on_new_bar) - - # V3: Access real-time data with proper methods - current_price = await data_manager.get_current_price() - data_5m = await data_manager.get_data("5min", bars=100) - - # V3: Get memory stats for monitoring - stats = await data_manager.get_memory_stats() - print(f"Memory usage: {stats}") - - # Process data... - await asyncio.sleep(60) - - await data_manager.cleanup() + # V3.1: TradingSuite manages data manager automatically + import asyncio + from project_x_py import TradingSuite, EventType + + # V3.1: TradingSuite creates and manages all components + suite = await TradingSuite.create( + "MNQ", + timeframes=["1min", "5min", "15min", "1hr"], + initial_days=5, + ) + + # V3.1: Data manager is automatically initialized and connected + # Access it via suite.data + print(f"Data manager ready for {suite.instrument}") + + + # V3.1: Register callbacks via suite's event bus + async def on_new_bar(event): + bar = event.data["data"] + timeframe = event.data["timeframe"] + print(f"New {timeframe} bar:") + print(f" Open: {bar['open']}, High: {bar['high']}") + print(f" Low: {bar['low']}, Close: {bar['close']}") + print(f" Volume: {bar['volume']}") + + + await suite.on(EventType.NEW_BAR, on_new_bar) + + # V3.1: Access real-time data via suite.data + current_price = await suite.data.get_current_price() + data_5m = await suite.data.get_data("5min", bars=100) + + # V3.1: Monitor memory stats + stats = suite.data.get_memory_stats() + print(f"Memory usage: {stats}") + + # Process data... + await asyncio.sleep(60) + + # V3.1: Cleanup is automatic with context manager + await suite.disconnect() + + # V3.1: Low-level direct usage (advanced users only) + # from project_x_py import ProjectX + # from project_x_py.realtime_data_manager import RealtimeDataManager + # + # async with ProjectX.from_env() as client: + # await client.authenticate() + # # Create components manually + # data_manager = RealtimeDataManager( + # instrument="MNQ", + # project_x=client, + # realtime_client=realtime_client, + # timeframes=["1min", "5min"], + # event_bus=event_bus, + # ) ``` Supported Timeframes: diff --git a/src/project_x_py/realtime_data_manager/callbacks.py b/src/project_x_py/realtime_data_manager/callbacks.py index 6e173f2..2900b13 100644 --- a/src/project_x_py/realtime_data_manager/callbacks.py +++ b/src/project_x_py/realtime_data_manager/callbacks.py @@ -30,47 +30,52 @@ Example Usage: ```python - # V3: Using EventBus for unified event handling - from project_x_py import EventBus, EventType - - event_bus = EventBus() - - - # V3: Register callbacks through EventBus - @event_bus.on(EventType.NEW_BAR) - async def on_new_bar(data): - tf = data["timeframe"] - bar = data["data"] + # V3.1: TradingSuite provides integrated event handling + from project_x_py import TradingSuite, EventType + + # V3.1: Create suite with data manager and event bus + suite = await TradingSuite.create( + "MNQ", + timeframes=["1min", "5min", "15min"], + initial_days=5, + ) + + + # V3.1: Register callbacks through suite's event bus + @suite.events.on(EventType.NEW_BAR) + async def on_new_bar(event): + tf = event.data["timeframe"] + bar = event.data["data"] print( f"New {tf} bar: O={bar['open']}, H={bar['high']}, L={bar['low']}, C={bar['close']}" ) - # V3: Implement trading logic with actual field names + # V3.1: Implement trading logic with actual field names if tf == "5min" and bar["close"] > bar["open"]: # Bullish bar detected - print(f"Bullish 5min bar detected at {data['bar_time']}") + print(f"Bullish 5min bar detected at {event.data['bar_time']}") - # V3: Emit custom events for strategy - await event_bus.emit( + # V3.1: Emit custom events for strategy + await suite.events.emit( EventType.CUSTOM, {"event": "bullish_signal", "timeframe": tf, "price": bar["close"]}, ) - # V3: Register for tick updates - @event_bus.on(EventType.TICK_UPDATE) - async def on_tick(data): + # V3.1: Register for tick updates + @suite.events.on(EventType.TICK_UPDATE) + async def on_tick(event): # This is called on every tick - keep it lightweight! + data = event.data print(f"Price: {data['price']}, Volume: {data['volume']}") - # V3: Legacy callback registration (backward compatibility) - # Will be removed in V4 - async def legacy_callback(data): - print(f"Legacy: {data}") + # V3.1: Alternative registration method + async def bar_callback(event): + print(f"Bar update: {event.data}") - await data_manager.add_callback("new_bar", legacy_callback) + await suite.on(EventType.NEW_BAR, bar_callback) ``` Event Data Structures: diff --git a/src/project_x_py/realtime_data_manager/core.py b/src/project_x_py/realtime_data_manager/core.py index 0bf7df0..ec46b68 100644 --- a/src/project_x_py/realtime_data_manager/core.py +++ b/src/project_x_py/realtime_data_manager/core.py @@ -41,63 +41,58 @@ Example Usage: ```python - # V3: Create shared async realtime client with factory - from project_x_py import EventBus - from project_x_py.realtime import create_realtime_client + # V3.1: TradingSuite manages data manager automatically + from project_x_py import TradingSuite, EventType - async_realtime_client = await create_realtime_client( - jwt_token=client.jwt_token, account_id=str(client.account_id) - ) - await async_realtime_client.connect() - - # V3: Initialize with EventBus for unified event handling - event_bus = EventBus() - - # Initialize async data manager with dependency injection - manager = RealtimeDataManager( - instrument="MNQ", # V3: Using actual contract symbols - project_x=client, # V3: ProjectX client from context manager - realtime_client=async_realtime_client, + # V3.1: Create suite with integrated data manager + suite = await TradingSuite.create( + "MNQ", # Using E-mini NASDAQ futures timeframes=["1min", "5min", "15min", "1hr"], + initial_days=5, timezone="America/Chicago", # CME timezone - event_bus=event_bus, # V3: EventBus integration ) - # Load historical data for all timeframes - if await manager.initialize(initial_days=5): - print("Historical data loaded successfully") + # V3.1: Data manager is automatically initialized with historical data + print(f"Data manager ready for {suite.instrument}") - # Start real-time feed (registers callbacks with existing client) - if await manager.start_realtime_feed(): - print("Real-time OHLCV feed active") - - # V3: Register callback with actual field names - async def on_new_bar(data): - timeframe = data["timeframe"] - bar_data = data["data"] + # V3.1: Register callbacks via suite's event bus + @suite.events.on(EventType.NEW_BAR) + async def on_new_bar(event): + timeframe = event.data["timeframe"] + bar_data = event.data["data"] print(f"New {timeframe} bar:") print(f" Open: {bar_data['open']}, High: {bar_data['high']}") print(f" Low: {bar_data['low']}, Close: {bar_data['close']}") print(f" Volume: {bar_data['volume']}") - await manager.add_callback("new_bar", on_new_bar) - - # V3: Access multi-timeframe OHLCV data - data_5m = await manager.get_data("5min", bars=100) - data_15m = await manager.get_data("15min", bars=50) - mtf_data = await manager.get_mtf_data() # All timeframes at once + # V3.1: Access multi-timeframe OHLCV data via suite.data + data_5m = await suite.data.get_data("5min", bars=100) + data_15m = await suite.data.get_data("15min", bars=50) + mtf_data = await suite.data.get_mtf_data() # All timeframes at once - # Get current market price (latest tick or bar close) - current_price = await manager.get_current_price() + # V3.1: Get current market price + current_price = await suite.data.get_current_price() - # V3: Monitor memory and performance - stats = await manager.get_memory_stats() + # V3.1: Monitor memory and performance + stats = suite.data.get_memory_stats() print(f"Data points stored: {stats['total_data_points']}") - # When done, clean up resources - await manager.cleanup() + # V3.1: Cleanup is automatic + await suite.disconnect() + + # V3.1: Low-level direct usage (advanced users only) + # from project_x_py.realtime_data_manager import RealtimeDataManager + # manager = RealtimeDataManager( + # instrument="MNQ", + # project_x=client, + # realtime_client=realtime_client, + # timeframes=["1min", "5min"], + # event_bus=event_bus, + # ) + # await manager.initialize(initial_days=5) + # await manager.start_realtime_feed() ``` Supported Timeframes: @@ -207,7 +202,7 @@ class RealtimeDataManager( # Initialize async data manager with dependency injection manager = RealtimeDataManager( - instrument="MGC", # Mini Gold futures + instrument="MNQ", # E-mini NASDAQ futures project_x=async_project_x_client, # For historical data loading realtime_client=async_realtime_client, timeframes=["1min", "5min", "15min", "1hr"], @@ -270,7 +265,7 @@ def __init__( for live WebSocket market data. Args: - instrument: Trading instrument symbol (e.g., "MGC", "MNQ", "ES"). + instrument: Trading instrument symbol (e.g., "MNQ", "ES", "NQ"). This should be the base symbol, not a specific contract. project_x: ProjectXBase client instance for initial historical data loading. @@ -311,7 +306,7 @@ def __init__( # Create data manager with multiple timeframes for Gold mini futures data_manager = RealtimeDataManager( - instrument="MGC", # Gold mini futures + instrument="MNQ", # E-mini NASDAQ futures project_x=px_client, realtime_client=realtime_client, timeframes=["1min", "5min", "15min", "1hr"], diff --git a/src/project_x_py/realtime_data_manager/data_access.py b/src/project_x_py/realtime_data_manager/data_access.py index 1216e62..7bf3af7 100644 --- a/src/project_x_py/realtime_data_manager/data_access.py +++ b/src/project_x_py/realtime_data_manager/data_access.py @@ -27,14 +27,24 @@ Example Usage: ```python - # V3: Data access with Polars DataFrames - # Get the most recent 100 bars of 5-minute data - data_5m = await manager.get_data("5min", bars=100) + # V3.1: Data access via TradingSuite + from project_x_py import TradingSuite + from project_x_py.indicators import RSI, MACD + + # V3.1: Create suite with data manager + suite = await TradingSuite.create( + "MNQ", + timeframes=["1min", "5min", "15min"], + initial_days=5, + ) + + # V3.1: Get the most recent 100 bars of 5-minute data + data_5m = await suite.data.get_data("5min", bars=100) if data_5m is not None: print(f"Got {len(data_5m)} bars of 5-minute data") - # V3: Access actual OHLCV columns + # V3.1: Access actual OHLCV columns latest = data_5m.tail(1) print(f"Latest bar:") print(f" Open: {latest['open'][0]}") @@ -43,23 +53,21 @@ print(f" Close: {latest['close'][0]}") print(f" Volume: {latest['volume'][0]}") - # V3: Calculate indicators with Polars + # V3.1: Calculate indicators with Polars if len(data_5m) >= 20: sma_20 = data_5m["close"].tail(20).mean() print(f"20-bar SMA: {sma_20:.2f}") - # V3: Use pipe for indicator chaining - from project_x_py.indicators import RSI, MACD - + # V3.1: Use pipe for indicator chaining with_indicators = data_5m.pipe(RSI, period=14).pipe(MACD) - # V3: Get current market price - current_price = await manager.get_current_price() + # V3.1: Get current market price + current_price = await suite.data.get_current_price() if current_price is not None: - print(f"Current MNQ price: ${current_price:.2f}") + print(f"Current {suite.instrument} price: ${current_price:.2f}") - # V3: Get multi-timeframe data for analysis - mtf_data = await manager.get_mtf_data() + # V3.1: Get multi-timeframe data for analysis + mtf_data = await suite.data.get_mtf_data() for tf, data in mtf_data.items(): print(f"{tf}: {len(data)} bars available") if len(data) > 0: @@ -219,7 +227,7 @@ async def get_current_price(self) -> float | None: if current_price > threshold: # Place a sell order await order_manager.place_market_order( - contract_id="MGC", + contract_id="MNQ", side=1, # Sell size=1, ) diff --git a/src/project_x_py/realtime_data_manager/validation.py b/src/project_x_py/realtime_data_manager/validation.py index 88d3ff4..c1380fa 100644 --- a/src/project_x_py/realtime_data_manager/validation.py +++ b/src/project_x_py/realtime_data_manager/validation.py @@ -27,34 +27,34 @@ Example Usage: ```python - # V3: Validation with async patterns and actual field names - async with ProjectX.from_env() as client: - await client.authenticate() - - manager = RealtimeDataManager( - instrument="MNQ", # V3: Actual contract symbol - project_x=client, - realtime_client=realtime_client, - ) - - # V3: Check validation status asynchronously - status = await manager.get_realtime_validation_status() - print(f"Feed active: {status['is_running']}") - print(f"Contract ID: {status['contract_id']}") - print(f"Symbol: {status['symbol']}") - print(f"Ticks processed: {status['ticks_processed']}") - print(f"Quotes validated: {status['quotes_validated']}") - print(f"Trades validated: {status['trades_validated']}") - - # V3: Check ProjectX Gateway compliance - compliance = status["projectx_compliance"] - for check, result in compliance.items(): - status_icon = "βœ…" if result else "❌" - print(f"{status_icon} {check}: {result}") - - # V3: Monitor validation errors - if status.get("validation_errors", 0) > 0: - print(f"⚠️ Validation errors detected: {status['validation_errors']}") + # V3.1: Validation status via TradingSuite + from project_x_py import TradingSuite + + # V3.1: Create suite with integrated data manager + suite = await TradingSuite.create( + "MNQ", # E-mini NASDAQ futures + timeframes=["1min", "5min"], + initial_days=5, + ) + + # V3.1: Check validation status via suite.data + status = suite.data.get_realtime_validation_status() + print(f"Feed active: {status['is_running']}") + print(f"Contract ID: {status['contract_id']}") + print(f"Symbol: {status['symbol']}") + print(f"Ticks processed: {status['ticks_processed']}") + print(f"Quotes validated: {status['quotes_validated']}") + print(f"Trades validated: {status['trades_validated']}") + + # V3.1: Check ProjectX Gateway compliance + compliance = status["projectx_compliance"] + for check, result in compliance.items(): + status_icon = "βœ…" if result else "❌" + print(f"{status_icon} {check}: {result}") + + # V3.1: Monitor validation errors + if status.get("validation_errors", 0) > 0: + print(f"⚠️ Validation errors detected: {status['validation_errors']}") ``` Validation Process: @@ -241,7 +241,7 @@ def _symbol_matches_instrument( bool: True if symbol matches our instrument """ # Extract the base symbol from the full symbol ID - # Example: "F.US.EP" -> "EP", "F.US.MGC" -> "MGC" + # Example: "F.US.EP" -> "EP", "F.US.MNQ" -> "MNQ" if "." in symbol: parts = symbol.split(".") base_symbol = parts[-1] if parts else symbol diff --git a/src/project_x_py/trading_suite.py b/src/project_x_py/trading_suite.py index 65bda82..9285837 100644 --- a/src/project_x_py/trading_suite.py +++ b/src/project_x_py/trading_suite.py @@ -253,7 +253,7 @@ async def create( - Market data subscriptions Args: - instrument: Trading symbol (e.g., "MNQ", "MGC", "ES") + instrument: Trading symbol (e.g., "MNQ", "ES", "NQ") timeframes: Data timeframes (default: ["5min"]) features: Optional features to enable **kwargs: Additional configuration options