Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 69 additions & 37 deletions ibflex/Types.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class decorator. Class attributes are annotated with PEP 484 type hints.
"SymbolSummary",
"AssetSummary",
"Order",
"StockGrantActivity",
]

import datetime
Expand Down Expand Up @@ -187,7 +188,7 @@ class FlexStatement(FlexElement):
ConversionRates: Tuple["ConversionRate", ...] = ()
HKIPOOpenSubscriptions: Tuple = () # TODO
CommissionCredits: Tuple = () # TODO
StockGrantActivities: Tuple = () # TODO
StockGrantActivities: Tuple["StockGrantActivity", ...] = ()
SLBCollaterals: Tuple = () # TODO
IncentiveCouponAccrualDetails: Tuple = () # TODO
DepositsOnHold: Tuple = () # TODO
Expand Down Expand Up @@ -1298,7 +1299,6 @@ class Lot(FlexElement):
relatedTradeID: Optional[str] = None
rtn: Optional[str] = None
initialInvestment: Optional[decimal.Decimal] = None
positionActionID: Optional[str] = None


@dataclass(frozen=True)
Expand Down Expand Up @@ -1429,39 +1429,6 @@ class SymbolSummary(FlexElement):
relatedTradeID: Optional[str] = None
origTransactionID: Optional[str] = None
relatedTransactionID: Optional[str] = None
positionActionID: Optional[str] = None
changeInPrice: Optional[decimal.Decimal] = None
changeInQuantity: Optional[decimal.Decimal] = None
closePrice: Optional[decimal.Decimal] = None
commodityType: Optional[str] = None
cost: Optional[decimal.Decimal] = None
deliveryType: Optional[str] = None
exchOrderId: Optional[str] = None
extExecID: Optional[str] = None
fifoPnlRealized: Optional[decimal.Decimal] = None
fineness: Optional[decimal.Decimal] = None
holdingPeriodDateTime: Optional[datetime.datetime] = None
ibCommission: Optional[decimal.Decimal] = None
ibCommissionCurrency: Optional[str] = None
ibExecID: Optional[str] = None
ibOrderID: Optional[str] = None
initialInvestment: Optional[decimal.Decimal] = None
mtmPnl: Optional[decimal.Decimal] = None
netCash: Optional[decimal.Decimal] = None
notes: Optional[str] = None
openCloseIndicator: Optional[enums.OpenClose] = None
openDateTime: Optional[datetime.datetime] = None
origOrderID: Optional[str] = None
rtn: Optional[str] = None
serialNumber: Optional[str] = None
settleDateTarget: Optional[datetime.date] = None
taxes: Optional[decimal.Decimal] = None
tradeMoney: Optional[decimal.Decimal] = None
tradePrice: Optional[decimal.Decimal] = None
transactionID: Optional[str] = None
weight: Optional[str] = None
whenRealized: Optional[datetime.datetime] = None
whenReopened: Optional[datetime.datetime] = None


@dataclass(frozen=True)
Expand Down Expand Up @@ -1571,7 +1538,6 @@ class AssetSummary(FlexElement):
relatedTransactionID: Optional[str] = None
rtn: Optional[str] = None
initialInvestment: Optional[decimal.Decimal] = None
positionActionID: Optional[str] = None


@dataclass(frozen=True)
Expand Down Expand Up @@ -1682,7 +1648,6 @@ class Order(FlexElement):
weight: Optional[str] = None
positionActionID: Optional[str] = None


@dataclass(frozen=True)
class TradeConfirm(FlexElement):
"""Wrapped in <TradeConfirms>"""
Expand Down Expand Up @@ -2120,13 +2085,15 @@ class Transfer(FlexElement):
securityID: Optional[str] = None
cusip: Optional[str] = None
isin: Optional[str] = None
figi: Optional[str] = None
listingExchange: Optional[str] = None
underlyingSecurityID: Optional[str] = None
underlyingListingExchange: Optional[str] = None
reportDate: Optional[datetime.date] = None
underlyingConid: Optional[str] = None
date: Optional[datetime.date] = None
dateTime: Optional[datetime.datetime] = None
settleDate: Optional[datetime.date] = None
account: Optional[str] = None
deliveringBroker: Optional[str] = None
quantity: Optional[decimal.Decimal] = None
Expand All @@ -2143,6 +2110,7 @@ class Transfer(FlexElement):
securityIDType: Optional[str] = None
underlyingSymbol: Optional[str] = None
issuer: Optional[str] = None
issuerCountryCode: Optional[str] = None
multiplier: Optional[decimal.Decimal] = None
strike: Optional[decimal.Decimal] = None
expiry: Optional[datetime.date] = None
Expand All @@ -2154,6 +2122,9 @@ class Transfer(FlexElement):
pnlAmountInBase: Optional[decimal.Decimal] = None
fxPnl: Optional[decimal.Decimal] = None
transactionID: Optional[str] = None
levelOfDetail: Optional[str] = None
positionInstructionID: Optional[str] = None # Took a punt on the type here because not seen in XML data
positionInstructionSetID: Optional[str] = None # Took a punt on the type here because not seen in XML data
serialNumber: Optional[str] = None
deliveryType: Optional[str] = None
commodityType: Optional[str] = None
Expand Down Expand Up @@ -2282,6 +2253,10 @@ class CorporateAction(FlexElement):
commodityType: Optional[str] = None
fineness: Optional[decimal.Decimal] = None
weight: Optional[str] = None
figi: Optional[str] = None
issuerCountryCode: Optional[str] = None
costBasis: Optional[decimal.Decimal] = None
realizedPL: Optional[decimal.Decimal] = None


@dataclass(frozen=True)
Expand Down Expand Up @@ -2727,6 +2702,17 @@ class TransactionTax(FlexElement):
source: Optional[str] = None
code: Tuple[enums.Code, ...] = ()
levelOfDetail: Optional[str] = None
subCategory: Optional[str] = None
figi: Optional[str] = None
issuerCountryCode: Optional[str] = None
settleDate: Optional[datetime.date] = None
orderId: Optional[str] = None
serialNumber: Optional[str] = None
deliveryType: Optional[str] = None
commodityType: Optional[str] = None
fineness: Optional[decimal.Decimal] = None
weight: Optional[str] = None



@dataclass(frozen=True)
Expand Down Expand Up @@ -2856,5 +2842,51 @@ class SLBOpenContract(FlexElement):
fineness: Optional[decimal.Decimal] = None
weight: Optional[decimal.Decimal] = None

@dataclass(frozen=True)
class StockGrantActivity(FlexElement):
"""Wrapped in <StockGrantActivities>"""

accountId: Optional[str] = None
acctAlias: Optional[str] = None
model: Optional[str] = None
currency: Optional[str] = None
fxRateToBase: Optional[decimal.Decimal] = None
assetCategory: Optional[enums.AssetClass] = None # Called 'AssetClass; in the configuration form # Mentioned in the report configuration screen but not seen in the XML data
subCategory: Optional[str] = None
symbol: Optional[str] = None # symbol of instrument traded, e.g. AAPL, not unique in IBKR as it can exist on different exchanges: (symbol, Exchange, Currency, Asset Type) is unique
description: Optional[str] = None
conid: Optional[str] = None # IBKR identifier of instrument, unique key within IBKR
securityID: Optional[str] = None
securityIDType: Optional[str] = None
cusip: Optional[str] = None
isin: Optional[str] = None
figi: Optional[str] = None
listingExchange: Optional[str] = None
underlyingConid: Optional[str] = None
underlyingSymbol: Optional[str] = None
underlyingSecurityID: Optional[str] = None
underlyingListingExchange: Optional[str] = None
issuer: Optional[str] = None
issuerCountryCode: Optional[str] = None
multiplier: Optional[decimal.Decimal] = None
strike: Optional[decimal.Decimal] = None
expiry: Optional[datetime.date] = None
putCall: Optional[enums.PutCall] = None
principalAdjustFactor: Optional[decimal.Decimal] = None
reportDate: Optional[datetime.date] = None
activityDescription: Optional[str] = None
awardDate: Optional[datetime.date] = None
vestingDate: Optional[datetime.date] = None
quantity: Optional[decimal.Decimal] = None
price: Optional[decimal.Decimal] = None
value: Optional[decimal.Decimal] = None
serialNumber: Optional[str] = None
deliveryType: Optional[str] = None
commodityType: Optional[str] = None
fineness: Optional[decimal.Decimal] = None
weight: Optional[decimal.Decimal] = None



# Type alias to work around https://github.com/python/mypy/issues/1775
_ClientFeesDetail = ClientFeesDetail
61 changes: 61 additions & 0 deletions tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,67 @@ def testParse(self):
self.assertEqual(instance.mtmPnl, decimal.Decimal("-118.00"))
self.assertEqual(instance.tradeID, None)

class StockGrantActivityTestCase(unittest.TestCase):
data = ET.fromstring(
(
'<StockGrantActivity accountId="U123456" acctAlias="" model="" currency="USD" '
'fxRateToBase="0.73459" assetCategory="STK" subCategory="COMMON" '
'symbol="IBKR" description="INTERACTIVE BROKERS GRO-CL A" conid="43645865" '
'securityID="US45841N1072" securityIDType="ISIN" cusip="45841N107" '
'isin="US45841N1072" figi="BBG000LV0836" listingExchange="NASDAQ" '
'underlyingConid="" underlyingSymbol="IBKR" underlyingSecurityID="" '
'underlyingListingExchange="" issuer="" issuerCountryCode="US" '
'multiplier="1" strike="" expiry="" putCall="" principalAdjustFactor="" '
'reportDate="2025-06-12" '
'activityDescription="Stock Award Grant for Cash Deposit" '
'awardDate="2025-06-12" vestingDate="2026-06-12" quantity="2.6454" '
'price="204.51" value="541.01" serialNumber="" deliveryType="" '
'commodityType="" fineness="0.0" weight="0.0"/>'
)
)

def testParse(self):
instance = parser.parse_data_element(self.data)
self.assertIsInstance(instance, Types.StockGrantActivity)
self.assertEqual(instance.accountId, "U123456")
self.assertEqual(instance.acctAlias, None)
self.assertEqual(instance.model, None)
self.assertEqual(instance.currency, "USD")
self.assertEqual(instance.fxRateToBase, decimal.Decimal("0.73459"))
self.assertEqual(instance.assetCategory, enums.AssetClass.STOCK)
self.assertEqual(instance.subCategory, "COMMON")
self.assertEqual(instance.symbol, "IBKR")
self.assertEqual(instance.description, "INTERACTIVE BROKERS GRO-CL A")
self.assertEqual(instance.conid, "43645865")
self.assertEqual(instance.securityID, "US45841N1072")
self.assertEqual(instance.securityIDType, "ISIN")
self.assertEqual(instance.cusip, "45841N107")
self.assertEqual(instance.isin, "US45841N1072")
self.assertEqual(instance.figi, "BBG000LV0836")
self.assertEqual(instance.listingExchange, "NASDAQ")
self.assertEqual(instance.underlyingConid, None)
self.assertEqual(instance.underlyingSymbol, "IBKR")
self.assertEqual(instance.underlyingSecurityID, None)
self.assertEqual(instance.underlyingListingExchange, None)
self.assertEqual(instance.issuer, None)
self.assertEqual(instance.issuerCountryCode, "US")
self.assertEqual(instance.multiplier, decimal.Decimal("1"))
self.assertEqual(instance.strike, None)
self.assertEqual(instance.expiry, None)
self.assertEqual(instance.putCall, None)
self.assertEqual(instance.principalAdjustFactor, None)
self.assertEqual(instance.reportDate, datetime.date(2025,6,12))
self.assertEqual(instance.activityDescription, "Stock Award Grant for Cash Deposit")
self.assertEqual(instance.awardDate, datetime.date(2025,6,12))
self.assertEqual(instance.vestingDate, datetime.date(2026,6,12))
self.assertEqual(instance.quantity, decimal.Decimal("2.6454"))
self.assertEqual(instance.price, decimal.Decimal("204.51"))
self.assertEqual(instance.value, decimal.Decimal("541.01"))
self.assertEqual(instance.serialNumber, None)
self.assertEqual(instance.deliveryType, None)
self.assertEqual(instance.commodityType, None)
self.assertEqual(instance.fineness, decimal.Decimal("0.0"))
self.assertEqual(instance.weight, decimal.Decimal("0.0"))

if __name__ == '__main__':
unittest.main(verbosity=3)