Trading API
Comprehensive trading functionality including order management, position tracking, and portfolio management.
Order Management
Async order management for ProjectX trading.
Author: @TexasCoding Date: 2025-08-02
- Overview:
This package provides the async OrderManager system for ProjectX, offering robust, extensible order placement, modification, cancellation, tracking, and advanced bracket/position management. Integrates with both API and real-time clients for seamless trading workflows.
- Key Features:
Unified async order placement (market, limit, stop, trailing, bracket)
Modification/cancellation with tick-size alignment
Position-based order and risk management
Real-time tracking, event-driven callbacks, and statistics
Modular design for strategy and bot development
Thread-safe operations with async locks
Automatic price alignment to instrument tick sizes
Comprehensive order lifecycle management
- Order Types Supported:
Market Orders: Immediate execution at current market price
Limit Orders: Execution at specified price or better
Stop Orders: Market orders triggered at stop price
Trailing Stop Orders: Dynamic stops that follow price movement
Bracket Orders: Entry + stop loss + take profit combinations
- Real-time Capabilities:
WebSocket-based order status tracking
Immediate fill/cancellation detection
Event-driven callbacks for custom logic
Local caching to reduce API calls
- Example Usage:
```python # V3: Async order management with event bus integration import asyncio from project_x_py import (
ProjectX, create_realtime_client, create_order_manager, EventBus,
)
- 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%}”)
See also
order_manager.core.OrderManager
order_manager.bracket_orders
order_manager.order_types
order_manager.position_orders
order_manager.tracking
order_manager.utils
- class OrderManager(project_x_client, event_bus, config=None)[source]
Bases:
OrderTrackingMixin
,OrderTypesMixin
,BracketOrderMixin
,PositionOrderMixin
Async comprehensive order management system for ProjectX trading operations.
This class handles all order-related operations including placement, modification, cancellation, and tracking using async/await patterns. It integrates with both the AsyncProjectX client and the AsyncProjectXRealtimeClient for live order monitoring.
- Features:
Complete async order lifecycle management
Bracket order strategies with automatic stop/target placement
Real-time order status tracking (fills/cancellations detected from status changes)
Automatic price alignment to instrument tick sizes
OCO (One-Cancels-Other) order support
Position-based order management
Async-safe operations for concurrent trading
Order callback registration for custom event handling
Performance optimization with local order caching
Comprehensive error handling and validation
Thread-safe operations with async locks
- Order Status Enum Values:
0: None (undefined)
1: Open (active order)
2: Filled (completely executed)
3: Cancelled (cancelled by user or system)
4: Expired (timed out)
5: Rejected (rejected by exchange)
6: Pending (awaiting submission)
- Order Side Enum Values:
0: Buy (bid)
1: Sell (ask)
- Order Type Enum Values:
1: Limit
2: Market
3: StopLimit
4: Stop
5: TrailingStop
6: JoinBid (places limit buy at current best bid)
7: JoinAsk (places limit sell at current best ask)
The OrderManager combines multiple mixins to provide a unified interface for all order-related operations, ensuring consistent behavior and comprehensive functionality across different order types and strategies.
- __init__(project_x_client, event_bus, config=None)[source]
Initialize the OrderManager with an ProjectX client and optional configuration.
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.
- Parameters:
project_x_client (
ProjectXBase
) – ProjectX client instance for API access. This client should already be authenticated or authentication should be handled separately before attempting order operations.event_bus (
Any
) – EventBus instance for unified event handling. Required for all event emissions including order placements, fills, and cancellations.config (
OrderManagerConfig
|None
) – Optional configuration for order management behavior. If not provided, default values will be used for all configuration options.
- async cancel_all_orders(contract_id=None, account_id=None)[source]
Cancel all open orders, optionally filtered by contract.
- async cancel_order(order_id, account_id=None)[source]
Cancel an open order.
- async get_order_by_id(order_id)[source]
Get detailed order information by ID using cached data with API fallback.
- async get_order_statistics()[source]
Get comprehensive order management statistics and system health information.
Provides detailed metrics about order activity, real-time tracking status, position-order relationships, and system health for monitoring and debugging.
- Return type:
OrderManagerStats
- Returns:
Dict with complete statistics
- async initialize(realtime_client=None)[source]
Initialize the AsyncOrderManager with optional real-time capabilities.
This method configures the AsyncOrderManager for operation, optionally enabling real-time order status tracking if a realtime client is provided. Real-time tracking significantly improves performance by minimizing API calls and providing immediate order status updates through websocket connections.
When real-time tracking is enabled: 1. Order status changes are detected immediately 2. Fills, cancellations and rejections are processed in real-time 3. The order_manager caches order data to reduce API calls 4. Callbacks can be triggered for custom event handling 5. WebSocket connections are established for live updates 6. Order tracking is optimized for minimal latency
- Parameters:
realtime_client (
Optional
[ProjectXRealtimeClient
]) – Optional AsyncProjectXRealtimeClient for live order tracking. If provided, the order manager will connect to the real-time API and subscribe to user updates for order status tracking.- Returns:
True if initialization successful, False otherwise.
- Return type:
Note
Real-time tracking is highly recommended for production trading as it provides immediate order status updates and significantly reduces API rate limit consumption.
- async is_order_filled(order_id)[source]
Check if an order has been filled using cached data with API fallback.
Efficiently checks order fill status by first consulting the real-time cache (if available) before falling back to API queries for maximum performance.
- async modify_order(order_id, limit_price=None, stop_price=None, size=None)[source]
Modify an existing order.
- async place_order(contract_id, order_type, side, size, limit_price=None, stop_price=None, trail_price=None, custom_tag=None, linked_order_id=None, account_id=None)[source]
Place an order with comprehensive parameter support and automatic price alignment.
This is the core order placement method that all specific order type methods use internally. It provides complete control over all order parameters and handles automatic price alignment to prevent “Invalid price” errors from the exchange. The method is thread-safe and can be called concurrently from multiple tasks.
The method performs several important operations: 1. Validates all input parameters (size, prices, etc.) 2. Aligns all prices to the instrument’s tick size 3. Ensures proper account authentication 4. Places the order via the ProjectX API 5. Updates internal statistics and tracking 6. Logs the operation for debugging
- Parameters:
contract_id (
str
) – The contract ID to trade (e.g., “MGC”, “MES”, “F.US.EP”)order_type (
int
) – Order type integer value (1=Limit, 2=Market, 4=Stop, 5=TrailingStop)side (
int
) – Order side integer value: 0=Buy, 1=Sellsize (
int
) – Number of contracts to trade (positive integer)limit_price (
float
|None
) – Limit price for limit orders, automatically aligned to tick size.stop_price (
float
|None
) – Stop price for stop orders, automatically aligned to tick size.trail_price (
float
|None
) – Trail amount for trailing stop orders, automatically aligned to tick size.custom_tag (
str
|None
) – Custom identifier for the order (for your reference)linked_order_id (
int
|None
) – ID of a linked order for OCO (One-Cancels-Other) relationshipsaccount_id (
int
|None
) – Account ID. Uses default account from authenticated client if None.
- Returns:
Response containing order ID and status information
- Return type:
- Raises:
ProjectXOrderError – If order placement fails due to invalid parameters or API errors
Example
>>> # V3: Place a limit buy order with automatic price alignment >>> response = await om.place_order( ... contract_id="MGC", ... order_type=1, # Limit order ... side=0, # Buy ... size=1, ... limit_price=2050.0, # Automatically aligned to tick size ... custom_tag="my_strategy_001", # Optional tag for tracking ... ) >>> print(f"Order placed: {response.orderId}") >>> print(f"Success: {response.success}") >>> # V3: Place a stop loss order >>> stop_response = await om.place_order( ... contract_id="MGC", ... order_type=4, # Stop order ... side=1, # Sell ... size=1, ... stop_price=2040.0, # Automatically aligned to tick size ... )
Position Manager Module for ProjectX Trading Platform.
Author: @TexasCoding Date: 2025-08-02
- Overview:
Provides comprehensive position management functionality for ProjectX trading operations, including real-time tracking, P&L calculations, risk management, and direct position operations. Integrates with both API and real-time clients for seamless position lifecycle management.
- Key Features:
Real-time position tracking and monitoring via WebSocket
P&L calculations and portfolio analytics with market prices
Risk metrics and position sizing with configurable thresholds
Position monitoring and alerts with customizable triggers
Direct position operations (close, partial close, bulk operations)
Statistics, history, and comprehensive report generation
Thread-safe operations with async/await patterns
Event-driven callbacks for custom position monitoring
- Position Management Capabilities:
Real-time position updates and closure detection
Portfolio-level P&L analysis with current market prices
Risk assessment and position sizing calculations
Automated position monitoring with configurable alerts
Direct position operations through ProjectX API
Comprehensive reporting and historical analysis
Note
While this module provides direct access to the PositionManager, for most trading applications, it is recommended to use the TradingSuite. The suite automatically creates, configures, and manages the position manager, providing simplified access to its functionality via suite.positions. The example below shows the lower-level manual setup.
- Example Usage:
```python # V3: Comprehensive position management with EventBus integration import asyncio from project_x_py import (
ProjectX, create_realtime_client, create_position_manager, EventBus,
)
- 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)
See also
position_manager.core.PositionManager
position_manager.analytics.PositionAnalyticsMixin
position_manager.risk.RiskManagementMixin
position_manager.monitoring.PositionMonitoringMixin
position_manager.operations.PositionOperationsMixin
position_manager.reporting.PositionReportingMixin
position_manager.tracking.PositionTrackingMixin
- class PositionManager(project_x_client, event_bus, config=None)[source]
Bases:
PositionTrackingMixin
,PositionAnalyticsMixin
,RiskManagementMixin
,PositionMonitoringMixin
,PositionOperationsMixin
,PositionReportingMixin
Async comprehensive position management system for ProjectX trading operations.
This class handles all position-related operations including tracking, monitoring, analysis, and management using async/await patterns. It integrates with both the AsyncProjectX client and the async real-time client for live position monitoring.
- Features:
Complete async position lifecycle management
Real-time position tracking and monitoring via WebSocket
Portfolio-level position management and analytics
Automated P&L calculation and risk metrics
Position sizing and risk management tools
Event-driven position updates (closures detected from size=0)
Async-safe operations for concurrent access
Comprehensive position operations (close, partial close, bulk operations)
Statistics, history, and report generation
- Real-time Capabilities:
WebSocket-based position updates and closure detection
Immediate position change notifications
Event-driven callbacks for custom monitoring
Automatic position synchronization with order management
- Risk Management:
Portfolio risk assessment and concentration analysis
Position sizing calculations with configurable risk parameters
Risk warnings and threshold monitoring
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) ... ) >>> await position_manager.initialize(realtime_client=realtime_client) >>> # V3: Get current positions with actual field names >>> positions = await position_manager.get_all_positions() >>> mgc_position = await position_manager.get_position("MGC") >>> if mgc_position: >>> print(f"Size: {mgc_position.netPos}") >>> print(f"Avg Price: ${mgc_position.buyAvgPrice}") >>> # V3: Portfolio analytics with market prices >>> market_prices = {"MGC": 2050.0, "MNQ": 18500.0} >>> portfolio_pnl = await position_manager.calculate_portfolio_pnl( ... 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 ... )
- __init__(project_x_client, event_bus, config=None)[source]
Initialize the PositionManager with an ProjectX client and optional configuration.
Creates a comprehensive position management system with tracking, monitoring, alerts, risk management, and optional real-time/order synchronization.
- Parameters:
project_x_client (ProjectX) – The authenticated ProjectX client instance used for all API operations. Must be properly authenticated before use.
event_bus (
Any
) – EventBus instance for unified event handling. Required for all event emissions including position updates, P&L changes, and risk alerts.config (
PositionManagerConfig
|None
) – Optional configuration for position management behavior. If not provided, default values will be used for all configuration options.
- project_x
Reference to the ProjectX client
- Type:
- logger
Logger instance for this manager
- Type:
- position_lock
Thread-safe lock for position operations
- Type:
- realtime_client
Optional real-time client
- Type:
ProjectXRealtimeClient | None
- order_manager
Optional order manager for sync
- Type:
OrderManager | None
- event_bus
EventBus instance for unified event handling
- Type:
Any
- stats
Comprehensive tracking statistics
- Type:
- risk_settings
Risk management configuration
- Type:
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 ... )
- async cleanup()[source]
Clean up resources and connections when shutting down.
Performs complete cleanup of the AsyncPositionManager, including stopping monitoring tasks, clearing tracked data, and releasing all resources. Should be called when the manager is no longer needed to prevent memory leaks and ensure graceful shutdown.
- Return type:
- Cleanup operations:
Stops position monitoring (cancels async tasks)
Clears all tracked positions
Clears position history
Removes all callbacks
Clears all alerts
Disconnects order manager integration
Example
>>> # Basic cleanup >>> await position_manager.cleanup() >>> # Cleanup in finally block >>> position_manager = AsyncPositionManager(client) >>> try: ... await position_manager.initialize(realtime_client) ... # ... use position manager ... ... finally: ... await position_manager.cleanup() >>> # Context manager pattern (if implemented) >>> async with AsyncPositionManager(client) as pm: ... await pm.initialize(realtime_client) ... # ... automatic cleanup on exit ...
Note
Safe to call multiple times
Logs successful cleanup
Does not close underlying client connections
- async get_all_positions(account_id=None)[source]
Get all current positions from the API and update tracking.
Retrieves all open positions for the specified account, updates the internal tracking cache, and returns the position list. This is the primary method for fetching position data.
- Parameters:
account_id (int, optional) – The account ID to get positions for. If None, uses the default account from authentication. Defaults to None.
- Returns:
- List of all current open positions. Each Position object
contains id, accountId, contractId, type, size, averagePrice, and creationTimestamp. Empty list if no positions or on error.
- Return type:
- Side effects:
Updates self.tracked_positions with current data
Updates statistics (positions_tracked, last_update_time)
Example
>>> # V3: Get all positions with actual field names >>> positions = await position_manager.get_all_positions() >>> for pos in positions: ... print(f"Contract: {pos.contractId}") ... print(f" Net Position: {pos.netPos}") ... print(f" Buy Avg Price: ${pos.buyAvgPrice:.2f}") ... print(f" Unrealized P&L: ${pos.unrealizedPnl:.2f}") >>> # V3: Get positions for specific account >>> positions = await position_manager.get_all_positions(account_id=12345)
Note
In real-time mode, tracked positions are also updated via WebSocket, but this method always fetches fresh data from the API.
- async get_position(contract_id, account_id=None)[source]
Get a specific position by contract ID.
Searches for a position matching the given contract ID. In real-time mode, checks the local cache first for better performance before falling back to an API call.
- Parameters:
- Returns:
- Position object if found, containing all position details
(id, size, averagePrice, type, etc.). Returns None if no position exists for the contract.
- Return type:
Position | None
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}") ... else: ... print("No MGC position found")
- Performance:
Real-time mode: O(1) cache lookup, falls back to API if miss
Polling mode: Always makes API call via get_all_positions()
- async initialize(realtime_client=None, order_manager=None)[source]
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.
- Parameters:
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 (OrderManager, optional) – Order manager instance for automatic order synchronization. When provided, orders are automatically updated when positions change. Defaults to None (no order sync).
- Returns:
True if initialization successful, False if any errors occurred
- Return type:
- Raises:
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: 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 ... )
Note
Real-time mode provides instant position updates via WebSocket
Polling mode refreshes positions periodically (see start_monitoring)
Order synchronization helps maintain order/position consistency
- async is_position_open(contract_id, account_id=None)[source]
Check if a position exists for the given contract.
Convenience method to quickly check if you have an open position in a specific contract without retrieving the full position details.
- Parameters:
- Returns:
True if an open position exists (size != 0), False otherwise
- Return type:
Example
>>> # Check before placing an order >>> if await position_manager.is_position_open("MGC"): ... print("Already have MGC position") ... else: ... # Safe to open new position ... await order_manager.place_market_order("MGC", 0, 1)
Note
A position with size=0 is considered closed and returns False.
- async refresh_positions(account_id=None)[source]
Refresh all position data from the API.
Forces a fresh fetch of all positions from the API, updating the internal tracking cache. Useful for ensuring data is current after external changes or when real-time updates may have been missed.
- Parameters:
account_id (int, optional) – The account ID to refresh positions for. If None, uses the default account from authentication. Defaults to None.
- Returns:
True if refresh was successful, False if any error occurred
- Return type:
- Side effects:
Updates self.tracked_positions with fresh data
Updates position statistics
Logs refresh results
Example
>>> # Manually refresh positions >>> success = await position_manager.refresh_positions() >>> if success: ... print("Positions refreshed successfully") >>> # Refresh specific account >>> await position_manager.refresh_positions(account_id=12345)
Note
This method is called automatically during initialization and by the monitoring loop in polling mode.
Order Factory Functions
Position Management
- class PositionManager(project_x_client, event_bus, config=None)[source]
Bases:
PositionTrackingMixin
,PositionAnalyticsMixin
,RiskManagementMixin
,PositionMonitoringMixin
,PositionOperationsMixin
,PositionReportingMixin
Async comprehensive position management system for ProjectX trading operations.
This class handles all position-related operations including tracking, monitoring, analysis, and management using async/await patterns. It integrates with both the AsyncProjectX client and the async real-time client for live position monitoring.
- Features:
Complete async position lifecycle management
Real-time position tracking and monitoring via WebSocket
Portfolio-level position management and analytics
Automated P&L calculation and risk metrics
Position sizing and risk management tools
Event-driven position updates (closures detected from size=0)
Async-safe operations for concurrent access
Comprehensive position operations (close, partial close, bulk operations)
Statistics, history, and report generation
- Real-time Capabilities:
WebSocket-based position updates and closure detection
Immediate position change notifications
Event-driven callbacks for custom monitoring
Automatic position synchronization with order management
- Risk Management:
Portfolio risk assessment and concentration analysis
Position sizing calculations with configurable risk parameters
Risk warnings and threshold monitoring
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) ... ) >>> await position_manager.initialize(realtime_client=realtime_client) >>> # V3: Get current positions with actual field names >>> positions = await position_manager.get_all_positions() >>> mgc_position = await position_manager.get_position("MGC") >>> if mgc_position: >>> print(f"Size: {mgc_position.netPos}") >>> print(f"Avg Price: ${mgc_position.buyAvgPrice}") >>> # V3: Portfolio analytics with market prices >>> market_prices = {"MGC": 2050.0, "MNQ": 18500.0} >>> portfolio_pnl = await position_manager.calculate_portfolio_pnl( ... 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 ... )
- __init__(project_x_client, event_bus, config=None)[source]
Initialize the PositionManager with an ProjectX client and optional configuration.
Creates a comprehensive position management system with tracking, monitoring, alerts, risk management, and optional real-time/order synchronization.
- Parameters:
project_x_client (ProjectX) – The authenticated ProjectX client instance used for all API operations. Must be properly authenticated before use.
event_bus (
Any
) – EventBus instance for unified event handling. Required for all event emissions including position updates, P&L changes, and risk alerts.config (
PositionManagerConfig
|None
) – Optional configuration for position management behavior. If not provided, default values will be used for all configuration options.
- logger
Logger instance for this manager
- Type:
- position_lock
Thread-safe lock for position operations
- Type:
- realtime_client
Optional real-time client
- Type:
ProjectXRealtimeClient | None
- order_manager
Optional order manager for sync
- Type:
OrderManager | None
- event_bus
EventBus instance for unified event handling
- Type:
Any
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 ... )
- async initialize(realtime_client=None, order_manager=None)[source]
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.
- Parameters:
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 (OrderManager, optional) – Order manager instance for automatic order synchronization. When provided, orders are automatically updated when positions change. Defaults to None (no order sync).
- Returns:
True if initialization successful, False if any errors occurred
- Return type:
- Raises:
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: 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 ... )
Note
Real-time mode provides instant position updates via WebSocket
Polling mode refreshes positions periodically (see start_monitoring)
Order synchronization helps maintain order/position consistency
- async get_all_positions(account_id=None)[source]
Get all current positions from the API and update tracking.
Retrieves all open positions for the specified account, updates the internal tracking cache, and returns the position list. This is the primary method for fetching position data.
- Parameters:
account_id (int, optional) – The account ID to get positions for. If None, uses the default account from authentication. Defaults to None.
- Returns:
- List of all current open positions. Each Position object
contains id, accountId, contractId, type, size, averagePrice, and creationTimestamp. Empty list if no positions or on error.
- Return type:
- Side effects:
Updates self.tracked_positions with current data
Updates statistics (positions_tracked, last_update_time)
Example
>>> # V3: Get all positions with actual field names >>> positions = await position_manager.get_all_positions() >>> for pos in positions: ... print(f"Contract: {pos.contractId}") ... print(f" Net Position: {pos.netPos}") ... print(f" Buy Avg Price: ${pos.buyAvgPrice:.2f}") ... print(f" Unrealized P&L: ${pos.unrealizedPnl:.2f}") >>> # V3: Get positions for specific account >>> positions = await position_manager.get_all_positions(account_id=12345)
Note
In real-time mode, tracked positions are also updated via WebSocket, but this method always fetches fresh data from the API.
- async get_position(contract_id, account_id=None)[source]
Get a specific position by contract ID.
Searches for a position matching the given contract ID. In real-time mode, checks the local cache first for better performance before falling back to an API call.
- Parameters:
- Returns:
- Position object if found, containing all position details
(id, size, averagePrice, type, etc.). Returns None if no position exists for the contract.
- Return type:
Position | None
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}") ... else: ... print("No MGC position found")
- Performance:
Real-time mode: O(1) cache lookup, falls back to API if miss
Polling mode: Always makes API call via get_all_positions()
- async refresh_positions(account_id=None)[source]
Refresh all position data from the API.
Forces a fresh fetch of all positions from the API, updating the internal tracking cache. Useful for ensuring data is current after external changes or when real-time updates may have been missed.
- Parameters:
account_id (int, optional) – The account ID to refresh positions for. If None, uses the default account from authentication. Defaults to None.
- Returns:
True if refresh was successful, False if any error occurred
- Return type:
- Side effects:
Updates self.tracked_positions with fresh data
Updates position statistics
Logs refresh results
Example
>>> # Manually refresh positions >>> success = await position_manager.refresh_positions() >>> if success: ... print("Positions refreshed successfully") >>> # Refresh specific account >>> await position_manager.refresh_positions(account_id=12345)
Note
This method is called automatically during initialization and by the monitoring loop in polling mode.
- async is_position_open(contract_id, account_id=None)[source]
Check if a position exists for the given contract.
Convenience method to quickly check if you have an open position in a specific contract without retrieving the full position details.
- Parameters:
- Returns:
True if an open position exists (size != 0), False otherwise
- Return type:
Example
>>> # Check before placing an order >>> if await position_manager.is_position_open("MGC"): ... print("Already have MGC position") ... else: ... # Safe to open new position ... await order_manager.place_market_order("MGC", 0, 1)
Note
A position with size=0 is considered closed and returns False.
- async cleanup()[source]
Clean up resources and connections when shutting down.
Performs complete cleanup of the AsyncPositionManager, including stopping monitoring tasks, clearing tracked data, and releasing all resources. Should be called when the manager is no longer needed to prevent memory leaks and ensure graceful shutdown.
- Return type:
- Cleanup operations:
Stops position monitoring (cancels async tasks)
Clears all tracked positions
Clears position history
Removes all callbacks
Clears all alerts
Disconnects order manager integration
Example
>>> # Basic cleanup >>> await position_manager.cleanup() >>> # Cleanup in finally block >>> position_manager = AsyncPositionManager(client) >>> try: ... await position_manager.initialize(realtime_client) ... # ... use position manager ... ... finally: ... await position_manager.cleanup() >>> # Context manager pattern (if implemented) >>> async with AsyncPositionManager(client) as pm: ... await pm.initialize(realtime_client) ... # ... automatic cleanup on exit ...
Note
Safe to call multiple times
Logs successful cleanup
Does not close underlying client connections
Position Factory Functions
Trading Suite
Order Models
- class Order(id, accountId, contractId, creationTimestamp, updateTimestamp, status, type, side, size, symbolId=None, fillVolume=None, limitPrice=None, stopPrice=None, filledPrice=None, customTag=None)[source]
Bases:
object
Represents a trading order with all its details.
- status
Order status code (OrderStatus enum): 0=None, 1=Open, 2=Filled, 3=Cancelled, 4=Expired, 5=Rejected, 6=Pending
- Type:
- type
Order type (OrderType enum): 0=Unknown, 1=Limit, 2=Market, 3=StopLimit, 4=Stop, 5=TrailingStop, 6=JoinBid, 7=JoinAsk
- Type:
Example
>>> side_str = "Bid" if order.side == 0 else "Ask" >>> print(f"Order {order.id}: {side_str} {order.size} {order.contractId}")
- __init__(id, accountId, contractId, creationTimestamp, updateTimestamp, status, type, side, size, symbolId=None, fillVolume=None, limitPrice=None, stopPrice=None, filledPrice=None, customTag=None)
- class OrderPlaceResponse(orderId, success, errorCode, errorMessage)[source]
Bases:
object
Response from placing an order.
Example
>>> if response.success: ... print(f"Order placed successfully with ID: {response.orderId}") ... else: ... print(f"Order failed: {response.errorMessage}")
- __init__(orderId, success, errorCode, errorMessage)
- class BracketOrderResponse(success, entry_order_id, stop_order_id, target_order_id, entry_price, stop_loss_price, take_profit_price, entry_response, stop_response, target_response, error_message)[source]
Bases:
object
Response from placing a bracket order with entry, stop loss, and take profit.
- entry_response
Response from entry order
- Type:
- stop_response
Response from stop loss order
- Type:
Optional[OrderPlaceResponse]
- target_response
Response from take profit order
- Type:
Optional[OrderPlaceResponse]
Example
>>> if response.success: ... print(f"Bracket order placed successfully:") ... print(f" Entry: {response.entry_order_id} @ ${response.entry_price}") ... print(f" Stop: {response.stop_order_id} @ ${response.stop_loss_price}") ... print( ... f" Target: {response.target_order_id} @ ${response.take_profit_price}" ... ) ... else: ... print(f"Bracket order failed: {response.error_message}")
-
entry_response:
OrderPlaceResponse
|None
-
stop_response:
OrderPlaceResponse
|None
-
target_response:
OrderPlaceResponse
|None
- __init__(success, entry_order_id, stop_order_id, target_order_id, entry_price, stop_loss_price, take_profit_price, entry_response, stop_response, target_response, error_message)
Position Models
- class Position(id, accountId, contractId, creationTimestamp, type, size, averagePrice)[source]
Bases:
object
Represents an open trading position.
Note
This model contains only the fields returned by ProjectX API. For P&L calculations, use PositionManager.calculate_position_pnl() method.
Example
>>> direction = "LONG" if position.type == PositionType.LONG else "SHORT" >>> print( ... f"{direction} {position.size} {position.contractId} @ ${position.averagePrice}" ... )
- unrealized_pnl(current_price, tick_value=1.0)[source]
Calculate unrealized P&L given current price.
- __init__(id, accountId, contractId, creationTimestamp, type, size, averagePrice)
Account Models
- class Account(id, name, balance, canTrade, isVisible, simulated)[source]
Bases:
object
Represents a trading account with balance and permissions.
Example
>>> print(f"Account: {account.name}") >>> print(f"Balance: ${account.balance:,.2f}") >>> print(f"Trading enabled: {account.canTrade}")
- __init__(id, name, balance, canTrade, isVisible, simulated)
- class Trade(id, accountId, contractId, creationTimestamp, price, profitAndLoss, fees, side, size, voided, orderId)[source]
Bases:
object
Represents an executed trade with P&L information.
Note
A profitAndLoss value of None indicates a “half-turn” trade, meaning this trade opened or added to a position rather than closing it.
Example
>>> side_str = "Buy" if trade.side == 0 else "Sell" >>> pnl_str = f"${trade.profitAndLoss}" if trade.profitAndLoss else "Half-turn" >>> print(f"{side_str} {trade.size} @ ${trade.price} - P&L: {pnl_str}")
- __init__(id, accountId, contractId, creationTimestamp, price, profitAndLoss, fees, side, size, voided, orderId)