Skip to content

Commit ea2a133

Browse files
committed
mypy fixes
1 parent 2557e08 commit ea2a133

File tree

16 files changed

+174
-118
lines changed

16 files changed

+174
-118
lines changed

src/project_x_py/order_templates.py

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
import logging
3737
from abc import ABC, abstractmethod
38-
from typing import TYPE_CHECKING, Optional
38+
from typing import TYPE_CHECKING, Any, Optional
3939

4040
from project_x_py.indicators import ATR
4141
from project_x_py.models import BracketOrderResponse
@@ -52,7 +52,11 @@ class OrderTemplate(ABC):
5252

5353
@abstractmethod
5454
async def create_order(
55-
self, suite: "TradingSuite", side: int, size: Optional[int] = None, **kwargs
55+
self,
56+
suite: "TradingSuite",
57+
side: int,
58+
size: Optional[int] = None,
59+
**kwargs: Any,
5660
) -> BracketOrderResponse:
5761
"""Create an order using this template."""
5862
pass
@@ -92,7 +96,7 @@ async def create_order(
9296
risk_amount: Optional[float] = None,
9397
risk_percent: Optional[float] = None,
9498
entry_offset: float = 0,
95-
**kwargs,
99+
**kwargs: Any,
96100
) -> BracketOrderResponse:
97101
"""
98102
Create an order with fixed risk/reward ratio.
@@ -109,7 +113,7 @@ async def create_order(
109113
BracketOrderResponse with order details
110114
"""
111115
# Get current price
112-
current_price = await suite.data.get_latest_price()
116+
current_price = await suite.data.get_current_price()
113117
if not current_price:
114118
raise ValueError("Cannot get current price")
115119

@@ -138,8 +142,10 @@ async def create_order(
138142
size = int(risk_amount / (stop_dist * tick_value))
139143
elif risk_percent:
140144
# Get account balance
141-
account = await suite.client.get_account_info()
142-
risk_amount = account.balance * risk_percent
145+
account = suite.client.account_info
146+
if not account:
147+
raise ValueError("No account information available")
148+
risk_amount = float(account.balance) * risk_percent
143149
instrument = await suite.client.get_instrument(suite.instrument)
144150
tick_value = instrument.tickValue if instrument else 1.0
145151
size = int(risk_amount / (stop_dist * tick_value))
@@ -149,6 +155,9 @@ async def create_order(
149155
# Build order chain
150156
builder = OrderChainBuilder(suite)
151157

158+
if size is None:
159+
raise ValueError("Size is required")
160+
152161
if self.use_limit_entry:
153162
builder.limit_order(size=size, price=entry_price, side=side)
154163
else:
@@ -206,10 +215,10 @@ async def create_order(
206215
self,
207216
suite: "TradingSuite",
208217
side: int,
209-
size: int,
218+
size: Optional[int] = None,
210219
use_limit_entry: bool = False,
211220
entry_offset: float = 0,
212-
**kwargs,
221+
**kwargs: Any,
213222
) -> BracketOrderResponse:
214223
"""
215224
Create an order with ATR-based stops.
@@ -234,7 +243,7 @@ async def create_order(
234243
current_atr = float(data_with_atr[f"atr_{self.atr_period}"][-1])
235244

236245
# Get current price
237-
current_price = await suite.data.get_latest_price()
246+
current_price = await suite.data.get_current_price()
238247
if not current_price:
239248
raise ValueError("Cannot get current price")
240249

@@ -251,6 +260,9 @@ async def create_order(
251260
# Build order
252261
builder = OrderChainBuilder(suite)
253262

263+
if size is None:
264+
raise ValueError("Size is required")
265+
254266
if use_limit_entry:
255267
if side == 0: # BUY
256268
entry_price = current_price - entry_offset
@@ -296,11 +308,11 @@ async def create_order(
296308
self,
297309
suite: "TradingSuite",
298310
side: int,
299-
size: int,
311+
size: Optional[int] = None,
300312
breakout_level: Optional[float] = None,
301313
range_size: Optional[float] = None,
302314
lookback_bars: int = 20,
303-
**kwargs,
315+
**kwargs: Any,
304316
) -> BracketOrderResponse:
305317
"""
306318
Create a breakout order.
@@ -335,12 +347,16 @@ async def create_order(
335347
# Calculate entry price
336348
if side == 0: # BUY
337349
entry_price = breakout_level + self.breakout_offset
350+
if range_size is None:
351+
raise ValueError("Range size is required")
338352
stop_price = (
339353
breakout_level if self.stop_at_level else breakout_level - range_size
340354
)
341355
target_price = entry_price + (range_size * self.target_range_multiplier)
342356
else: # SELL
343357
entry_price = breakout_level - self.breakout_offset
358+
if range_size is None:
359+
raise ValueError("Range size is required")
344360
stop_price = (
345361
breakout_level if self.stop_at_level else breakout_level + range_size
346362
)
@@ -353,6 +369,9 @@ async def create_order(
353369
)
354370

355371
# Build order
372+
if size is None:
373+
raise ValueError("Size is required")
374+
356375
builder = (
357376
OrderChainBuilder(suite)
358377
.stop_order(size=size, price=entry_price, side=side)
@@ -396,9 +415,9 @@ async def create_order(
396415
self,
397416
suite: "TradingSuite",
398417
side: int,
399-
size: int,
418+
size: Optional[int] = None,
400419
check_spread: bool = True,
401-
**kwargs,
420+
**kwargs: Any,
402421
) -> BracketOrderResponse:
403422
"""
404423
Create a scalping order.
@@ -422,10 +441,10 @@ async def create_order(
422441
# Check spread if requested
423442
if check_spread and hasattr(suite, "orderbook") and suite.orderbook:
424443
orderbook = suite.orderbook
425-
spread_info = await orderbook.get_spread()
444+
spread = await orderbook.get_bid_ask_spread()
426445

427-
if spread_info:
428-
spread_ticks = spread_info["spread"] / tick_size
446+
if spread is not None:
447+
spread_ticks = spread / tick_size
429448
if spread_ticks > self.max_spread_ticks:
430449
raise ValueError(
431450
f"Spread too wide: {spread_ticks:.1f} ticks "
@@ -439,11 +458,14 @@ async def create_order(
439458
# Build order
440459
builder = OrderChainBuilder(suite)
441460

461+
if size is None:
462+
raise ValueError("Size is required")
463+
442464
if self.use_market_entry:
443465
builder.market_order(size=size, side=side)
444466
else:
445467
# Use limit at best bid/ask
446-
current_price = await suite.data.get_latest_price()
468+
current_price = await suite.data.get_current_price()
447469
if not current_price:
448470
raise ValueError("Cannot get current price")
449471
builder.limit_order(size=size, price=current_price, side=side)

src/project_x_py/order_tracker.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454

5555
import asyncio
5656
import logging
57-
from typing import TYPE_CHECKING, Any, Optional, Union
57+
from types import TracebackType
58+
from typing import TYPE_CHECKING, Any, Optional, Type, Union
5859

5960
from project_x_py.event_bus import EventType
6061
from project_x_py.models import BracketOrderResponse, Order, OrderPlaceResponse
@@ -113,7 +114,12 @@ async def __aenter__(self) -> "OrderTracker":
113114
await self._setup_event_handlers()
114115
return self
115116

116-
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
117+
async def __aexit__(
118+
self,
119+
exc_type: Optional[Type[BaseException]],
120+
exc_val: Optional[BaseException],
121+
exc_tb: Optional[TracebackType],
122+
) -> None:
117123
"""Exit the context manager and clean up."""
118124
await self.cleanup()
119125

@@ -212,7 +218,7 @@ async def wait_for_fill(self, timeout: float = 30.0) -> Order:
212218
return self._filled_order
213219
else:
214220
# Fetch latest order data
215-
order = await self.order_manager.get_order(self.order_id)
221+
order = await self.order_manager.get_order_by_id(self.order_id)
216222
if order and order.status == 2: # FILLED
217223
return order
218224
else:
@@ -249,7 +255,7 @@ async def wait_for_status(self, status: int, timeout: float = 30.0) -> Order:
249255

250256
# Check if already at target status
251257
if self._current_status == status:
252-
order = await self.order_manager.get_order(self.order_id)
258+
order = await self.order_manager.get_order_by_id(self.order_id)
253259
if order:
254260
return order
255261

@@ -260,7 +266,7 @@ async def wait_for_status(self, status: int, timeout: float = 30.0) -> Order:
260266
raise self._error
261267

262268
# Fetch latest order data
263-
order = await self.order_manager.get_order(self.order_id)
269+
order = await self.order_manager.get_order_by_id(self.order_id)
264270
if order and order.status == status:
265271
return order
266272
else:
@@ -457,9 +463,11 @@ async def execute(self) -> BracketOrderResponse:
457463
raise ValueError("Contract ID is required")
458464

459465
contract_id = self.contract_id or self.suite.instrument_id
466+
if not contract_id:
467+
raise ValueError("Contract ID is required")
460468

461469
# Calculate risk order prices if needed
462-
current_price = await self.suite.data.get_latest_price()
470+
current_price = await self.suite.data.get_current_price()
463471
if not current_price:
464472
raise ValueError("Cannot get current price for risk calculations")
465473

@@ -499,13 +507,15 @@ async def execute(self) -> BracketOrderResponse:
499507
bracket_entry_price = (
500508
entry_price if self.entry_type != "market" else current_price
501509
)
510+
assert self.side is not None # Already checked above
511+
assert self.size is not None # Already checked above
502512
result = await self.order_manager.place_bracket_order(
503513
contract_id=contract_id,
504514
side=self.side,
505515
size=self.size,
506516
entry_price=bracket_entry_price,
507-
stop_loss_price=stop_loss_price,
508-
take_profit_price=take_profit_price,
517+
stop_loss_price=stop_loss_price or 0.0,
518+
take_profit_price=take_profit_price or 0.0,
509519
entry_type=self.entry_type,
510520
)
511521

@@ -523,13 +533,17 @@ async def execute(self) -> BracketOrderResponse:
523533
contract_id=contract_id, side=self.side, size=self.size
524534
)
525535
elif self.entry_type == "limit":
536+
if self.entry_price is None:
537+
raise ValueError("Entry price is required for limit orders")
526538
response = await self.order_manager.place_limit_order(
527539
contract_id=contract_id,
528540
side=self.side,
529541
size=self.size,
530-
price=self.entry_price,
542+
limit_price=self.entry_price,
531543
)
532544
else: # stop
545+
if self.entry_price is None:
546+
raise ValueError("Entry price is required for stop orders")
533547
response = await self.order_manager.place_stop_order(
534548
contract_id=contract_id,
535549
side=self.side,

src/project_x_py/orderbook/memory.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,9 @@ async def get_memory_stats(self) -> OrderbookStats:
402402
spreads = [
403403
float(ask["price"]) - float(bid["price"])
404404
for bid, ask in zip(
405-
self.orderbook.best_bid_history, self.orderbook.best_ask_history
405+
self.orderbook.best_bid_history,
406+
self.orderbook.best_ask_history,
407+
strict=False,
406408
)
407409
if float(bid["price"]) > 0 and float(ask["price"]) > 0
408410
]

src/project_x_py/orderbook/profile.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -500,8 +500,8 @@ async def get_spread_analysis(
500500
spread_values = [s["spread"] for s in recent_spreads]
501501
current_spread = spread_values[-1]
502502
avg_spread = sum(spread_values) / len(spread_values)
503-
min_spread = min(spread_values)
504-
max_spread = max(spread_values)
503+
_min_spread = min(spread_values)
504+
_max_spread = max(spread_values)
505505

506506
# Calculate volatility
507507
variance = sum((s - avg_spread) ** 2 for s in spread_values) / len(
@@ -519,16 +519,16 @@ async def get_spread_analysis(
519519
)
520520

521521
if second_half_avg > first_half_avg * 1.1:
522-
spread_trend = "widening"
522+
_spread_trend = "widening"
523523
elif second_half_avg < first_half_avg * 0.9:
524-
spread_trend = "tightening"
524+
_spread_trend = "tightening"
525525
else:
526-
spread_trend = "stable"
526+
_spread_trend = "stable"
527527
else:
528-
spread_trend = "insufficient_data"
528+
_spread_trend = "insufficient_data"
529529

530530
# Calculate spread distribution
531-
spread_distribution = {
531+
_spread_distribution = {
532532
"tight": len([s for s in spread_values if s <= avg_spread * 0.8]),
533533
"normal": len(
534534
[
@@ -544,7 +544,7 @@ async def get_spread_analysis(
544544
current_time = datetime.now(self.orderbook.timezone)
545545

546546
# Get current orderbook state for liquidity metrics
547-
best_prices = self.orderbook._get_best_bid_ask_unlocked()
547+
_best_prices = self.orderbook._get_best_bid_ask_unlocked()
548548
bid_liquidity = 0.0
549549
ask_liquidity = 0.0
550550
total_liquidity = 0.0

src/project_x_py/position_manager/analytics.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"""
4747

4848
from datetime import datetime
49-
from typing import TYPE_CHECKING, Any
49+
from typing import TYPE_CHECKING
5050

5151
from project_x_py.models import Position
5252
from project_x_py.types.response_types import (
@@ -153,7 +153,7 @@ async def calculate_position_pnl(
153153
pnl_per_contract = price_change
154154

155155
unrealized_pnl = pnl_per_contract * position.size
156-
market_value = current_price * position.size
156+
_market_value = current_price * position.size
157157

158158
# Calculate additional fields for PositionAnalysisResponse
159159
position_value = abs(

src/project_x_py/position_manager/core.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676
from project_x_py.position_manager.risk import RiskManagementMixin
7777
from project_x_py.position_manager.tracking import PositionTrackingMixin
7878
from project_x_py.types.config_types import PositionManagerConfig
79-
from project_x_py.types.stats_types import PositionManagerStats
8079
from project_x_py.utils import (
8180
LogMessages,
8281
ProjectXLogger,

src/project_x_py/position_manager/reporting.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,10 @@ async def export_portfolio_report(
274274
"portfolio_summary": {
275275
"total_positions": len(positions),
276276
"total_pnl": pnl_data["total_pnl"],
277-
"total_exposure": risk_data["total_exposure"],
278-
"portfolio_risk": risk_data["portfolio_risk"],
277+
"total_exposure": risk_data["current_risk"],
278+
"portfolio_risk": risk_data["max_risk"],
279279
},
280-
"positions": pnl_data["positions"],
280+
"positions": positions,
281281
"risk_analysis": risk_data,
282282
"statistics": stats,
283283
"alerts": {

0 commit comments

Comments
 (0)