Skip to content

Commit 0a6c078

Browse files
committed
Create contract-from-params abstraction
The pattern of creating a specific concrete Contract type from generic contract arguments was repeated in many places, but we can just centralize it for cleaner logic and cleaner reuse. The goal here is to take things like Contract(secType="OPT", ...) generic contract containers and turn them into more specific Option(...) objects instead (or any of the other more specific Contract subclasses).
1 parent d9b8d57 commit 0a6c078

File tree

2 files changed

+11
-5
lines changed

2 files changed

+11
-5
lines changed

ib_async/contract.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ def create(**kwargs) -> "Contract":
137137

138138
return cls(**kwargs)
139139

140+
@staticmethod
141+
def recreate(c) -> "Contract":
142+
"""Comply an existing generic Contract into its most specific type."""
143+
return Contract.create(**util.dataclassAsDict(c))
144+
140145
def isHashable(self) -> bool:
141146
"""
142147
See if this contract can be hashed by conId.

ib_async/wrapper.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ def updatePortfolio(
481481
realizedPNL: float,
482482
account: str,
483483
):
484-
contract = Contract.create(**dataclassAsDict(contract))
484+
contract = Contract.recreate(contract)
485485
portfItem = PortfolioItem(
486486
contract,
487487
posSize,
@@ -505,7 +505,7 @@ def updatePortfolio(
505505
def position(
506506
self, account: str, contract: Contract, posSize: float, avgCost: float
507507
):
508-
contract = Contract.create(**dataclassAsDict(contract))
508+
contract = Contract.recreate(contract)
509509
position = Position(account, contract, posSize, avgCost)
510510
positions = self.positions[account]
511511

@@ -604,7 +604,7 @@ def openOrder(
604604
order = Order(
605605
**{k: v for k, v in dataclassAsDict(order).items() if v != "?"}
606606
)
607-
contract = Contract.create(**dataclassAsDict(contract))
607+
contract = Contract.recreate(contract)
608608
orderStatus = OrderStatus(orderId=orderId, status=orderState.status)
609609
trade = Trade(contract, order, orderStatus, [], [])
610610
self.trades[key] = trade
@@ -628,7 +628,7 @@ def openOrderEnd(self):
628628
self._endReq("openOrders")
629629

630630
def completedOrder(self, contract: Contract, order: Order, orderState: OrderState):
631-
contract = Contract.create(**dataclassAsDict(contract))
631+
contract = Contract.recreate(contract)
632632
orderStatus = OrderStatus(orderId=order.orderId, status=orderState.status)
633633
trade = Trade(contract, order, orderStatus, [], [])
634634
self._results["completedOrders"].append(trade)
@@ -720,10 +720,11 @@ def execDetails(self, reqId: int, contract: Contract, execution: Execution):
720720
key = self.orderKey(execution.clientId, execution.orderId, execution.permId)
721721
trade = self.trades.get(key)
722722

723+
# TODO: debug why spread contracts aren't fully detailed here. They have no legs in execDetails, but they do in orderStatus?
723724
if trade and contract == trade.contract:
724725
contract = trade.contract
725726
else:
726-
contract = Contract.create(**dataclassAsDict(contract))
727+
contract = Contract.recreate(contract)
727728

728729
execId = execution.execId
729730
isLive = reqId not in self._futures

0 commit comments

Comments
 (0)