Skip to content

Commit 06c2c4f

Browse files
committed
fix: order lifecycle tracking example and cleanup
- Fixed asyncio.wait() error by creating tasks instead of passing coroutines - Fixed instrument lookup - suite.instrument is already an Instrument object - Fixed Order field names (type not orderType) and Position field names (size not netQuantity) - Fixed cancel_order return type handling (returns bool not object) - Added comprehensive cleanup function to cancel open orders and close positions - Cleanup runs in finally block to ensure execution even on errors The example now properly demonstrates order lifecycle tracking and ensures no test orders or positions are left open after completion.
1 parent 5c13483 commit 06c2c4f

File tree

2 files changed

+109
-4
lines changed

2 files changed

+109
-4
lines changed

examples/15_order_lifecycle_tracking.py

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,11 @@ async def demonstrate_advanced_tracking() -> None:
299299
# Wait for any to fill
300300
print("Waiting for first fill...")
301301

302-
fill_tasks = [tracker.wait_for_fill(timeout=5) for tracker in trackers]
302+
# Create tasks instead of coroutines
303+
fill_tasks = [
304+
asyncio.create_task(tracker.wait_for_fill(timeout=5))
305+
for tracker in trackers
306+
]
303307

304308
try:
305309
# Wait for first fill
@@ -377,8 +381,100 @@ async def on_order_event(event: Any) -> None:
377381
await suite.off(EventType.ORDER_CANCELLED, on_order_event)
378382

379383

384+
async def cleanup_demo_orders_and_positions() -> None:
385+
"""Clean up any open orders and positions created during the demo."""
386+
print("\n" + "=" * 50)
387+
print("=== Demo Cleanup ===")
388+
print("=" * 50 + "\n")
389+
390+
async with await TradingSuite.create("MNQ") as suite:
391+
print("Cleaning up demo orders and positions...\n")
392+
393+
# 1. Cancel all open orders
394+
print("1. Checking for open orders...")
395+
open_orders = await suite.orders.search_open_orders()
396+
397+
if open_orders:
398+
print(f" Found {len(open_orders)} open orders to cancel:")
399+
for order in open_orders:
400+
try:
401+
success = await suite.orders.cancel_order(order.id)
402+
if success:
403+
# Get order type and side names safely
404+
order_type = (
405+
"LIMIT"
406+
if order.type == 1
407+
else "MARKET"
408+
if order.type == 2
409+
else "STOP"
410+
if order.type == 4
411+
else str(order.type)
412+
)
413+
side = (
414+
"BUY"
415+
if order.side == 0
416+
else "SELL"
417+
if order.side == 1
418+
else str(order.side)
419+
)
420+
print(f" ✅ Cancelled order {order.id} ({order_type} {side})")
421+
else:
422+
print(f" ⚠️ Failed to cancel order {order.id}")
423+
except Exception as e:
424+
print(f" ⚠️ Error cancelling order {order.id}: {e}")
425+
else:
426+
print(" No open orders found")
427+
428+
print()
429+
430+
# 2. Close all open positions
431+
print("2. Checking for open positions...")
432+
positions = await suite.positions.get_all_positions()
433+
434+
if positions:
435+
print(f" Found {len(positions)} open positions to close:")
436+
for position in positions:
437+
if position.size != 0:
438+
try:
439+
# Place a market order to close the position
440+
# Position type: 1=LONG, 2=SHORT
441+
side = (
442+
1 if position.type == 1 else 0
443+
) # SELL if long, BUY if short
444+
size = position.size # size is always positive
445+
446+
result = await suite.orders.place_market_order(
447+
contract_id=position.contractId, side=side, size=size
448+
)
449+
450+
if result.success:
451+
position_type = (
452+
"LONG"
453+
if position.type == 1
454+
else "SHORT"
455+
if position.type == 2
456+
else "UNKNOWN"
457+
)
458+
print(
459+
f" ✅ Closed {position_type} position in {position.contractId} (Size: {position.size})"
460+
)
461+
else:
462+
print(
463+
f" ⚠️ Failed to close position in {position.contractId}: {result.errorMessage}"
464+
)
465+
except Exception as e:
466+
print(
467+
f" ⚠️ Error closing position in {position.contractId}: {e}"
468+
)
469+
else:
470+
print(" No open positions found")
471+
472+
print("\n✅ Demo cleanup complete!")
473+
474+
380475
async def main() -> None:
381476
"""Run all demonstrations."""
477+
suite = None
382478
try:
383479
# Basic order tracking
384480
await demonstrate_order_tracker()
@@ -399,6 +495,12 @@ async def main() -> None:
399495
import traceback
400496

401497
traceback.print_exc()
498+
finally:
499+
# Always run cleanup, even if demo fails
500+
try:
501+
await cleanup_demo_orders_and_positions()
502+
except Exception as cleanup_error:
503+
print(f"\n⚠️ Cleanup error: {cleanup_error}")
402504

403505

404506
if __name__ == "__main__":

src/project_x_py/order_templates.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ async def create_order(
136136
if size is None:
137137
if risk_amount:
138138
# Size = Risk Amount / Stop Distance
139-
instrument = await suite.client.get_instrument(suite.instrument)
139+
# suite.instrument is already an Instrument object after initialization
140+
instrument = suite.instrument
140141
tick_value = instrument.tickValue if instrument else 1.0
141142
size = int(risk_amount / (stop_dist * tick_value))
142143
elif risk_percent:
@@ -145,7 +146,8 @@ async def create_order(
145146
if not account:
146147
raise ValueError("No account information available")
147148
risk_amount = float(account.balance) * risk_percent
148-
instrument = await suite.client.get_instrument(suite.instrument)
149+
# suite.instrument is already an Instrument object after initialization
150+
instrument = suite.instrument
149151
tick_value = instrument.tickValue if instrument else 1.0
150152
size = int(risk_amount / (stop_dist * tick_value))
151153
else:
@@ -430,7 +432,8 @@ async def create_order(
430432
BracketOrderResponse with order details
431433
"""
432434
# Get instrument for tick size
433-
instrument = await suite.client.get_instrument(suite.instrument)
435+
# suite.instrument is already an Instrument object after initialization
436+
instrument = suite.instrument
434437
if not instrument:
435438
raise ValueError("Cannot get instrument details")
436439

0 commit comments

Comments
 (0)