Skip to content

Commit 2557e08

Browse files
committed
work on v3 integration
1 parent 2c803ef commit 2557e08

35 files changed

+2962
-319
lines changed

SDK_IMPROVEMENTS_PLAN.md

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -713,18 +713,30 @@ def get_stats(self) -> TradingSuiteStats:
713713
- 63% reduction in order filtering code
714714
- Cleaner, more intuitive strategy code
715715

716-
### Phase 5 (Week 5): Advanced Features
717-
1. **Order Lifecycle Management** (5 days)
718-
- Implement OrderTracker
719-
- Delete manual tracking code
720-
721-
### Phase 6 (Week 6): Risk and Recovery
722-
1. **Built-in Risk Management** (3 days)
723-
- Create RiskManager
724-
- Integrate with order placement
725-
2. **Better Error Recovery** (2 days)
726-
- Implement automatic reconnection
727-
- Delete manual recovery code
716+
### Phase 5 (Week 5): Advanced Features ✅ COMPLETED (2025-08-04)
717+
1. **Order Lifecycle Management** ✅ COMPLETED
718+
- ✅ Implemented OrderTracker with context manager for automatic cleanup
719+
- ✅ Added async waiting mechanisms (wait_for_fill, wait_for_status)
720+
- ✅ Created OrderChainBuilder for fluent API order construction
721+
- ✅ Added common order templates (RiskReward, ATR, Breakout, Scalping)
722+
- ✅ Integrated into TradingSuite with track_order() and order_chain() methods
723+
- ✅ Removed need for manual order tracking in strategies
724+
- ✅ Created comprehensive example demonstrating all features
725+
726+
### Phase 6 (Week 6): Risk and Recovery ✅ COMPLETED (2025-08-04)
727+
1. **Built-in Risk Management** ✅ COMPLETED
728+
- ✅ Created comprehensive RiskManager with position sizing algorithms
729+
- ✅ Implemented trade validation against configurable risk rules
730+
- ✅ Added automatic stop-loss and take-profit attachment
731+
- ✅ Created ManagedTrade context manager for simplified trading
732+
- ✅ Integrated RiskManager into TradingSuite with feature flag
733+
- ✅ Added Kelly Criterion position sizing support
734+
- ✅ Implemented daily loss and trade limits
735+
- ✅ Created trailing stop monitoring
736+
2. **Better Error Recovery** (Future Enhancement)
737+
- ConnectionManager for automatic reconnection
738+
- StateManager for persistence and recovery
739+
- Operation queuing during disconnections
728740

729741
## Type System Implementation Roadmap
730742

@@ -1001,15 +1013,26 @@ def get_stats(self) -> TradingSuiteStats:
10011013
- **Strategy-Friendly**: Properties like `is_long`, `direction`, `symbol` make code cleaner
10021014
- **Result**: Much more intuitive and less error-prone strategy development
10031015

1004-
### Next Up
1005-
🎯 **Phase 5: Advanced Features**
1006-
- Implement OrderTracker for lifecycle management
1007-
- Delete manual tracking code
1008-
1009-
### Next Steps
1010-
1. Implement OrderTracker component (Week 5)
1011-
2. Add automatic order lifecycle tracking
1012-
3. Remove manual order state management code
1016+
**Phase 5: Order Lifecycle Management**
1017+
- **OrderTracker**: Context manager for comprehensive order lifecycle tracking
1018+
- **Async Waiting**: wait_for_fill() and wait_for_status() eliminate polling
1019+
- **OrderChainBuilder**: Fluent API for complex order structures
1020+
- **Order Templates**: Pre-configured templates for common trading patterns
1021+
- **Result**: 90% reduction in order management complexity
1022+
1023+
**Phase 6: Risk Management**
1024+
- **RiskManager**: Comprehensive risk management system with position sizing
1025+
- **ManagedTrade**: Context manager for risk-controlled trade execution
1026+
- **Risk Validation**: Automatic validation against configurable risk rules
1027+
- **Position Sizing**: Fixed risk and Kelly Criterion algorithms
1028+
- **Auto Risk Orders**: Automatic stop-loss and take-profit attachment
1029+
- **Result**: Professional-grade risk management built into the SDK
1030+
1031+
### Future Enhancements
1032+
- **Error Recovery**: ConnectionManager for automatic reconnection
1033+
- **State Persistence**: StateManager for saving/restoring trading state
1034+
- **Trade Journal**: Automatic trade logging and analysis
1035+
- **Performance Analytics**: Real-time strategy performance metrics
10131036

10141037
### Achievements So Far
10151038
- **80% reduction** in initialization code (from ~50 lines to 1 line)
@@ -1033,7 +1056,16 @@ def get_stats(self) -> TradingSuiteStats:
10331056
- **60-80% code reduction** in common trading patterns
10341057
- **Intuitive property names** - no more magic numbers or verbose checks
10351058
- **Strategy-friendly design** - properties like is_long, direction, symbol
1036-
- **14 comprehensive examples** demonstrating all v3.0.0 features
1059+
- **Comprehensive order lifecycle management** - OrderTracker eliminates manual state tracking
1060+
- **Fluent order API** - OrderChainBuilder for complex order structures
1061+
- **Pre-configured templates** - 11 order templates for common trading patterns
1062+
- **90% reduction** in order management complexity
1063+
- **Professional risk management** - RiskManager with position sizing algorithms
1064+
- **Risk-controlled trading** - ManagedTrade context manager for automatic risk management
1065+
- **Trade validation** - Automatic validation against configurable risk rules
1066+
- **Multiple position sizing methods** - Fixed risk, Kelly Criterion, and more
1067+
- **Automatic protective orders** - Stop-loss and take-profit attachment
1068+
- **17 comprehensive examples** demonstrating all v3.0.0 features
10371069

10381070
## Conclusion
10391071

examples/01_basic_client_connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ async def main():
7272
health_task, positions_task, instruments_task
7373
)
7474

75-
print(f"✅ API Calls Made: {health['client_stats']['api_calls']}")
75+
print(f"✅ API Calls Made: {health['api_calls']}")
7676
print(f"✅ Open Positions: {len(positions)}")
7777
print(f"✅ Found Instruments: {len(instruments)}")
7878

examples/02_order_management.py

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ async def main() -> bool:
121121
try:
122122
# Initialize trading suite with TradingSuite v3
123123
print("\n🔑 Initializing TradingSuite v3...")
124-
suite = await TradingSuite.create("MNQ", features=["realtime_tracking"])
124+
suite = await TradingSuite.create("MNQ")
125125

126126
account = suite.client.account_info
127127
if not account:
@@ -333,27 +333,60 @@ async def main() -> bool:
333333

334334
first_order = demo_orders[0]
335335
print(f"Attempting to modify Order #{first_order}")
336+
337+
# Get order details to determine type
338+
order_data = await order_manager.get_tracked_order_status(
339+
str(first_order), wait_for_cache=True
340+
)
341+
342+
if not order_data:
343+
# Fallback to API
344+
api_order = await order_manager.get_order_by_id(first_order)
345+
if api_order and hasattr(api_order, "type"):
346+
# Order types: 1=Limit, 2=Market, 4=Stop
347+
is_stop_order = api_order.type == 4
348+
else:
349+
is_stop_order = False
350+
else:
351+
# Check if it has a stop price (indicating stop order)
352+
is_stop_order = bool(order_data.get("stopPrice"))
353+
336354
await show_order_status(
337355
order_manager, first_order, "Before Modification"
338356
)
339357

340-
# Try modifying the order (move price closer to market)
341-
new_limit_price = current_price - Decimal("5.0") # Closer to market
342-
print(f"\nModifying to new limit price: ${new_limit_price:.2f}")
358+
# Modify based on order type
359+
if is_stop_order:
360+
# For stop orders, modify the stop price
361+
new_stop_price = current_price + Decimal("10.0") # Move stop closer
362+
print(
363+
f"\nModifying stop order to new stop price: ${new_stop_price:.2f}"
364+
)
343365

344-
if await wait_for_user_confirmation("Modify order?"):
345-
modify_success = await order_manager.modify_order(
346-
order_id=first_order, limit_price=float(new_limit_price)
366+
if await wait_for_user_confirmation("Modify order?"):
367+
modify_success = await order_manager.modify_order(
368+
order_id=first_order, stop_price=float(new_stop_price)
369+
)
370+
else:
371+
# For limit orders, modify the limit price
372+
new_limit_price = current_price - Decimal("5.0") # Closer to market
373+
print(
374+
f"\nModifying limit order to new limit price: ${new_limit_price:.2f}"
347375
)
348376

349-
if modify_success:
350-
print(f"✅ Order {first_order} modified successfully")
351-
await asyncio.sleep(2)
352-
await show_order_status(
353-
order_manager, first_order, "After Modification"
377+
if await wait_for_user_confirmation("Modify order?"):
378+
modify_success = await order_manager.modify_order(
379+
order_id=first_order, limit_price=float(new_limit_price)
354380
)
355-
else:
356-
print(f"❌ Failed to modify order {first_order}")
381+
382+
if "modify_success" in locals() and modify_success:
383+
print(f"✅ Order {first_order} modified successfully")
384+
await asyncio.sleep(2)
385+
await show_order_status(
386+
order_manager, first_order, "After Modification"
387+
)
388+
elif "modify_success" in locals():
389+
print(f"❌ Failed to modify order {first_order}")
357390

358391
# Monitor orders for a short time
359392
if demo_orders:
@@ -427,12 +460,14 @@ async def main() -> bool:
427460

428461
stats = await order_manager.get_order_statistics()
429462
print("Order Manager Statistics:")
430-
print(f" Orders Placed: {stats['statistics']['orders_placed']}")
431-
print(f" Orders Cancelled: {stats['statistics']['orders_cancelled']}")
432-
print(f" Orders Modified: {stats['statistics']['orders_modified']}")
433-
print(f" Bracket Orders: {stats['statistics']['bracket_orders_placed']}")
434-
print(f" Tracked Orders: {stats['tracked_orders']}")
435-
print(f" Real-time Enabled: {stats['realtime_enabled']}")
463+
print(f" Orders Placed: {stats.get('orders_placed', 0)}")
464+
print(f" Orders Cancelled: {stats.get('orders_cancelled', 0)}")
465+
print(f" Orders Modified: {stats.get('orders_modified', 0)}")
466+
print(f" Bracket Orders: {stats.get('bracket_orders', 0)}")
467+
print(f" Fill Rate: {stats.get('fill_rate', 0):.1%}")
468+
print(f" Market Orders: {stats.get('market_orders', 0)}")
469+
print(f" Limit Orders: {stats.get('limit_orders', 0)}")
470+
print(f" Stop Orders: {stats.get('stop_orders', 0)}")
436471

437472
finally:
438473
# Enhanced cleanup: Cancel ALL orders and close ALL positions

examples/03_position_management.py

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ async def try_get_data(days: int, interval: int) -> float | None:
7777
return None
7878

7979

80-
async def display_positions(position_manager: "PositionManager") -> None:
80+
async def display_positions(
81+
position_manager: "PositionManager", suite: TradingSuite | None = None
82+
) -> None:
8183
"""Display current positions with detailed information."""
8284
print("\n📊 Current Positions:")
8385
print("-" * 80)
@@ -88,17 +90,45 @@ async def display_positions(position_manager: "PositionManager") -> None:
8890
print("No open positions")
8991
return
9092

91-
# Get portfolio P&L concurrently with position display
92-
pnl_task = asyncio.create_task(position_manager.get_portfolio_pnl())
93-
94-
portfolio_pnl = await pnl_task
95-
# Display each position
93+
# Display each position with real P&L calculation
9694
for position in positions:
9795
print(f"\n{position.contractId}:")
9896
print(f" Quantity: {position.size}")
9997
print(f" Average Price: ${position.averagePrice:.2f}")
100-
print(f" Position Value: ${position.averagePrice:.2f}")
101-
print(f" Unrealized P&L: ${portfolio_pnl.get('unrealized_pnl', 0):.2f}")
98+
99+
# Calculate position value
100+
position_value = position.averagePrice * position.size
101+
print(f" Position Value: ${position_value:,.2f}")
102+
103+
# Calculate real P&L if we have market data
104+
unrealized_pnl = 0.0
105+
if suite:
106+
try:
107+
# Get current market price
108+
current_price = await suite.data.get_current_price()
109+
if current_price:
110+
# Get instrument info for tick value
111+
instrument_info = await suite.client.get_instrument(
112+
suite.instrument
113+
)
114+
tick_value = instrument_info.tickValue
115+
116+
# Calculate P&L using position manager's method
117+
pnl_data = await position_manager.calculate_position_pnl(
118+
position, float(current_price), point_value=tick_value
119+
)
120+
unrealized_pnl = pnl_data["unrealized_pnl"]
121+
except Exception:
122+
# If we can't get real-time price, try portfolio P&L
123+
try:
124+
portfolio_pnl = await position_manager.get_portfolio_pnl()
125+
unrealized_pnl = portfolio_pnl.get("unrealized_pnl", 0) / len(
126+
positions
127+
)
128+
except Exception:
129+
pass
130+
131+
print(f" Unrealized P&L: ${unrealized_pnl:,.2f}")
102132

103133

104134
async def display_risk_metrics(position_manager: "PositionManager") -> None:
@@ -137,7 +167,9 @@ async def display_risk_metrics(position_manager: "PositionManager") -> None:
137167

138168

139169
async def monitor_positions(
140-
position_manager: "PositionManager", duration: int = 30
170+
position_manager: "PositionManager",
171+
suite: TradingSuite | None = None,
172+
duration: int = 30,
141173
) -> None:
142174
"""Monitor positions for a specified duration with async updates."""
143175
print(f"\n👁️ Monitoring positions for {duration} seconds...")
@@ -157,13 +189,36 @@ async def monitor_positions(
157189
if positions:
158190
print(f" Active positions: {len(positions)}")
159191

160-
# Get P&L if available
192+
# Get P&L with current market prices
193+
total_pnl = 0.0
161194
try:
162-
pnl = await position_manager.get_portfolio_pnl()
163-
print(f" Total P&L: ${pnl.get('total_pnl', 0):,.2f}")
195+
# Try to calculate real P&L with current prices
196+
if suite and positions:
197+
current_price = await suite.data.get_current_price()
198+
if current_price:
199+
instrument_info = await suite.client.get_instrument(
200+
suite.instrument
201+
)
202+
tick_value = instrument_info.tickValue
203+
204+
for position in positions:
205+
pnl_data = (
206+
await position_manager.calculate_position_pnl(
207+
position,
208+
float(current_price),
209+
point_value=tick_value,
210+
)
211+
)
212+
total_pnl += pnl_data["unrealized_pnl"]
213+
else:
214+
# Fallback to portfolio P&L
215+
pnl = await position_manager.get_portfolio_pnl()
216+
total_pnl = pnl.get("total_pnl", 0)
164217
except Exception:
165218
pass
166219

220+
print(f" Total P&L: ${total_pnl:,.2f}")
221+
167222
# Get summary if available
168223
if hasattr(position_manager, "get_portfolio_summary"):
169224
try:
@@ -194,7 +249,6 @@ async def main() -> bool:
194249
print("\n🔑 Initializing TradingSuite v3...")
195250
suite = await TradingSuite.create(
196251
"MNQ",
197-
features=["realtime_tracking"],
198252
timeframes=["1min", "5min"],
199253
)
200254

@@ -208,7 +262,7 @@ async def main() -> bool:
208262

209263
if existing_positions:
210264
print(f"Found {len(existing_positions)} existing positions")
211-
await display_positions(suite.positions)
265+
await display_positions(suite.positions, suite)
212266
else:
213267
print("No existing positions found")
214268

@@ -218,11 +272,14 @@ async def main() -> bool:
218272
)
219273
print(" (This will place a REAL order on the market)")
220274

221-
# Get current price for order placement
275+
# Get instrument info and current price for order placement
276+
instrument_info = await suite.client.get_instrument("MNQ")
277+
contract_id = instrument_info.id
222278
current_price = await get_current_market_price(suite)
223279

224280
if current_price:
225281
print(f"\n Current MNQ price: ${current_price:.2f}")
282+
print(f" Contract ID: {contract_id}")
226283
print(" Test order: BUY 1 MNQ at market")
227284

228285
# Wait for user confirmation
@@ -236,7 +293,7 @@ async def main() -> bool:
236293
if response == "y":
237294
print("\n Placing market order...")
238295
order_response = await suite.orders.place_market_order(
239-
contract_id="MNQ",
296+
contract_id=contract_id,
240297
side=0,
241298
size=1, # Buy
242299
)
@@ -266,7 +323,7 @@ async def main() -> bool:
266323
print("=" * 80)
267324

268325
# 1. Display current positions
269-
await display_positions(suite.positions)
326+
await display_positions(suite.positions, suite)
270327

271328
# 2. Show risk metrics
272329
await display_risk_metrics(suite.positions)
@@ -313,7 +370,7 @@ async def main() -> bool:
313370
print("=" * 80)
314371

315372
# Monitor for 30 seconds
316-
await monitor_positions(suite.positions, duration=30)
373+
await monitor_positions(suite.positions, suite, duration=30)
317374

318375
# 6. Offer to close positions
319376
if await suite.positions.get_all_positions():

0 commit comments

Comments
 (0)