Skip to content

Commit

Permalink
feat: Add SignlePnL and OrderId support (#131)
Browse files Browse the repository at this point in the history
- adding the ability to set OrderId so we can place complex orders i.e
bracket order or other parent/child orders
- adding support to request single position PnL

---------

Co-authored-by: Chip Kent <[email protected]>
  • Loading branch information
Da5hes and chipkent authored Sep 11, 2024
1 parent b42069c commit 7840aaf
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 2 deletions.
37 changes: 35 additions & 2 deletions src/deephaven_ib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,8 @@ class IbSessionTws:
* **accounts_summary**: account summary. Automatically populated.
* **accounts_positions**: account positions. Automatically populated.
* **accounts_pnl**: account PNL. Automatically populated.
* **accounts_pnl_single**: single PNL. populated by calling request_single_pnl() on a specific contract.
####
# News
####
Expand Down Expand Up @@ -608,6 +609,15 @@ def deephaven_ib_parse_note(note:str, key:str) -> Optional[str]:
.move_columns_up(["RequestId", "ReceiveTime", "Account", "ModelCode"]) \
.drop_columns("Note") \
.last_by("RequestId"),
"accounts_pnl_single": tables_raw["raw_accounts_pnl_single"] \
.natural_join(tables_raw["raw_requests"], on="RequestId", joins="Note") \
.update([
"Account=(String)deephaven_ib_parse_note(Note,`account`)",
"ModelCode=(String)deephaven_ib_parse_note(Note,`model_code`)",
"ConId=(String)deephaven_ib_parse_note(Note,`conid`)"]) \
.move_columns_up(["RequestId", "ReceiveTime", "Account", "ModelCode", "ConId"]) \
.drop_columns("Note") \
.last_by("RequestId"),
"contracts_matching": tables_raw["raw_contracts_matching"] \
.natural_join(tables_raw["raw_requests"], on="RequestId", joins="Pattern=Note") \
.move_columns_up(["RequestId", "ReceiveTime", "Pattern"]) \
Expand Down Expand Up @@ -776,6 +786,24 @@ def request_account_positions(self, account: str, model_code: str = "") -> Reque
req_id = self._client.request_account_positions(account, model_code)
return Request(request_id=req_id)

def request_single_pnl(self, contract: RegisteredContract, account: str, model_code: str = "") -> Request:
"""Request PNL updates for a single position. Results are returned in the ``accounts_pnl_single`` table.
Args:
contract (RegisteredContract): contract data is requested for.
account (str): Account to request PNL for.
model_code (str): Model portfolio code to request PNL for.
Returns:
A Request.
Raises:
Exception: problem executing action.
"""
self._assert_connected()
req_id = self._client.request_single_pnl(account, model_code, contract.contract_details[0].contract.conId)
return Request(request_id=req_id)


####################################################################################################################
####################################################################################################################
Expand Down Expand Up @@ -1156,7 +1184,12 @@ def order_place(self, contract: RegisteredContract, order: Order) -> Request:
raise Exception(
f"RegisteredContracts with multiple contract details are not supported for orders: {contract}")

req_id = self._client.next_order_id()
if order.orderId == 0 or order.orderId is None:
req_id = self._client.next_order_id()
order.orderId = req_id
else:
req_id = order.orderId

cd = contract.contract_details[0]
self._client.log_request(req_id, "PlaceOrder", cd.contract, {"order": f"Order({order})"})
self._client.placeOrder(req_id, cd.contract, order)
Expand Down
38 changes: 38 additions & 0 deletions src/deephaven_ib/_tws/tws_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ def _build_table_writers() -> Dict[str, TableWriter]:
table_writers["accounts_pnl"] = TableWriter(
["RequestId", "DailyPnl", "UnrealizedPnl", "RealizedPnl"],
[dtypes.int64, dtypes.float64, dtypes.float64, dtypes.float64])

table_writers["accounts_pnl_single"] = TableWriter(
["RequestId", "Position", "DailyPnL", "UnrealizedPnL", "RealizedPnL", "Value"],
[dtypes.int64, dtypes.float64, dtypes.float64, dtypes.float64, dtypes.float64, dtypes.float64])

####
# News
Expand Down Expand Up @@ -590,6 +594,25 @@ def request_account_positions(self, account: str, model_code: str = "") -> int:
self.reqPositionsMulti(reqId=req_id, account=account, modelCode=model_code)
return req_id

def request_single_pnl(self, account: str, model_code: str, conid: int) -> int:
"""Request PNL updates for a single position. Results are returned in the `accounts_pnl_single` table.
Args:
account (str): Account to request PNL for.
model_code (str): Model portfolio code to request PNL for.
con_id (int): Contract ID of the position to request PNL for.
Returns:
Request ID
Raises:
Exception
"""

req_id = self.request_id_manager.next_id()
self.log_request(req_id, "PnlSingle", None, {"account": account, "model_code": model_code, "conid": conid})
self.reqPnLSingle(reqId=req_id, account=account, modelCode=model_code, conid=conid)
return req_id

####
# reqManagedAccts
Expand All @@ -605,6 +628,7 @@ def managedAccounts(self, accountsList: str):
self.request_account_pnl(account)
self.request_account_overview(account)
self.request_account_positions(account)


####
# reqFamilyCodes
Expand Down Expand Up @@ -698,6 +722,20 @@ def accountSummary(self, reqId: int, account: str, tag: str, value: str, currenc
self._table_writers["accounts_summary"].write_row([reqId, account, tag, value, currency])

####
# reqPnLSingle
####

def pnlSingle(self, reqId: int, pos: decimal.Decimal, dailyPnL: float, unrealizedPnL: float, realizedPnL: float, value: float):
EWrapper.pnlSingle(self, reqId, pos, dailyPnL, unrealizedPnL, realizedPnL, value)
self._table_writers["accounts_pnl_single"].write_row([
reqId,
pos,
dailyPnL,
unrealizedPnL,
realizedPnL,
value
])

# reqPositions
####

Expand Down

0 comments on commit 7840aaf

Please sign in to comment.