diff --git a/examples/01_basic_client_connection.py b/examples/01_basic_client_connection.py index 5f01b79..46a8a0e 100644 --- a/examples/01_basic_client_connection.py +++ b/examples/01_basic_client_connection.py @@ -6,7 +6,7 @@ This is the foundation for all other async examples. Usage: - Run with: uv run examples/async_01_basic_client_connection.py + Run with: uv run examples/01_basic_client_connection.py Or use test.sh which sets environment variables: ./test.sh Author: TexasCoding diff --git a/examples/02_order_management.py b/examples/02_order_management.py index 06d4017..4104b79 100644 --- a/examples/02_order_management.py +++ b/examples/02_order_management.py @@ -17,7 +17,7 @@ Usage: Run with: ./test.sh (sets environment variables) - Or: uv run examples/async_02_order_management.py + Or: uv run examples/02_order_management.py Author: TexasCoding Date: July 2025 @@ -32,6 +32,7 @@ create_realtime_client, setup_logging, ) +from project_x_py.models import Order async def wait_for_user_confirmation(message: str) -> bool: @@ -80,7 +81,11 @@ async def show_order_status(order_manager, order_id: int, description: str): else: # Fall back to API check for status print(f" Order {order_id} not in real-time cache, checking API...") - api_order = await order_manager.get_order_by_id(order_id) + api_order: Order | None = await order_manager.get_order_by_id(order_id) + if not isinstance(api_order, Order): + print(f" Order {order_id} not found in API either") + return + if api_order: status_map = {1: "Open", 2: "Filled", 3: "Cancelled", 4: "Partially Filled"} status = status_map.get(api_order.status, f"Unknown ({api_order.status})") diff --git a/examples/03_position_management.py b/examples/03_position_management.py index 7fb9a3e..365388c 100644 --- a/examples/03_position_management.py +++ b/examples/03_position_management.py @@ -13,7 +13,7 @@ Usage: Run with: ./test.sh (sets environment variables) - Or: uv run examples/async_03_position_management.py + Or: uv run examples/03_position_management.py Author: TexasCoding Date: July 2025 @@ -30,13 +30,14 @@ create_realtime_client, setup_logging, ) -from project_x_py.async_realtime_data_manager import AsyncRealtimeDataManager +from project_x_py.position_manager import PositionManager +from project_x_py.realtime_data_manager import RealtimeDataManager async def get_current_market_price( client: ProjectX, symbol="MNQ", - realtime_data_manager: AsyncRealtimeDataManager | None = None, + realtime_data_manager: RealtimeDataManager | None = None, ): """Get current market price with async fallback for closed markets.""" # Try to get real-time price first if available @@ -74,7 +75,7 @@ async def try_get_data(days, interval): return None -async def display_positions(position_manager): +async def display_positions(position_manager: PositionManager): """Display current positions with detailed information.""" print("\nšŸ“Š Current Positions:") print("-" * 80) @@ -88,29 +89,24 @@ async def display_positions(position_manager): # Get portfolio P&L concurrently with position display pnl_task = asyncio.create_task(position_manager.get_portfolio_pnl()) + portfolio_pnl = await pnl_task # Display each position - for symbol, position in positions.items(): - print(f"\n{symbol}:") - print(f" Quantity: {position.quantity}") + for position in positions: + print(f"\n{position.contractId}:") + print(f" Quantity: {position.size}") print(f" Average Price: ${position.averagePrice:.2f}") - print(f" Position Value: ${position.positionValue:.2f}") - print(f" Unrealized P&L: ${position.unrealizedPnl:.2f}") - - # Show percentage change - if position.averagePrice > 0: - pnl_pct = ( - position.unrealizedPnl / (position.quantity * position.averagePrice) - ) * 100 - print(f" P&L %: {pnl_pct:+.2f}%") + print(f" Position Value: ${position.averagePrice:.2f}") + print(f" Unrealized P&L: ${portfolio_pnl.get('unrealized_pnl', 0):.2f}") # Show portfolio totals - portfolio_pnl = await pnl_task print("\n" + "=" * 40) - print(f"Portfolio Total P&L: ${portfolio_pnl:.2f}") + print(f"Portfolio Total P&L: ${portfolio_pnl.get('net_pnl', 0):.2f}") print("=" * 40) -async def monitor_positions_realtime(position_manager, duration_seconds=30): +async def monitor_positions_realtime( + position_manager: PositionManager, duration_seconds: int = 30 +): """Monitor positions with real-time updates.""" print(f"\nšŸ”„ Monitoring positions for {duration_seconds} seconds...") diff --git a/examples/04_realtime_data.py b/examples/04_realtime_data.py index 2783b7e..7f7b6e1 100644 --- a/examples/04_realtime_data.py +++ b/examples/04_realtime_data.py @@ -14,7 +14,7 @@ Usage: Run with: ./test.sh (sets environment variables) - Or: uv run examples/async_04_realtime_data.py + Or: uv run examples/04_realtime_data.py Author: TexasCoding Date: July 2025 @@ -35,10 +35,10 @@ ) if TYPE_CHECKING: - from project_x_py.async_realtime_data_manager import AsyncRealtimeDataManager + from project_x_py.realtime_data_manager import RealtimeDataManager -async def display_current_prices(data_manager: AsyncRealtimeDataManager): +async def display_current_prices(data_manager: RealtimeDataManager): """Display current prices across all timeframes asynchronously.""" print("\nšŸ“Š Current Prices:") diff --git a/examples/05_orderbook_analysis.py b/examples/05_orderbook_analysis.py index 8b5e37a..c49c765 100644 --- a/examples/05_orderbook_analysis.py +++ b/examples/05_orderbook_analysis.py @@ -11,11 +11,11 @@ - Market imbalance monitoring - Best bid/ask tracking -Uses MNQ for Level 2 orderbook data with AsyncOrderBook. +Uses MNQ for Level 2 orderbook data with OrderBook. Usage: Run with: ./test.sh (sets environment variables) - Or: uv run examples/async_05_orderbook_analysis.py + Or: uv run examples/05_orderbook_analysis.py Note: This example includes several wait periods: - 5 seconds for initial data population @@ -38,6 +38,7 @@ create_realtime_client, setup_logging, ) +from project_x_py.orderbook import OrderBook async def display_best_prices(orderbook): @@ -259,9 +260,9 @@ async def monitor_orderbook_feed(orderbook, duration_seconds=60): print(f" Update Cycles: {update_count}", flush=True) -async def demonstrate_all_orderbook_methods(orderbook): - """Comprehensive demonstration of all AsyncOrderBook methods.""" - print("\nšŸ” Testing all available AsyncOrderBook methods...", flush=True) +async def demonstrate_all_orderbook_methods(orderbook: OrderBook): + """Comprehensive demonstration of all OrderBook methods.""" + print("\nšŸ” Testing all available OrderBook methods...", flush=True) print( "šŸ“ Note: Some methods may show zero values without live market data connection" ) diff --git a/examples/06_multi_timeframe_strategy.py b/examples/06_multi_timeframe_strategy.py index 7f08e0b..a6fcb89 100644 --- a/examples/06_multi_timeframe_strategy.py +++ b/examples/06_multi_timeframe_strategy.py @@ -15,7 +15,7 @@ Usage: Run with: ./test.sh (sets environment variables) - Or: uv run examples/async_06_multi_timeframe_strategy.py + Or: uv run examples/06_multi_timeframe_strategy.py Author: TexasCoding Date: July 2025 @@ -31,9 +31,10 @@ create_trading_suite, ) from project_x_py.indicators import RSI, SMA +from project_x_py.models import BracketOrderResponse, Position -class AsyncMultiTimeframeStrategy: +class MultiTimeframeStrategy: """ Async multi-timeframe trend following strategy. @@ -47,11 +48,13 @@ class AsyncMultiTimeframeStrategy: def __init__( self, + client: ProjectX, trading_suite: dict, symbol: str = "MNQ", max_position_size: int = 2, risk_percentage: float = 0.02, ): + self.client = client self.suite = trading_suite self.symbol = symbol self.max_position_size = max_position_size @@ -245,19 +248,20 @@ async def generate_trading_signal(self): async def execute_signal(self, signal_data: dict): """Execute trading signal with proper risk management.""" # Check current position - positions = await self.position_manager.get_all_positions() - current_position = positions.get(self.symbol) + positions: list[Position] = await self.position_manager.get_all_positions() + current_position = next( + (pos for pos in positions if pos.contractId == self.symbol), None + ) # Position size limits - if ( - current_position - and abs(current_position.quantity) >= self.max_position_size - ): + if current_position and abs(current_position.size) >= self.max_position_size: self.logger.info("Max position size reached, skipping signal") return # Get account info for position sizing - account_balance = float(self.order_manager.project_x.account_info.balance) + account_balance = ( + float(self.client.account_info.balance) if self.client.account_info else 0 + ) # Calculate position size based on risk entry_price = signal_data["price"] @@ -285,11 +289,11 @@ async def execute_signal(self, signal_data: dict): return # Get active contract - instruments = await self.order_manager.project_x.search_instruments(self.symbol) + instruments = await self.client.search_instruments(self.symbol) if not instruments: return - contract_id = instruments[0].activeContract + contract_id = instruments[0].id # Place bracket order self.logger.info( @@ -313,8 +317,14 @@ async def execute_signal(self, signal_data: dict): take_profit_price=take_profit, ) + if not isinstance(response, BracketOrderResponse): + self.logger.error(f"āŒ Unexpected order type: {type(response)}") + return + if response and response.success: - self.logger.info(f"āœ… Order placed successfully: {response.orderId}") + self.logger.info( + f"āœ… Order placed successfully: {response.entry_order_id}" + ) else: self.logger.error("āŒ Order placement failed") @@ -433,7 +443,8 @@ def signal_handler(signum, frame): await suite["data_manager"].start_realtime_feed() # Create and configure strategy - strategy = AsyncMultiTimeframeStrategy( + strategy = MultiTimeframeStrategy( + client=client, trading_suite=suite, symbol="MNQ", max_position_size=2, diff --git a/examples/07_technical_indicators.py b/examples/07_technical_indicators.py index 4852224..ac3ce2b 100644 --- a/examples/07_technical_indicators.py +++ b/examples/07_technical_indicators.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Async Technical Indicators Analysis Example +Technical Indicators Analysis Example Demonstrates concurrent technical analysis using async patterns: - Concurrent calculation of multiple indicators @@ -12,7 +12,7 @@ Usage: Run with: ./test.sh (sets environment variables) - Or: uv run examples/async_07_technical_indicators.py + Or: uv run examples/07_technical_indicators.py Author: TexasCoding Date: July 2025 @@ -179,7 +179,7 @@ async def on_data_update(timeframe): update_count += 1 # Get latest data - data = await data_manager.get_data(timeframe) + data: pl.DataFrame | None = await data_manager.get_data(timeframe) if data is None: return @@ -189,8 +189,7 @@ async def on_data_update(timeframe): return # Calculate key indicators - data = data.pipe(RSI, period=14) - data = data.pipe(SMA, period=20) + data = data.pipe(RSI, period=14).pipe(SMA, period=20) last_row = data.tail(1) timestamp = datetime.now().strftime("%H:%M:%S") diff --git a/examples/08_order_and_position_tracking.py b/examples/08_order_and_position_tracking.py index d1ab15f..7516066 100644 --- a/examples/08_order_and_position_tracking.py +++ b/examples/08_order_and_position_tracking.py @@ -1,23 +1,23 @@ #!/usr/bin/env python3 """ -Async Order and Position Tracking Demo +Order and Position Tracking Demo This demo script demonstrates the automatic order cleanup functionality when positions are closed, -using proper async components (AsyncOrderManager, AsyncPositionManager, AsyncRealtimeDataManager). +using proper components (OrderManager, PositionManager, RealtimeDataManager). It creates a bracket order and monitors positions and orders in real-time, showing how the system automatically cancels remaining orders when a position is closed (either by stop loss, take profit, or manual closure from the broker). Features demonstrated: -- Proper async components for all operations +- Proper components for all operations - Automatic order cleanup when positions close - Non-blocking real-time monitoring with clear status updates -- Proper async cleanup on exit (cancels open orders and closes positions) +- Proper cleanup on exit (cancels open orders and closes positions) - Concurrent operations for improved performance Usage: - python examples/async_08_order_and_position_tracking.py + python examples/08_order_and_position_tracking.py Manual Testing: - Let the script create a bracket order @@ -35,10 +35,11 @@ from datetime import datetime from project_x_py import ProjectX, create_trading_suite +from project_x_py.models import BracketOrderResponse, Order, Position -class AsyncOrderPositionDemo: - """Async demo class for order and position tracking with automatic cleanup.""" +class OrderPositionDemo: + """Demo class for order and position tracking with automatic cleanup.""" def __init__(self): self.client = None @@ -113,6 +114,10 @@ async def create_demo_bracket_order(self) -> bool: account_id=account_info.id, ) + if not isinstance(bracket_response, BracketOrderResponse): + print(f"āŒ Unexpected bracket order type: {type(bracket_response)}") + return False + if bracket_response and bracket_response.success: print("āœ… Bracket order created successfully!") print(f" Entry Order ID: {bracket_response.entry_order_id}") @@ -171,6 +176,8 @@ async def display_status(self): if positions: print("\nšŸ¦ Position Details:") for pos in positions: + if not isinstance(pos, Position): + continue direction = ( "LONG" if pos.type == 1 @@ -200,6 +207,9 @@ async def display_status(self): if orders: print("\nšŸ“ Order Details:") for order in orders: + if not isinstance(order, Order): + print(f" āŒ Unexpected order type: {type(order)}") + continue order_type = "UNKNOWN" if hasattr(order, "type"): order_type = order.type @@ -303,13 +313,20 @@ async def cleanup_all_positions_and_orders(self): print(f"šŸ“‹ Cancelling {len(orders)} open orders...") cancel_tasks = [] for order in orders: + if not isinstance(order, Order): + continue cancel_tasks.append( self.suite["order_manager"].cancel_order(order.id) ) # Wait for all cancellations to complete - results = await asyncio.gather(*cancel_tasks, return_exceptions=True) - for order, result in zip(orders, results, strict=False): + cancel_results: list[Order | BaseException] = await asyncio.gather( + *cancel_tasks, return_exceptions=True + ) + for order, result in zip(orders, cancel_results, strict=False): + if not isinstance(order, Order): + print(f" āŒ Unexpected order type: {type(order)}") + continue if isinstance(result, Exception): print(f" āŒ Error cancelling order {order.id}: {result}") elif result: @@ -318,7 +335,9 @@ async def cleanup_all_positions_and_orders(self): print(f" āš ļø Failed to cancel order {order.id}") # Close all open positions - positions = await self.suite["position_manager"].get_all_positions() + positions: list[Position] = await self.suite[ + "position_manager" + ].get_all_positions() if positions: print(f"šŸ¦ Closing {len(positions)} open positions...") close_tasks = [] @@ -330,7 +349,9 @@ async def cleanup_all_positions_and_orders(self): ) # Wait for all positions to close - results = await asyncio.gather(*close_tasks, return_exceptions=True) + results: list[Position | BaseException] = await asyncio.gather( + *close_tasks, return_exceptions=True + ) for position, result in zip(positions, results, strict=False): if isinstance(result, Exception): print( @@ -355,7 +376,7 @@ async def cleanup_all_positions_and_orders(self): print(f"āŒ Error during cleanup: {e}") async def run(self, client: ProjectX): - """Main async demo execution.""" + """Main demo execution.""" self.setup_signal_handlers() self.client = client @@ -376,9 +397,9 @@ async def run(self, client: ProjectX): print(f"āŒ Failed to authenticate: {e}") return False - # Create async trading suite + # Create trading suite try: - print("\nšŸ”§ Setting up async trading suite...") + print("\nšŸ”§ Setting up trading suite...") jwt_token = self.client.session_token self.suite = await create_trading_suite( instrument="MNQ", @@ -388,10 +409,10 @@ async def run(self, client: ProjectX): timeframes=["5min"], # Minimal timeframes for demo ) - print("āœ… Async trading suite created with automatic order cleanup enabled") + print("āœ… Trading suite created with automatic order cleanup enabled") except Exception as e: - print(f"āŒ Failed to create async trading suite: {e}") + print(f"āŒ Failed to create trading suite: {e}") return False # Connect real-time client and initialize data feed @@ -442,8 +463,8 @@ async def run(self, client: ProjectX): async def main(): - """Main async entry point.""" - demo = AsyncOrderPositionDemo() + """Main entry point.""" + demo = OrderPositionDemo() try: async with ProjectX.from_env() as client: success = await demo.run(client) diff --git a/examples/09_get_check_available_instruments.py b/examples/09_get_check_available_instruments.py index 4dc5035..7dd2d0d 100755 --- a/examples/09_get_check_available_instruments.py +++ b/examples/09_get_check_available_instruments.py @@ -1,19 +1,19 @@ #!/usr/bin/env python3 """ -Async Interactive Instrument Search Demo for ProjectX +Interactive Instrument Search Demo for ProjectX -This async version demonstrates: +This version demonstrates: - Using ProjectX client with async context manager - Async instrument search with await client.search_instruments() - Async best match selection with await client.get_instrument() - Non-blocking user input handling - Background performance stats monitoring -- Proper async authentication flow +- Proper authentication flow Key differences from sync version: - Uses ProjectX instead of ProjectX - All API calls use await (search_instruments, get_instrument) -- Async context manager (async with) +- Context manager (with) - Can run background tasks while accepting user input """ @@ -108,13 +108,13 @@ def show_common_symbols(): async def get_user_input(prompt): - """Get user input asynchronously""" + """Get user input""" loop = asyncio.get_event_loop() return await loop.run_in_executor(None, input, prompt) async def run_interactive_search(client): - """Run the interactive search loop""" + """Run the interactive search loop.""" show_common_symbols() print("\nHow the search works:") @@ -145,7 +145,7 @@ async def run_interactive_search(client): async def main(): - """Main async entry point""" + """Main entry point.""" print("╔═══════════════════════════════════════════════════════╗") print("ā•‘ Async ProjectX Instrument Search Interactive Demo ā•‘") print("ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•") diff --git a/examples/basic_usage.py b/examples/basic_usage.py index 03d491c..d5167c0 100644 --- a/examples/basic_usage.py +++ b/examples/basic_usage.py @@ -1,7 +1,7 @@ """ -Basic async usage example for the ProjectX Python SDK v2.0.0 +Basic usage example for the ProjectX Python SDK v2.0.0 -This example demonstrates the new async/await patterns introduced in v2.0.0. +This example demonstrates the new patterns introduced in v2.0.0. """ import asyncio @@ -12,7 +12,7 @@ async def main(): - """Main async function demonstrating basic SDK usage.""" + """Main function demonstrating basic SDK usage.""" # Method 1: Using environment variables (recommended) # Set these environment variables: @@ -23,7 +23,7 @@ async def main(): print("=" * 50) try: - # Create async client using environment variables + # Create client using environment variables async with ProjectX.from_env() as client: print("āœ… Client created successfully") if client.account_info is None: diff --git a/examples/factory_functions_demo.py b/examples/factory_functions_demo.py index 0f401f8..941e7c5 100644 --- a/examples/factory_functions_demo.py +++ b/examples/factory_functions_demo.py @@ -1,8 +1,8 @@ """ -Example demonstrating the async factory functions for creating trading components. +Example demonstrating the factory functions for creating trading components. This example shows how to use the convenient factory functions to create -async trading components with minimal boilerplate code. +trading components with minimal boilerplate code. """ import asyncio @@ -19,7 +19,7 @@ async def simple_component_creation(): - """Demonstrate creating individual async components.""" + """Demonstrate creating individual components.""" print("=" * 60) print("SIMPLE COMPONENT CREATION") print("=" * 60) diff --git a/examples/integrated_trading_suite.py b/examples/integrated_trading_suite.py index a5d59f0..2eb9bb5 100644 --- a/examples/integrated_trading_suite.py +++ b/examples/integrated_trading_suite.py @@ -1,7 +1,7 @@ """ -Example demonstrating integrated async trading suite with shared ProjectXRealtimeClient. +Example demonstrating integrated trading suite with shared ProjectXRealtimeClient. -This example shows how multiple async managers can share a single real-time WebSocket +This example shows how multiple managers can share a single real-time WebSocket connection, ensuring efficient resource usage and coordinated event handling. """ diff --git a/examples/order_manager_usage.py b/examples/order_manager_usage.py index e2646a4..0d7ff55 100644 --- a/examples/order_manager_usage.py +++ b/examples/order_manager_usage.py @@ -1,7 +1,7 @@ """ -Example demonstrating AsyncOrderManager usage for order operations. +Example demonstrating OrderManager usage for order operations. -This example shows how to use the AsyncOrderManager for placing orders, +This example shows how to use the OrderManager for placing orders, managing brackets, and handling order modifications with async/await. """ diff --git a/examples/orderbook_usage.py b/examples/orderbook_usage.py index a15d9a3..d24d6dd 100644 --- a/examples/orderbook_usage.py +++ b/examples/orderbook_usage.py @@ -1,7 +1,7 @@ """ -Example demonstrating AsyncOrderBook usage for market depth analysis. +Example demonstrating OrderBook usage for market depth analysis. -This example shows how to use the AsyncOrderBook for: +This example shows how to use the OrderBook for: - Real-time Level 2 market depth processing - Trade flow analysis - Iceberg order detection @@ -45,7 +45,7 @@ async def main(): # Initialize with real-time capabilities if await orderbook.initialize(realtime_client): - print("āœ… AsyncOrderBook initialized with real-time data") + print("āœ… OrderBook initialized with real-time data") else: print("āŒ Failed to initialize orderbook") return @@ -168,7 +168,7 @@ async def main(): # Clean up await orderbook.cleanup() - print("\nāœ… AsyncOrderBook cleanup completed") + print("\nāœ… OrderBook cleanup completed") if __name__ == "__main__": diff --git a/examples/position_manager_usage.py b/examples/position_manager_usage.py index afe4b64..fff367a 100644 --- a/examples/position_manager_usage.py +++ b/examples/position_manager_usage.py @@ -1,7 +1,7 @@ """ -Example demonstrating AsyncPositionManager usage for position operations. +Example demonstrating PositionManager usage for position operations. -This example shows how to use the AsyncPositionManager for tracking positions, +This example shows how to use the PositionManager for tracking positions, calculating P&L, managing risk, and handling position monitoring with async/await. """ diff --git a/examples/realtime_data_manager_usage.py b/examples/realtime_data_manager_usage.py index 48be7c4..4f32777 100644 --- a/examples/realtime_data_manager_usage.py +++ b/examples/realtime_data_manager_usage.py @@ -1,7 +1,7 @@ """ -Example demonstrating AsyncRealtimeDataManager usage for real-time OHLCV data. +Example demonstrating RealtimeDataManager usage for real-time OHLCV data. -This example shows how to use the AsyncRealtimeDataManager for managing +This example shows how to use the RealtimeDataManager for managing multi-timeframe OHLCV data with real-time updates via WebSocket. """ diff --git a/src/project_x_py/__init__.py b/src/project_x_py/__init__.py index e6e67f2..a78fb9b 100644 --- a/src/project_x_py/__init__.py +++ b/src/project_x_py/__init__.py @@ -27,15 +27,7 @@ __author__ = "TexasCoding" # Core client classes - renamed from Async* to standard names -from .async_client import AsyncProjectX as ProjectX -from .async_order_manager import AsyncOrderManager as OrderManager -from .async_orderbook import ( - AsyncOrderBook as OrderBook, - create_async_orderbook as create_orderbook, -) -from .async_position_manager import AsyncPositionManager as PositionManager -from .async_realtime import AsyncProjectXRealtimeClient as ProjectXRealtimeClient -from .async_realtime_data_manager import AsyncRealtimeDataManager as RealtimeDataManager +from .client import ProjectX # Configuration management from .config import ( @@ -88,6 +80,14 @@ ProjectXConfig, Trade, ) +from .order_manager import OrderManager +from .orderbook import ( + OrderBook, + create_orderbook, +) +from .position_manager import PositionManager +from .realtime import ProjectXRealtimeClient as ProjectXRealtimeClient +from .realtime_data_manager import RealtimeDataManager # Utility functions from .utils import ( diff --git a/src/project_x_py/async_client.py b/src/project_x_py/client.py similarity index 98% rename from src/project_x_py/async_client.py rename to src/project_x_py/client.py index 6cd303d..9ce827b 100644 --- a/src/project_x_py/async_client.py +++ b/src/project_x_py/client.py @@ -58,7 +58,7 @@ ) -class AsyncRateLimiter: +class RateLimiter: """Simple async rate limiter using sliding window.""" def __init__(self, max_requests: int, window_seconds: int): @@ -90,7 +90,7 @@ async def acquire(self) -> None: self.requests.append(now) -class AsyncProjectX: +class ProjectX: """ Async core ProjectX client for the ProjectX Python SDK. @@ -127,10 +127,10 @@ class AsyncProjectX: Example: >>> # Basic async SDK usage with environment variables (recommended) >>> import asyncio - >>> from project_x_py import AsyncProjectX + >>> from project_x_py import ProjectX >>> >>> async def main(): - >>> async with AsyncProjectX.from_env() as client: + >>> async with ProjectX.from_env() as client: >>> await client.authenticate() >>> positions = await client.get_positions() >>> print(f"Found {len(positions)} positions") @@ -191,11 +191,11 @@ def __init__( self.cache_hit_count = 0 # Rate limiting - 100 requests per minute by default - self.rate_limiter = AsyncRateLimiter(max_requests=100, window_seconds=60) + self.rate_limiter = RateLimiter(max_requests=100, window_seconds=60) self.logger = logging.getLogger(__name__) - async def __aenter__(self) -> "AsyncProjectX": + async def __aenter__(self) -> "ProjectX": """Async context manager entry.""" self._client = await self._create_client() return self @@ -210,7 +210,7 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): @asynccontextmanager async def from_env( cls, config: ProjectXConfig | None = None, account_name: str | None = None - ) -> AsyncGenerator["AsyncProjectX", None]: + ) -> AsyncGenerator["ProjectX", None]: """ Create async ProjectX client using environment variables (recommended approach). @@ -229,7 +229,7 @@ async def from_env( account_name: Optional account name (overrides environment variable) Yields: - AsyncProjectX: Configured async client instance ready for building trading applications + ProjectX: Configured async client instance ready for building trading applications Raises: ValueError: If required environment variables are not set @@ -245,10 +245,10 @@ async def from_env( >>> >>> # Create async client (recommended approach) >>> import asyncio - >>> from project_x_py import AsyncProjectX + >>> from project_x_py import ProjectX >>> >>> async def main(): - >>> async with AsyncProjectX.from_env() as client: + >>> async with ProjectX.from_env() as client: >>> await client.authenticate() >>> # Use the client... >>> @@ -275,7 +275,7 @@ async def from_env( @asynccontextmanager async def from_config_file( cls, config_file: str, account_name: str | None = None - ) -> AsyncGenerator["AsyncProjectX", None]: + ) -> AsyncGenerator["ProjectX", None]: """Create async ProjectX client using a configuration file. Alternative initialization method that loads configuration and credentials @@ -294,7 +294,7 @@ async def from_config_file( specified in the config file. Yields: - AsyncProjectX: Configured client instance ready for trading operations + ProjectX: Configured client instance ready for trading operations Raises: FileNotFoundError: If config file doesn't exist @@ -312,7 +312,7 @@ async def from_config_file( ... } >>> >>> # Use client with config file - >>> async with AsyncProjectX.from_config_file("config.json") as client: + >>> async with ProjectX.from_config_file("config.json") as client: ... await client.authenticate() ... # Client is ready for trading diff --git a/src/project_x_py/lock_coordinator.py b/src/project_x_py/lock_coordinator.py deleted file mode 100644 index b24c22d..0000000 --- a/src/project_x_py/lock_coordinator.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python3 -""" -Lock Coordinator for Thread-Safe Component Interactions - -This module provides coordination between different components (OrderManager, -PositionManager, RealtimeClient) to prevent race conditions and ensure -consistent data access patterns. -""" - -import threading -from collections.abc import Generator -from contextlib import contextmanager - - -class LockCoordinator: - """ - Coordinates locks between different components to prevent deadlocks and race conditions. - - This class ensures that when multiple components need to access shared data, - they do so in a consistent order to prevent deadlocks. - """ - - def __init__(self): - """Initialize the lock coordinator.""" - # Master lock for coordinating component access - self._master_lock = threading.RLock() - - # Component-specific locks in a consistent order to prevent deadlocks - self._realtime_lock = threading.RLock() - self._order_lock = threading.RLock() - self._position_lock = threading.RLock() - - # Lock hierarchy to prevent deadlocks (always acquire in this order) - self._lock_hierarchy = [ - self._realtime_lock, # 1. Realtime client (data source) - self._order_lock, # 2. Order manager - self._position_lock, # 3. Position manager - ] - - @property - def realtime_lock(self) -> threading.RLock: - """Get the realtime client lock.""" - return self._realtime_lock - - @property - def order_lock(self) -> threading.RLock: - """Get the order manager lock.""" - return self._order_lock - - @property - def position_lock(self) -> threading.RLock: - """Get the position manager lock.""" - return self._position_lock - - @contextmanager - def coordinated_access(self, *components: str) -> Generator[None, None, None]: - """ - Acquire locks for multiple components in a safe order. - - Args: - *components: Component names ('realtime', 'order', 'position') - - Example: - >>> with coordinator.coordinated_access("realtime", "order"): - ... # Access data from both components safely - ... pass - """ - # Map component names to locks - component_locks = { - "realtime": self._realtime_lock, - "order": self._order_lock, - "position": self._position_lock, - } - - # Get locks in hierarchy order to prevent deadlocks - requested_locks = [] - for lock in self._lock_hierarchy: - for component in components: - if component_locks.get(component) == lock: - requested_locks.append(lock) - break - - # Acquire locks in order - acquired_locks = [] - try: - for lock in requested_locks: - lock.acquire() - acquired_locks.append(lock) - yield - finally: - # Release locks in reverse order - for lock in reversed(acquired_locks): - lock.release() - - @contextmanager - def all_components_locked(self) -> Generator[None, None, None]: - """ - Acquire all component locks for major operations. - - Use this for operations that need to modify data across multiple components. - """ - with self.coordinated_access("realtime", "order", "position"): - yield - - -# Global lock coordinator instance (singleton pattern) -_global_coordinator: LockCoordinator | None = None -_coordinator_lock = threading.Lock() - - -def get_lock_coordinator() -> LockCoordinator: - """ - Get the global lock coordinator instance. - - Returns: - LockCoordinator: The singleton coordinator instance - """ - global _global_coordinator - - if _global_coordinator is None: - with _coordinator_lock: - if _global_coordinator is None: - _global_coordinator = LockCoordinator() - - return _global_coordinator diff --git a/src/project_x_py/async_order_manager.py b/src/project_x_py/order_manager.py similarity index 98% rename from src/project_x_py/async_order_manager.py rename to src/project_x_py/order_manager.py index b04a380..7603ff3 100644 --- a/src/project_x_py/async_order_manager.py +++ b/src/project_x_py/order_manager.py @@ -34,7 +34,7 @@ async def main(): # Create and initialize order manager order_manager = AsyncOrderManager(client) - realtime_client = AsyncProjectXRealtimeClient(client.config) + realtime_client = ProjectXRealtimeClient(client.config) await order_manager.initialize(realtime_client=realtime_client) # Place a simple market order @@ -79,8 +79,8 @@ async def main(): ) if TYPE_CHECKING: - from .async_client import AsyncProjectX - from .async_realtime import AsyncProjectXRealtimeClient + from .client import ProjectX + from .realtime import ProjectXRealtimeClient class OrderStats(TypedDict): @@ -93,7 +93,7 @@ class OrderStats(TypedDict): last_order_time: datetime | None -class AsyncOrderManager: +class OrderManager: """ Async comprehensive order management system for ProjectX trading operations. @@ -136,7 +136,7 @@ class AsyncOrderManager: Example Usage: ```python # Create async order manager with dependency injection - order_manager = AsyncOrderManager(async_project_x_client) + order_manager = OrderManager(async_project_x_client) # Initialize with optional real-time client await order_manager.initialize(realtime_client=async_realtime_client) @@ -188,28 +188,28 @@ async def on_order_filled(order_data): ``` """ - def __init__(self, project_x_client: "AsyncProjectX"): + def __init__(self, project_x_client: "ProjectX"): """ - Initialize the AsyncOrderManager with an AsyncProjectX client. + Initialize the OrderManager with an ProjectX client. - Creates a new instance of the AsyncOrderManager that uses the provided AsyncProjectX client + Creates a new instance of the OrderManager that uses the provided ProjectX client for API access. This establishes the foundation for order operations but does not set up real-time capabilities. To enable real-time order tracking, call the `initialize` method with a real-time client after initialization. Args: - project_x_client: AsyncProjectX client instance for API access. This client + project_x_client: ProjectX client instance for API access. This client should already be authenticated or authentication should be handled separately before attempting order operations. Example: ```python # Create the AsyncProjectX client first - client = AsyncProjectX() + client = ProjectX() await client.authenticate() # Then create the order manager - order_manager = AsyncOrderManager(client) + order_manager = OrderManager(client) ``` """ self.project_x = project_x_client @@ -219,7 +219,7 @@ def __init__(self, project_x_client: "AsyncProjectX"): self.order_lock = asyncio.Lock() # Real-time integration (optional) - self.realtime_client: AsyncProjectXRealtimeClient | None = None + self.realtime_client: ProjectXRealtimeClient | None = None self._realtime_enabled = False # Internal order state tracking (for realtime optimization) @@ -247,7 +247,7 @@ def __init__(self, project_x_client: "AsyncProjectX"): self.logger.info("AsyncOrderManager initialized") async def initialize( - self, realtime_client: Optional["AsyncProjectXRealtimeClient"] = None + self, realtime_client: Optional["ProjectXRealtimeClient"] = None ) -> bool: """ Initialize the AsyncOrderManager with optional real-time capabilities. @@ -274,11 +274,11 @@ async def initialize( Example: ```python # Create and set up the required components - px_client = AsyncProjectX() + px_client = ProjectX() await px_client.authenticate() # Create the realtime client - realtime = AsyncProjectXRealtimeClient(px_client.config) + realtime = ProjectXRealtimeClient(px_client.config) # Initialize order manager with realtime capabilities order_manager = AsyncOrderManager(px_client) diff --git a/src/project_x_py/async_orderbook/__init__.py b/src/project_x_py/orderbook/__init__.py similarity index 91% rename from src/project_x_py/async_orderbook/__init__.py rename to src/project_x_py/orderbook/__init__.py index 24da238..12fb139 100644 --- a/src/project_x_py/async_orderbook/__init__.py +++ b/src/project_x_py/orderbook/__init__.py @@ -25,16 +25,16 @@ Example: Basic usage with real-time data:: - >>> from project_x_py import AsyncProjectX, create_async_orderbook + >>> from project_x_py import ProjectX, create_orderbook >>> import asyncio >>> >>> async def main(): ... # Initialize client and connect - ... client = AsyncProjectX() + ... client = ProjectX() ... await client.connect() ... ... # Create orderbook with factory function - ... orderbook = create_async_orderbook( + ... orderbook = create_orderbook( ... instrument="MNQ", # Micro Nasdaq futures ... project_x=client, ... timezone_str="America/Chicago" @@ -81,13 +81,13 @@ from typing import TYPE_CHECKING, Any if TYPE_CHECKING: - from project_x_py.async_client import AsyncProjectX - from project_x_py.async_realtime import AsyncProjectXRealtimeClient + from project_x_py.client import ProjectX + from project_x_py.realtime import ProjectXRealtimeClient import logging from .analytics import MarketAnalytics -from .base import AsyncOrderBookBase +from .base import OrderBookBase from .detection import OrderDetection from .memory import MemoryManager from .profile import VolumeProfile @@ -110,22 +110,22 @@ __all__ = [ # Types "AsyncCallback", - "AsyncOrderBook", "CallbackType", "DomType", "IcebergConfig", "MarketDataDict", "MemoryConfig", + "OrderBook", "OrderbookSide", "OrderbookSnapshot", "PriceLevelDict", "SyncCallback", "TradeDict", - "create_async_orderbook", + "create_orderbook", ] -class AsyncOrderBook(AsyncOrderBookBase): +class OrderBook(OrderBookBase): """ Async Level 2 Orderbook with comprehensive market analysis. @@ -152,7 +152,7 @@ class AsyncOrderBook(AsyncOrderBookBase): parameters to prevent memory leaks during long-running sessions. Example: - >>> orderbook = AsyncOrderBook("ES", project_x_client) + >>> orderbook = OrderBook("ES", project_x_client) >>> await orderbook.initialize(realtime_client) >>> >>> # Get basic orderbook data @@ -174,11 +174,11 @@ class AsyncOrderBook(AsyncOrderBookBase): def __init__( self, instrument: str, - project_x: "AsyncProjectX | None" = None, + project_x: "ProjectX | None" = None, timezone_str: str = DEFAULT_TIMEZONE, ): """ - Initialize the async orderbook. + Initialize the orderbook. Args: instrument: Trading instrument symbol @@ -197,7 +197,7 @@ def __init__( async def initialize( self, - realtime_client: "AsyncProjectXRealtimeClient | None" = None, + realtime_client: "ProjectXRealtimeClient | None" = None, subscribe_to_depth: bool = True, subscribe_to_quotes: bool = True, ) -> bool: @@ -206,7 +206,7 @@ async def initialize( This method configures the orderbook for operation, sets up the memory manager, and optionally connects to the real-time data feed. It must be called after - creating an AsyncOrderBook instance and before using any other methods. + creating an OrderBook instance and before using any other methods. The initialization process performs the following steps: 1. Starts the memory manager for automatic cleanup @@ -229,7 +229,7 @@ async def initialize( initialization failed. Example: - >>> orderbook = AsyncOrderBook("MNQ", client) + >>> orderbook = OrderBook("MNQ", client) >>> success = await orderbook.initialize( ... realtime_client=client.realtime_client, ... subscribe_to_depth=True, @@ -253,11 +253,11 @@ async def initialize( self.logger.error("Failed to initialize real-time connection") return False - self.logger.info(f"AsyncOrderBook initialized for {self.instrument}") + self.logger.info(f"OrderBook initialized for {self.instrument}") return True except Exception as e: - self.logger.error(f"Failed to initialize AsyncOrderBook: {e}") + self.logger.error(f"Failed to initialize OrderBook: {e}") return False # Delegate analytics methods @@ -353,16 +353,16 @@ async def cleanup(self) -> None: await super().cleanup() -def create_async_orderbook( +def create_orderbook( instrument: str, - project_x: "AsyncProjectX | None" = None, - realtime_client: "AsyncProjectXRealtimeClient | None" = None, + project_x: "ProjectX | None" = None, + realtime_client: "ProjectXRealtimeClient | None" = None, timezone_str: str = DEFAULT_TIMEZONE, -) -> AsyncOrderBook: +) -> OrderBook: """ - Factory function to create an async orderbook. + Factory function to create an orderbook. - This factory function creates and returns an AsyncOrderBook instance for the specified + This factory function creates and returns an OrderBook instance for the specified instrument. It simplifies the process of creating an orderbook by handling the initial configuration. Note that the returned orderbook is not yet initialized - you must call the initialize() method separately to start the orderbook's functionality. @@ -384,12 +384,12 @@ def create_async_orderbook( All timestamps in the orderbook will be converted to this timezone. Returns: - AsyncOrderBook: Orderbook instance that must be initialized with a call + OrderBook: Orderbook instance that must be initialized with a call to initialize() before use. Example: >>> # Create an orderbook for E-mini S&P 500 futures - >>> orderbook = create_async_orderbook( + >>> orderbook = create_orderbook( ... instrument="ES", # E-mini S&P 500 ... project_x=client, ... timezone_str="America/New_York", @@ -404,4 +404,4 @@ def create_async_orderbook( # Note: realtime_client is passed to initialize() separately to allow # for async initialization _ = realtime_client # Mark as intentionally unused - return AsyncOrderBook(instrument, project_x, timezone_str) + return OrderBook(instrument, project_x, timezone_str) diff --git a/src/project_x_py/async_orderbook/analytics.py b/src/project_x_py/orderbook/analytics.py similarity index 98% rename from src/project_x_py/async_orderbook/analytics.py rename to src/project_x_py/orderbook/analytics.py index 99694a0..70fe891 100644 --- a/src/project_x_py/async_orderbook/analytics.py +++ b/src/project_x_py/orderbook/analytics.py @@ -25,16 +25,16 @@ import polars as pl -from .base import AsyncOrderBookBase +from .base import OrderBookBase class MarketAnalytics: """ - Provides market analytics for the async orderbook. + Provides market analytics for the orderbook. - This class implements advanced analytics methods for the AsyncOrderBook, focusing on + This class implements advanced analytics methods for the OrderBook, focusing on extracting actionable market insights from the raw orderbook data. It is designed as - a component that is injected into the main AsyncOrderBook to provide specialized + a component that is injected into the main OrderBook to provide specialized analytical capabilities while maintaining a clean separation of concerns. The analytics methods focus on several key areas: @@ -54,7 +54,7 @@ class MarketAnalytics: and visualization components that need deeper insights beyond raw orderbook data. """ - def __init__(self, orderbook: AsyncOrderBookBase): + def __init__(self, orderbook: OrderBookBase): self.orderbook = orderbook self.logger = logging.getLogger(__name__) diff --git a/src/project_x_py/async_orderbook/base.py b/src/project_x_py/orderbook/base.py similarity index 97% rename from src/project_x_py/async_orderbook/base.py rename to src/project_x_py/orderbook/base.py index ea27c2e..0c1c0e4 100644 --- a/src/project_x_py/async_orderbook/base.py +++ b/src/project_x_py/orderbook/base.py @@ -16,7 +16,7 @@ - Configurable memory management - Event-driven architecture with customizable callbacks -The AsyncOrderBookBase class serves as the foundation for the complete AsyncOrderBook +The OrderBookBase class serves as the foundation for the complete OrderBook implementation, providing the essential infrastructure while delegating specialized functionality to dedicated component classes. """ @@ -31,21 +31,21 @@ import pytz # type: ignore[import-untyped] if TYPE_CHECKING: - from project_x_py.async_client import AsyncProjectX + from project_x_py.client import ProjectX import logging -from project_x_py.async_orderbook.memory import MemoryManager -from project_x_py.async_orderbook.types import ( +from project_x_py.exceptions import ProjectXError +from project_x_py.orderbook.memory import MemoryManager +from project_x_py.orderbook.types import ( DEFAULT_TIMEZONE, CallbackType, DomType, MemoryConfig, ) -from project_x_py.exceptions import ProjectXError -class AsyncOrderBookBase: +class OrderBookBase: """ Base class for async orderbook with core functionality. @@ -62,7 +62,7 @@ class AsyncOrderBookBase: 5. Implement the callback registration system 6. Support price level history tracking for advanced analytics - This base class is designed to be extended by the full AsyncOrderBook implementation, + This base class is designed to be extended by the full OrderBook implementation, which adds specialized components for analytics, detection algorithms, and real-time data handling. @@ -74,7 +74,7 @@ class AsyncOrderBookBase: def __init__( self, instrument: str, - project_x: "AsyncProjectX | None" = None, + project_x: "ProjectX | None" = None, timezone_str: str = DEFAULT_TIMEZONE, ): """ @@ -558,4 +558,4 @@ async def cleanup(self) -> None: await self.memory_manager.stop() async with self._callback_lock: self.callbacks.clear() - self.logger.info("AsyncOrderBook cleanup completed") + self.logger.info("OrderBook cleanup completed") diff --git a/src/project_x_py/async_orderbook/detection.py b/src/project_x_py/orderbook/detection.py similarity index 98% rename from src/project_x_py/async_orderbook/detection.py rename to src/project_x_py/orderbook/detection.py index 1510267..1bfa472 100644 --- a/src/project_x_py/async_orderbook/detection.py +++ b/src/project_x_py/orderbook/detection.py @@ -31,8 +31,8 @@ import polars as pl -from project_x_py.async_orderbook.base import AsyncOrderBookBase -from project_x_py.async_orderbook.types import IcebergConfig +from project_x_py.orderbook.base import OrderBookBase +from project_x_py.orderbook.types import IcebergConfig class OrderDetection: @@ -42,7 +42,7 @@ class OrderDetection: This class implements sophisticated algorithms for detecting hidden patterns in orderbook data that may indicate specific trading behaviors, hidden liquidity, or other market microstructure phenomena. It is designed as a specialized component - of the AsyncOrderBook that focuses solely on detection capabilities. + of the OrderBook that focuses solely on detection capabilities. Key features: 1. Iceberg order detection - Identifies large orders that are deliberately split @@ -64,7 +64,7 @@ class OrderDetection: allowing for more sophisticated and reliable detections. """ - def __init__(self, orderbook: AsyncOrderBookBase): + def __init__(self, orderbook: OrderBookBase): self.orderbook = orderbook self.logger = logging.getLogger(__name__) self.iceberg_config = IcebergConfig() diff --git a/src/project_x_py/async_orderbook/memory.py b/src/project_x_py/orderbook/memory.py similarity index 98% rename from src/project_x_py/async_orderbook/memory.py rename to src/project_x_py/orderbook/memory.py index 841c196..4984772 100644 --- a/src/project_x_py/async_orderbook/memory.py +++ b/src/project_x_py/orderbook/memory.py @@ -11,12 +11,12 @@ from typing import TYPE_CHECKING, Any if TYPE_CHECKING: - from project_x_py.async_orderbook.base import AsyncOrderBookBase + from project_x_py.orderbook.base import OrderBookBase import contextlib import logging -from project_x_py.async_orderbook.types import MemoryConfig +from project_x_py.orderbook.types import MemoryConfig class MemoryManager: @@ -56,7 +56,7 @@ class MemoryManager: - Maximum entries for various history trackers """ - def __init__(self, orderbook: "AsyncOrderBookBase", config: MemoryConfig): + def __init__(self, orderbook: "OrderBookBase", config: MemoryConfig): self.orderbook = orderbook self.config = config self.logger = logging.getLogger(__name__) diff --git a/src/project_x_py/async_orderbook/profile.py b/src/project_x_py/orderbook/profile.py similarity index 99% rename from src/project_x_py/async_orderbook/profile.py rename to src/project_x_py/orderbook/profile.py index e8ea5e6..c1dbe66 100644 --- a/src/project_x_py/async_orderbook/profile.py +++ b/src/project_x_py/orderbook/profile.py @@ -29,7 +29,7 @@ import polars as pl -from .base import AsyncOrderBookBase +from .base import OrderBookBase class VolumeProfile: @@ -38,7 +38,7 @@ class VolumeProfile: This class implements advanced market structure analysis methods focusing on volume distribution and key price level identification. It is designed as a specialized - component of the AsyncOrderBook that reveals deeper insights into market structure + component of the OrderBook that reveals deeper insights into market structure and participant behavior patterns. Key functionalities: @@ -60,7 +60,7 @@ class VolumeProfile: focus analysis on the most relevant recent market activity. """ - def __init__(self, orderbook: AsyncOrderBookBase): + def __init__(self, orderbook: OrderBookBase): self.orderbook = orderbook self.logger = logging.getLogger(__name__) diff --git a/src/project_x_py/async_orderbook/realtime.py b/src/project_x_py/orderbook/realtime.py similarity index 95% rename from src/project_x_py/async_orderbook/realtime.py rename to src/project_x_py/orderbook/realtime.py index ef7ecc1..9b526f8 100644 --- a/src/project_x_py/async_orderbook/realtime.py +++ b/src/project_x_py/orderbook/realtime.py @@ -11,21 +11,21 @@ import polars as pl if TYPE_CHECKING: - from project_x_py.async_realtime import AsyncProjectXRealtimeClient + from project_x_py.realtime import ProjectXRealtimeClient import logging -from .base import AsyncOrderBookBase +from .base import OrderBookBase from .types import DomType class RealtimeHandler: """Handles real-time data updates for the async orderbook.""" - def __init__(self, orderbook: AsyncOrderBookBase): + def __init__(self, orderbook: OrderBookBase): self.orderbook = orderbook self.logger = logging.getLogger(__name__) - self.realtime_client: AsyncProjectXRealtimeClient | None = None + self.realtime_client: ProjectXRealtimeClient | None = None # Track connection state self.is_connected = False @@ -33,7 +33,7 @@ def __init__(self, orderbook: AsyncOrderBookBase): async def initialize( self, - realtime_client: "AsyncProjectXRealtimeClient", + realtime_client: "ProjectXRealtimeClient", subscribe_to_depth: bool = True, subscribe_to_quotes: bool = True, ) -> bool: @@ -41,7 +41,7 @@ async def initialize( Initialize real-time data feed connection. Args: - realtime_client: Async real-time client instance + realtime_client: real-time client instance subscribe_to_depth: Subscribe to market depth updates subscribe_to_quotes: Subscribe to quote updates @@ -60,16 +60,16 @@ async def initialize( self.is_connected = True self.logger.info( - f"AsyncOrderBook initialized successfully for {self.orderbook.instrument}" + f"OrderBook initialized successfully for {self.orderbook.instrument}" ) return True except Exception as e: - self.logger.error(f"Failed to initialize AsyncOrderBook: {e}") + self.logger.error(f"Failed to initialize OrderBook: {e}") return False async def _setup_realtime_callbacks(self) -> None: - """Setup async callbacks for real-time data processing.""" + """Setup callbacks for real-time data processing.""" if not self.realtime_client: return @@ -82,7 +82,7 @@ async def _setup_realtime_callbacks(self) -> None: await self.realtime_client.add_callback("quote_update", self._on_quote_update) async def _on_market_depth_update(self, data: dict[str, Any]) -> None: - """Async callback for market depth updates (Level 2 data).""" + """Callback for market depth updates (Level 2 data).""" try: self.logger.debug(f"Market depth callback received: {list(data.keys())}") # The data comes structured as {"contract_id": ..., "data": ...} @@ -109,7 +109,7 @@ async def _on_market_depth_update(self, data: dict[str, Any]) -> None: self.logger.error(f"Error processing market depth update: {e}") async def _on_quote_update(self, data: dict[str, Any]) -> None: - """Async callback for quote updates.""" + """Callback for quote updates.""" try: # The data comes structured as {"contract_id": ..., "data": ...} contract_id = data.get("contract_id", "") diff --git a/src/project_x_py/async_orderbook/types.py b/src/project_x_py/orderbook/types.py similarity index 100% rename from src/project_x_py/async_orderbook/types.py rename to src/project_x_py/orderbook/types.py diff --git a/src/project_x_py/async_position_manager.py b/src/project_x_py/position_manager.py similarity index 97% rename from src/project_x_py/async_position_manager.py rename to src/project_x_py/position_manager.py index 30ed803..478dd61 100644 --- a/src/project_x_py/async_position_manager.py +++ b/src/project_x_py/position_manager.py @@ -30,12 +30,12 @@ from .models import Position if TYPE_CHECKING: - from .async_client import AsyncProjectX - from .async_order_manager import AsyncOrderManager - from .async_realtime import AsyncProjectXRealtimeClient + from .client import ProjectX + from .order_manager import OrderManager + from .realtime import ProjectXRealtimeClient -class AsyncPositionManager: +class PositionManager: """ Async comprehensive position management system for ProjectX trading operations. @@ -54,7 +54,7 @@ class AsyncPositionManager: Example Usage: >>> # Create async position manager with dependency injection - >>> position_manager = AsyncPositionManager(async_project_x_client) + >>> position_manager = PositionManager(async_project_x_client) >>> # Initialize with optional real-time client >>> await position_manager.initialize(realtime_client=async_realtime_client) >>> # Get current positions @@ -72,23 +72,23 @@ class AsyncPositionManager: ... ) """ - def __init__(self, project_x_client: "AsyncProjectX"): + def __init__(self, project_x_client: "ProjectX"): """ - Initialize the AsyncPositionManager with an AsyncProjectX client. + Initialize the PositionManager with an ProjectX client. Creates a comprehensive position management system with tracking, monitoring, alerts, risk management, and optional real-time/order synchronization. Args: - project_x_client (AsyncProjectX): The authenticated AsyncProjectX client instance + project_x_client (ProjectX): The authenticated ProjectX client instance used for all API operations. Must be properly authenticated before use. Attributes: - project_x (AsyncProjectX): Reference to the ProjectX client + project_x (ProjectX): Reference to the ProjectX client logger (logging.Logger): Logger instance for this manager position_lock (asyncio.Lock): Thread-safe lock for position operations - realtime_client (AsyncProjectXRealtimeClient | None): Optional real-time client - order_manager (AsyncOrderManager | None): Optional order manager for sync + realtime_client (ProjectXRealtimeClient | None): Optional real-time client + order_manager (OrderManager | None): Optional order manager for sync tracked_positions (dict[str, Position]): Current positions by contract ID position_history (dict[str, list[dict]]): Historical position changes position_callbacks (dict[str, list[Any]]): Event callbacks by type @@ -97,9 +97,9 @@ def __init__(self, project_x_client: "AsyncProjectX"): risk_settings (dict): Risk management configuration Example: - >>> async with AsyncProjectX.from_env() as client: + >>> async with ProjectX.from_env() as client: ... await client.authenticate() - ... position_manager = AsyncPositionManager(client) + ... position_manager = PositionManager(client) """ self.project_x = project_x_client self.logger = logging.getLogger(__name__) @@ -108,11 +108,11 @@ def __init__(self, project_x_client: "AsyncProjectX"): self.position_lock = asyncio.Lock() # Real-time integration (optional) - self.realtime_client: AsyncProjectXRealtimeClient | None = None + self.realtime_client: ProjectXRealtimeClient | None = None self._realtime_enabled = False # Order management integration (optional) - self.order_manager: AsyncOrderManager | None = None + self.order_manager: OrderManager | None = None self._order_sync_enabled = False # Position tracking (maintains local state for business logic) @@ -145,24 +145,24 @@ def __init__(self, project_x_client: "AsyncProjectX"): "alert_threshold": 0.005, # 0.5% threshold for alerts } - self.logger.info("AsyncPositionManager initialized") + self.logger.info("PositionManager initialized") async def initialize( self, - realtime_client: Optional["AsyncProjectXRealtimeClient"] = None, - order_manager: Optional["AsyncOrderManager"] = None, + realtime_client: Optional["ProjectXRealtimeClient"] = None, + order_manager: Optional["OrderManager"] = None, ) -> bool: """ - Initialize the AsyncPositionManager with optional real-time capabilities and order synchronization. + Initialize the PositionManager with optional real-time capabilities and order synchronization. This method sets up advanced features including real-time position tracking via WebSocket and automatic order synchronization. Must be called before using real-time features. Args: - realtime_client (AsyncProjectXRealtimeClient, optional): Real-time client instance + realtime_client (ProjectXRealtimeClient, optional): Real-time client instance for WebSocket-based position updates. When provided, enables live position tracking without polling. Defaults to None (polling mode). - order_manager (AsyncOrderManager, optional): Order manager instance for automatic + order_manager (OrderManager, optional): Order manager instance for automatic order synchronization. When provided, orders are automatically updated when positions change. Defaults to None (no order sync). @@ -174,11 +174,11 @@ async def initialize( Example: >>> # Initialize with real-time tracking - >>> rt_client = create_async_realtime_client(jwt_token) + >>> rt_client = create_realtime_client(jwt_token) >>> success = await position_manager.initialize(realtime_client=rt_client) >>> >>> # Initialize with both real-time and order sync - >>> order_mgr = AsyncOrderManager(client, rt_client) + >>> order_mgr = OrderManager(client, rt_client) >>> success = await position_manager.initialize( ... realtime_client=rt_client, order_manager=order_mgr ... ) @@ -195,17 +195,17 @@ async def initialize( await self._setup_realtime_callbacks() self._realtime_enabled = True self.logger.info( - "āœ… AsyncPositionManager initialized with real-time capabilities" + "āœ… PositionManager initialized with real-time capabilities" ) else: - self.logger.info("āœ… AsyncPositionManager initialized (polling mode)") + self.logger.info("āœ… PositionManager initialized (polling mode)") # Set up order management integration if provided if order_manager: self.order_manager = order_manager self._order_sync_enabled = True self.logger.info( - "āœ… AsyncPositionManager initialized with order synchronization" + "āœ… PositionManager initialized with order synchronization" ) # Load initial positions @@ -214,7 +214,7 @@ async def initialize( return True except Exception as e: - self.logger.error(f"āŒ Failed to initialize AsyncPositionManager: {e}") + self.logger.error(f"āŒ Failed to initialize PositionManager: {e}") return False async def _setup_realtime_callbacks(self) -> None: diff --git a/src/project_x_py/async_realtime.py b/src/project_x_py/realtime.py similarity index 99% rename from src/project_x_py/async_realtime.py rename to src/project_x_py/realtime.py index 6a12f03..d8ae962 100644 --- a/src/project_x_py/async_realtime.py +++ b/src/project_x_py/realtime.py @@ -29,7 +29,7 @@ from .models import ProjectXConfig -class AsyncProjectXRealtimeClient: +class ProjectXRealtimeClient: """ Async real-time client for ProjectX Gateway API WebSocket connections. @@ -57,7 +57,7 @@ class AsyncProjectXRealtimeClient: Example: >>> # Create async client with ProjectX Gateway URLs - >>> client = AsyncProjectXRealtimeClient(jwt_token, account_id) + >>> client = ProjectXRealtimeClient(jwt_token, account_id) >>> # 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) @@ -115,17 +115,17 @@ def __init__( Example: >>> # Using default TopStepX endpoints - >>> client = AsyncProjectXRealtimeClient(jwt_token, "12345") + >>> client = ProjectXRealtimeClient(jwt_token, "12345") >>> >>> # Using custom config >>> config = ProjectXConfig( ... user_hub_url="https://custom.api.com/hubs/user", ... market_hub_url="https://custom.api.com/hubs/market", ... ) - >>> client = AsyncProjectXRealtimeClient(jwt_token, "12345", config=config) + >>> client = ProjectXRealtimeClient(jwt_token, "12345", config=config) >>> >>> # Override specific URL - >>> client = AsyncProjectXRealtimeClient( + >>> client = ProjectXRealtimeClient( ... jwt_token, ... "12345", ... market_hub_url="https://test.api.com/hubs/market", @@ -343,7 +343,7 @@ async def connect(self) -> bool: 6. Updates connection statistics Example: - >>> client = AsyncProjectXRealtimeClient(jwt_token, account_id) + >>> client = ProjectXRealtimeClient(jwt_token, account_id) >>> if await client.connect(): ... print("Connected to ProjectX Gateway") ... # Subscribe to updates @@ -489,8 +489,8 @@ async def subscribe_user_updates(self) -> bool: >>> await client.add_callback("position_update", on_position_update) >>> await client.subscribe_user_updates() >>> # Multiple accounts (if supported) - >>> client1 = AsyncProjectXRealtimeClient(jwt, "12345") - >>> client2 = AsyncProjectXRealtimeClient(jwt, "67890") + >>> client1 = ProjectXRealtimeClient(jwt, "12345") + >>> client2 = ProjectXRealtimeClient(jwt, "67890") >>> await client1.connect() >>> await client2.connect() >>> await client1.subscribe_user_updates() # Account 12345 events diff --git a/src/project_x_py/async_realtime_data_manager.py b/src/project_x_py/realtime_data_manager.py similarity index 97% rename from src/project_x_py/async_realtime_data_manager.py rename to src/project_x_py/realtime_data_manager.py index 4aaa1b1..15fe4f1 100644 --- a/src/project_x_py/async_realtime_data_manager.py +++ b/src/project_x_py/realtime_data_manager.py @@ -108,11 +108,11 @@ async def on_new_bar(data): import pytz if TYPE_CHECKING: - from .async_client import AsyncProjectX - from .async_realtime import AsyncProjectXRealtimeClient + from .client import ProjectX + from .realtime import ProjectXRealtimeClient -class AsyncRealtimeDataManager: +class RealtimeDataManager: """ Async optimized real-time OHLCV data manager for efficient multi-timeframe trading data. @@ -154,11 +154,11 @@ class AsyncRealtimeDataManager: Example Usage: ```python # Create shared async realtime client - async_realtime_client = AsyncProjectXRealtimeClient(config) + async_realtime_client = ProjectXRealtimeClient(config) await async_realtime_client.connect() # Initialize async data manager with dependency injection - manager = AsyncRealtimeDataManager( + manager = RealtimeDataManager( instrument="MGC", # Mini Gold futures project_x=async_project_x_client, # For historical data loading realtime_client=async_realtime_client, @@ -206,27 +206,27 @@ async def on_new_bar(data): def __init__( self, instrument: str, - project_x: "AsyncProjectX", - realtime_client: "AsyncProjectXRealtimeClient", + project_x: "ProjectX", + realtime_client: "ProjectXRealtimeClient", timeframes: list[str] | None = None, timezone: str = "America/Chicago", ): """ - Initialize the async optimized real-time OHLCV data manager with dependency injection. + Initialize the optimized real-time OHLCV data manager with dependency injection. - Creates a new instance of the AsyncRealtimeDataManager that manages real-time market data + Creates a new instance of the RealtimeDataManager that manages real-time market data for a specific trading instrument across multiple timeframes. The manager uses dependency - injection with AsyncProjectX for historical data loading and AsyncProjectXRealtimeClient + injection with ProjectX for historical data loading and ProjectXRealtimeClient for live WebSocket market data. Args: instrument: Trading instrument symbol (e.g., "MGC", "MNQ", "ES"). This should be the base symbol, not a specific contract. - project_x: AsyncProjectX client instance for initial historical data loading. + project_x: ProjectX client instance for initial historical data loading. This client should already be authenticated before passing to this constructor. - realtime_client: AsyncProjectXRealtimeClient instance for live market data. + realtime_client: ProjectXRealtimeClient instance for live market data. The client does not need to be connected yet, as the manager will handle connection when start_realtime_feed() is called. @@ -247,14 +247,14 @@ def __init__( Example: ```python # Create the required clients first - px_client = AsyncProjectX() + px_client = ProjectX() await px_client.authenticate() # Create and connect realtime client - realtime_client = AsyncProjectXRealtimeClient(px_client.config) + realtime_client = ProjectXRealtimeClient(px_client.config) # Create data manager with multiple timeframes for Gold mini futures - data_manager = AsyncRealtimeDataManager( + data_manager = RealtimeDataManager( instrument="MGC", # Gold mini futures project_x=px_client, realtime_client=realtime_client, @@ -343,7 +343,7 @@ def __init__( # Background cleanup task self._cleanup_task: asyncio.Task | None = None - self.logger.info(f"AsyncRealtimeDataManager initialized for {instrument}") + self.logger.info(f"RealtimeDataManager initialized for {instrument}") async def _cleanup_old_data(self) -> None: """ @@ -487,7 +487,7 @@ async def initialize(self, initial_days: int = 1) -> bool: """ try: self.logger.info( - f"Initializing AsyncRealtimeDataManager for {self.instrument}..." + f"Initializing RealtimeDataManager for {self.instrument}..." ) # Get the contract ID for the instrument @@ -518,7 +518,7 @@ async def initialize(self, initial_days: int = 1) -> bool: self.logger.warning(f"āš ļø No data loaded for {tf_key} timeframe") self.logger.info( - f"āœ… AsyncRealtimeDataManager initialized for {self.instrument}" + f"āœ… RealtimeDataManager initialized for {self.instrument}" ) return True @@ -644,7 +644,7 @@ async def stop_realtime_feed(self) -> None: self._cleanup_task = None # Unsubscribe from market data - # Note: unsubscribe_market_data will be implemented in AsyncProjectXRealtimeClient + # Note: unsubscribe_market_data will be implemented in ProjectXRealtimeClient if self.contract_id: self.logger.info(f"šŸ“‰ Unsubscribing from {self.contract_id}") @@ -1276,7 +1276,7 @@ async def cleanup(self) -> None: self.callbacks.clear() self.indicator_cache.clear() - self.logger.info("āœ… AsyncRealtimeDataManager cleanup completed") + self.logger.info("āœ… RealtimeDataManager cleanup completed") def _parse_and_validate_trade_payload(self, trade_data): """Parse and validate trade payload, returning the parsed data or None if invalid."""