Skip to content

Commit 9265f82

Browse files
committed
Add typing fixes
1 parent 12b9608 commit 9265f82

14 files changed

+103
-70
lines changed

examples/02_order_management.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
create_realtime_client,
3333
setup_logging,
3434
)
35+
from project_x_py.models import Order
3536

3637

3738
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):
8081
else:
8182
# Fall back to API check for status
8283
print(f" Order {order_id} not in real-time cache, checking API...")
83-
api_order = await order_manager.get_order_by_id(order_id)
84+
api_order: Order | None = await order_manager.get_order_by_id(order_id)
85+
if not isinstance(api_order, Order):
86+
print(f" Order {order_id} not found in API either")
87+
return
88+
8489
if api_order:
8590
status_map = {1: "Open", 2: "Filled", 3: "Cancelled", 4: "Partially Filled"}
8691
status = status_map.get(api_order.status, f"Unknown ({api_order.status})")

examples/03_position_management.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
create_realtime_client,
3131
setup_logging,
3232
)
33+
from project_x_py.position_manager import PositionManager
3334
from project_x_py.realtime_data_manager import RealtimeDataManager
3435

3536

@@ -74,7 +75,7 @@ async def try_get_data(days, interval):
7475
return None
7576

7677

77-
async def display_positions(position_manager):
78+
async def display_positions(position_manager: PositionManager):
7879
"""Display current positions with detailed information."""
7980
print("\n📊 Current Positions:")
8081
print("-" * 80)
@@ -88,29 +89,24 @@ async def display_positions(position_manager):
8889
# Get portfolio P&L concurrently with position display
8990
pnl_task = asyncio.create_task(position_manager.get_portfolio_pnl())
9091

92+
portfolio_pnl = await pnl_task
9193
# Display each position
92-
for symbol, position in positions.items():
93-
print(f"\n{symbol}:")
94-
print(f" Quantity: {position.quantity}")
94+
for position in positions:
95+
print(f"\n{position.contractId}:")
96+
print(f" Quantity: {position.size}")
9597
print(f" Average Price: ${position.averagePrice:.2f}")
96-
print(f" Position Value: ${position.positionValue:.2f}")
97-
print(f" Unrealized P&L: ${position.unrealizedPnl:.2f}")
98-
99-
# Show percentage change
100-
if position.averagePrice > 0:
101-
pnl_pct = (
102-
position.unrealizedPnl / (position.quantity * position.averagePrice)
103-
) * 100
104-
print(f" P&L %: {pnl_pct:+.2f}%")
98+
print(f" Position Value: ${position.averagePrice:.2f}")
99+
print(f" Unrealized P&L: ${portfolio_pnl.get('unrealized_pnl', 0):.2f}")
105100

106101
# Show portfolio totals
107-
portfolio_pnl = await pnl_task
108102
print("\n" + "=" * 40)
109-
print(f"Portfolio Total P&L: ${portfolio_pnl:.2f}")
103+
print(f"Portfolio Total P&L: ${portfolio_pnl.get('net_pnl', 0):.2f}")
110104
print("=" * 40)
111105

112106

113-
async def monitor_positions_realtime(position_manager, duration_seconds=30):
107+
async def monitor_positions_realtime(
108+
position_manager: PositionManager, duration_seconds: int = 30
109+
):
114110
"""Monitor positions with real-time updates."""
115111
print(f"\n🔄 Monitoring positions for {duration_seconds} seconds...")
116112

examples/05_orderbook_analysis.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
create_realtime_client,
3939
setup_logging,
4040
)
41+
from project_x_py.orderbook import OrderBook
4142

4243

4344
async def display_best_prices(orderbook):
@@ -259,7 +260,7 @@ async def monitor_orderbook_feed(orderbook, duration_seconds=60):
259260
print(f" Update Cycles: {update_count}", flush=True)
260261

261262

262-
async def demonstrate_all_orderbook_methods(orderbook):
263+
async def demonstrate_all_orderbook_methods(orderbook: OrderBook):
263264
"""Comprehensive demonstration of all OrderBook methods."""
264265
print("\n🔍 Testing all available OrderBook methods...", flush=True)
265266
print(

examples/06_multi_timeframe_strategy.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
create_trading_suite,
3232
)
3333
from project_x_py.indicators import RSI, SMA
34+
from project_x_py.models import BracketOrderResponse, Position
3435

3536

3637
class MultiTimeframeStrategy:
@@ -47,11 +48,13 @@ class MultiTimeframeStrategy:
4748

4849
def __init__(
4950
self,
51+
client: ProjectX,
5052
trading_suite: dict,
5153
symbol: str = "MNQ",
5254
max_position_size: int = 2,
5355
risk_percentage: float = 0.02,
5456
):
57+
self.client = client
5558
self.suite = trading_suite
5659
self.symbol = symbol
5760
self.max_position_size = max_position_size
@@ -245,19 +248,20 @@ async def generate_trading_signal(self):
245248
async def execute_signal(self, signal_data: dict):
246249
"""Execute trading signal with proper risk management."""
247250
# Check current position
248-
positions = await self.position_manager.get_all_positions()
249-
current_position = positions.get(self.symbol)
251+
positions: list[Position] = await self.position_manager.get_all_positions()
252+
current_position = next(
253+
(pos for pos in positions if pos.contractId == self.symbol), None
254+
)
250255

251256
# Position size limits
252-
if (
253-
current_position
254-
and abs(current_position.quantity) >= self.max_position_size
255-
):
257+
if current_position and abs(current_position.size) >= self.max_position_size:
256258
self.logger.info("Max position size reached, skipping signal")
257259
return
258260

259261
# Get account info for position sizing
260-
account_balance = float(self.order_manager.project_x.account_info.balance)
262+
account_balance = (
263+
float(self.client.account_info.balance) if self.client.account_info else 0
264+
)
261265

262266
# Calculate position size based on risk
263267
entry_price = signal_data["price"]
@@ -285,11 +289,11 @@ async def execute_signal(self, signal_data: dict):
285289
return
286290

287291
# Get active contract
288-
instruments = await self.order_manager.project_x.search_instruments(self.symbol)
292+
instruments = await self.client.search_instruments(self.symbol)
289293
if not instruments:
290294
return
291295

292-
contract_id = instruments[0].activeContract
296+
contract_id = instruments[0].id
293297

294298
# Place bracket order
295299
self.logger.info(
@@ -313,8 +317,14 @@ async def execute_signal(self, signal_data: dict):
313317
take_profit_price=take_profit,
314318
)
315319

320+
if not isinstance(response, BracketOrderResponse):
321+
self.logger.error(f"❌ Unexpected order type: {type(response)}")
322+
return
323+
316324
if response and response.success:
317-
self.logger.info(f"✅ Order placed successfully: {response.orderId}")
325+
self.logger.info(
326+
f"✅ Order placed successfully: {response.entry_order_id}"
327+
)
318328
else:
319329
self.logger.error("❌ Order placement failed")
320330

@@ -434,6 +444,7 @@ def signal_handler(signum, frame):
434444

435445
# Create and configure strategy
436446
strategy = MultiTimeframeStrategy(
447+
client=client,
437448
trading_suite=suite,
438449
symbol="MNQ",
439450
max_position_size=2,

examples/07_technical_indicators.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ async def on_data_update(timeframe):
179179
update_count += 1
180180

181181
# Get latest data
182-
data = await data_manager.get_data(timeframe)
182+
data: pl.DataFrame | None = await data_manager.get_data(timeframe)
183183
if data is None:
184184
return
185185

@@ -189,8 +189,7 @@ async def on_data_update(timeframe):
189189
return
190190

191191
# Calculate key indicators
192-
data = data.pipe(RSI, period=14)
193-
data = data.pipe(SMA, period=20)
192+
data = data.pipe(RSI, period=14).pipe(SMA, period=20)
194193

195194
last_row = data.tail(1)
196195
timestamp = datetime.now().strftime("%H:%M:%S")

examples/08_order_and_position_tracking.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
Order and Position Tracking Demo
44
55
This demo script demonstrates the automatic order cleanup functionality when positions are closed,
6-
using proper async components (AsyncOrderManager, AsyncPositionManager, AsyncRealtimeDataManager).
6+
using proper components (OrderManager, PositionManager, RealtimeDataManager).
77
88
It creates a bracket order and monitors positions and orders in real-time, showing how the system
99
automatically cancels remaining orders when a position is closed (either by stop loss, take profit,
1010
or manual closure from the broker).
1111
1212
Features demonstrated:
13-
- Proper async components for all operations
13+
- Proper components for all operations
1414
- Automatic order cleanup when positions close
1515
- Non-blocking real-time monitoring with clear status updates
16-
- Proper async cleanup on exit (cancels open orders and closes positions)
16+
- Proper cleanup on exit (cancels open orders and closes positions)
1717
- Concurrent operations for improved performance
1818
1919
Usage:
@@ -35,6 +35,7 @@
3535
from datetime import datetime
3636

3737
from project_x_py import ProjectX, create_trading_suite
38+
from project_x_py.models import BracketOrderResponse, Order, Position
3839

3940

4041
class OrderPositionDemo:
@@ -113,6 +114,10 @@ async def create_demo_bracket_order(self) -> bool:
113114
account_id=account_info.id,
114115
)
115116

117+
if not isinstance(bracket_response, BracketOrderResponse):
118+
print(f"❌ Unexpected bracket order type: {type(bracket_response)}")
119+
return False
120+
116121
if bracket_response and bracket_response.success:
117122
print("✅ Bracket order created successfully!")
118123
print(f" Entry Order ID: {bracket_response.entry_order_id}")
@@ -171,6 +176,8 @@ async def display_status(self):
171176
if positions:
172177
print("\n🏦 Position Details:")
173178
for pos in positions:
179+
if not isinstance(pos, Position):
180+
continue
174181
direction = (
175182
"LONG"
176183
if pos.type == 1
@@ -200,6 +207,9 @@ async def display_status(self):
200207
if orders:
201208
print("\n📝 Order Details:")
202209
for order in orders:
210+
if not isinstance(order, Order):
211+
print(f" ❌ Unexpected order type: {type(order)}")
212+
continue
203213
order_type = "UNKNOWN"
204214
if hasattr(order, "type"):
205215
order_type = order.type
@@ -303,13 +313,20 @@ async def cleanup_all_positions_and_orders(self):
303313
print(f"📋 Cancelling {len(orders)} open orders...")
304314
cancel_tasks = []
305315
for order in orders:
316+
if not isinstance(order, Order):
317+
continue
306318
cancel_tasks.append(
307319
self.suite["order_manager"].cancel_order(order.id)
308320
)
309321

310322
# Wait for all cancellations to complete
311-
results = await asyncio.gather(*cancel_tasks, return_exceptions=True)
312-
for order, result in zip(orders, results, strict=False):
323+
cancel_results: list[Order | BaseException] = await asyncio.gather(
324+
*cancel_tasks, return_exceptions=True
325+
)
326+
for order, result in zip(orders, cancel_results, strict=False):
327+
if not isinstance(order, Order):
328+
print(f" ❌ Unexpected order type: {type(order)}")
329+
continue
313330
if isinstance(result, Exception):
314331
print(f" ❌ Error cancelling order {order.id}: {result}")
315332
elif result:
@@ -318,7 +335,9 @@ async def cleanup_all_positions_and_orders(self):
318335
print(f" ⚠️ Failed to cancel order {order.id}")
319336

320337
# Close all open positions
321-
positions = await self.suite["position_manager"].get_all_positions()
338+
positions: list[Position] = await self.suite[
339+
"position_manager"
340+
].get_all_positions()
322341
if positions:
323342
print(f"🏦 Closing {len(positions)} open positions...")
324343
close_tasks = []
@@ -330,7 +349,9 @@ async def cleanup_all_positions_and_orders(self):
330349
)
331350

332351
# Wait for all positions to close
333-
results = await asyncio.gather(*close_tasks, return_exceptions=True)
352+
results: list[Position | BaseException] = await asyncio.gather(
353+
*close_tasks, return_exceptions=True
354+
)
334355
for position, result in zip(positions, results, strict=False):
335356
if isinstance(result, Exception):
336357
print(
@@ -355,7 +376,7 @@ async def cleanup_all_positions_and_orders(self):
355376
print(f"❌ Error during cleanup: {e}")
356377

357378
async def run(self, client: ProjectX):
358-
"""Main async demo execution."""
379+
"""Main demo execution."""
359380
self.setup_signal_handlers()
360381
self.client = client
361382

@@ -376,9 +397,9 @@ async def run(self, client: ProjectX):
376397
print(f"❌ Failed to authenticate: {e}")
377398
return False
378399

379-
# Create async trading suite
400+
# Create trading suite
380401
try:
381-
print("\n🔧 Setting up async trading suite...")
402+
print("\n🔧 Setting up trading suite...")
382403
jwt_token = self.client.session_token
383404
self.suite = await create_trading_suite(
384405
instrument="MNQ",
@@ -388,10 +409,10 @@ async def run(self, client: ProjectX):
388409
timeframes=["5min"], # Minimal timeframes for demo
389410
)
390411

391-
print("✅ Async trading suite created with automatic order cleanup enabled")
412+
print("✅ Trading suite created with automatic order cleanup enabled")
392413

393414
except Exception as e:
394-
print(f"❌ Failed to create async trading suite: {e}")
415+
print(f"❌ Failed to create trading suite: {e}")
395416
return False
396417

397418
# Connect real-time client and initialize data feed
@@ -442,7 +463,7 @@ async def run(self, client: ProjectX):
442463

443464

444465
async def main():
445-
"""Main async entry point."""
466+
"""Main entry point."""
446467
demo = OrderPositionDemo()
447468
try:
448469
async with ProjectX.from_env() as client:

examples/09_get_check_available_instruments.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
#!/usr/bin/env python3
22
"""
3-
Async Interactive Instrument Search Demo for ProjectX
3+
Interactive Instrument Search Demo for ProjectX
44
5-
This async version demonstrates:
5+
This version demonstrates:
66
- Using ProjectX client with async context manager
77
- Async instrument search with await client.search_instruments()
88
- Async best match selection with await client.get_instrument()
99
- Non-blocking user input handling
1010
- Background performance stats monitoring
11-
- Proper async authentication flow
11+
- Proper authentication flow
1212
1313
Key differences from sync version:
1414
- Uses ProjectX instead of ProjectX
1515
- All API calls use await (search_instruments, get_instrument)
16-
- Async context manager (async with)
16+
- Context manager (with)
1717
- Can run background tasks while accepting user input
1818
"""
1919

@@ -108,13 +108,13 @@ def show_common_symbols():
108108

109109

110110
async def get_user_input(prompt):
111-
"""Get user input asynchronously"""
111+
"""Get user input"""
112112
loop = asyncio.get_event_loop()
113113
return await loop.run_in_executor(None, input, prompt)
114114

115115

116116
async def run_interactive_search(client):
117-
"""Run the interactive search loop"""
117+
"""Run the interactive search loop."""
118118
show_common_symbols()
119119

120120
print("\nHow the search works:")
@@ -145,7 +145,7 @@ async def run_interactive_search(client):
145145

146146

147147
async def main():
148-
"""Main async entry point"""
148+
"""Main entry point."""
149149
print("╔═══════════════════════════════════════════════════════╗")
150150
print("║ Async ProjectX Instrument Search Interactive Demo ║")
151151
print("╚═══════════════════════════════════════════════════════╝")

0 commit comments

Comments
 (0)