From da6a7dc9878adf6131a4bc990880f014a398ee8f Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Mon, 27 Oct 2025 17:23:17 +0100 Subject: [PATCH 01/48] Implement protobuf works with protobuf ONLY, requires 10.40 or greater --- .gitignore | 3 + README.md | 19 +- ib_async/client.py | 563 +++++--- ib_async/connection.py | 1 + ib_async/contract.py | 70 +- ib_async/decoder.py | 1236 ++++------------- ib_async/ib.py | 369 +++-- ib_async/message.py | 203 +++ ib_async/objects.py | 21 + ib_async/order.py | 20 +- ib_async/protobuf/AccountDataEnd_pb2.py | 37 + ib_async/protobuf/AccountDataEnd_pb2.pyi | 11 + ib_async/protobuf/AccountDataRequest_pb2.py | 37 + ib_async/protobuf/AccountDataRequest_pb2.pyi | 13 + ib_async/protobuf/AccountSummaryEnd_pb2.py | 37 + ib_async/protobuf/AccountSummaryEnd_pb2.pyi | 11 + .../protobuf/AccountSummaryRequest_pb2.py | 37 + .../protobuf/AccountSummaryRequest_pb2.pyi | 15 + ib_async/protobuf/AccountSummary_pb2.py | 37 + ib_async/protobuf/AccountSummary_pb2.pyi | 19 + .../protobuf/AccountUpdateMultiEnd_pb2.py | 37 + .../protobuf/AccountUpdateMultiEnd_pb2.pyi | 11 + ib_async/protobuf/AccountUpdateMulti_pb2.py | 37 + ib_async/protobuf/AccountUpdateMulti_pb2.pyi | 21 + ib_async/protobuf/AccountUpdateTime_pb2.py | 37 + ib_async/protobuf/AccountUpdateTime_pb2.pyi | 11 + .../AccountUpdatesMultiRequest_pb2.py | 37 + .../AccountUpdatesMultiRequest_pb2.pyi | 17 + ib_async/protobuf/AccountValue_pb2.py | 37 + ib_async/protobuf/AccountValue_pb2.pyi | 17 + ib_async/protobuf/AllOpenOrdersRequest_pb2.py | 37 + .../protobuf/AllOpenOrdersRequest_pb2.pyi | 9 + .../protobuf/AutoOpenOrdersRequest_pb2.py | 37 + .../protobuf/AutoOpenOrdersRequest_pb2.pyi | 11 + .../CalculateImpliedVolatilityRequest_pb2.py | 42 + .../CalculateImpliedVolatilityRequest_pb2.pyi | 29 + .../CalculateOptionPriceRequest_pb2.py | 42 + .../CalculateOptionPriceRequest_pb2.pyi | 29 + ib_async/protobuf/CancelAccountSummary_pb2.py | 37 + .../protobuf/CancelAccountSummary_pb2.pyi | 11 + .../protobuf/CancelAccountUpdatesMulti_pb2.py | 37 + .../CancelAccountUpdatesMulti_pb2.pyi | 11 + .../CancelCalculateImpliedVolatility_pb2.py | 37 + .../CancelCalculateImpliedVolatility_pb2.pyi | 11 + .../CancelCalculateOptionPrice_pb2.py | 37 + .../CancelCalculateOptionPrice_pb2.pyi | 11 + ib_async/protobuf/CancelContractData_pb2.py | 37 + ib_async/protobuf/CancelContractData_pb2.pyi | 11 + .../protobuf/CancelFundamentalsData_pb2.py | 37 + .../protobuf/CancelFundamentalsData_pb2.pyi | 11 + ib_async/protobuf/CancelHeadTimestamp_pb2.py | 37 + ib_async/protobuf/CancelHeadTimestamp_pb2.pyi | 11 + ib_async/protobuf/CancelHistogramData_pb2.py | 37 + ib_async/protobuf/CancelHistogramData_pb2.pyi | 11 + ib_async/protobuf/CancelHistoricalData_pb2.py | 37 + .../protobuf/CancelHistoricalData_pb2.pyi | 11 + .../protobuf/CancelHistoricalTicks_pb2.py | 37 + .../protobuf/CancelHistoricalTicks_pb2.pyi | 11 + ib_async/protobuf/CancelMarketData_pb2.py | 37 + ib_async/protobuf/CancelMarketData_pb2.pyi | 11 + ib_async/protobuf/CancelMarketDepth_pb2.py | 37 + ib_async/protobuf/CancelMarketDepth_pb2.pyi | 13 + ib_async/protobuf/CancelNewsBulletins_pb2.py | 37 + ib_async/protobuf/CancelNewsBulletins_pb2.pyi | 9 + ib_async/protobuf/CancelOrderRequest_pb2.py | 38 + ib_async/protobuf/CancelOrderRequest_pb2.pyi | 15 + ib_async/protobuf/CancelPnLSingle_pb2.py | 37 + ib_async/protobuf/CancelPnLSingle_pb2.pyi | 11 + ib_async/protobuf/CancelPnL_pb2.py | 37 + ib_async/protobuf/CancelPnL_pb2.pyi | 11 + ib_async/protobuf/CancelPositionsMulti_pb2.py | 37 + .../protobuf/CancelPositionsMulti_pb2.pyi | 11 + ib_async/protobuf/CancelPositions_pb2.py | 37 + ib_async/protobuf/CancelPositions_pb2.pyi | 9 + ib_async/protobuf/CancelRealTimeBars_pb2.py | 37 + ib_async/protobuf/CancelRealTimeBars_pb2.pyi | 11 + .../protobuf/CancelScannerSubscription_pb2.py | 37 + .../CancelScannerSubscription_pb2.pyi | 11 + ib_async/protobuf/CancelTickByTick_pb2.py | 37 + ib_async/protobuf/CancelTickByTick_pb2.pyi | 11 + ib_async/protobuf/CancelWshEventData_pb2.py | 37 + ib_async/protobuf/CancelWshEventData_pb2.pyi | 11 + ib_async/protobuf/CancelWshMetaData_pb2.py | 37 + ib_async/protobuf/CancelWshMetaData_pb2.pyi | 11 + ib_async/protobuf/ComboLeg_pb2.py | 37 + ib_async/protobuf/ComboLeg_pb2.pyi | 27 + .../protobuf/CommissionAndFeesReport_pb2.py | 37 + .../protobuf/CommissionAndFeesReport_pb2.pyi | 21 + ib_async/protobuf/CompletedOrder_pb2.py | 40 + ib_async/protobuf/CompletedOrder_pb2.pyi | 19 + ib_async/protobuf/CompletedOrdersEnd_pb2.py | 37 + ib_async/protobuf/CompletedOrdersEnd_pb2.pyi | 9 + .../protobuf/CompletedOrdersRequest_pb2.py | 37 + .../protobuf/CompletedOrdersRequest_pb2.pyi | 11 + ib_async/protobuf/ContractDataEnd_pb2.py | 37 + ib_async/protobuf/ContractDataEnd_pb2.pyi | 11 + ib_async/protobuf/ContractDataRequest_pb2.py | 38 + ib_async/protobuf/ContractDataRequest_pb2.pyi | 15 + ib_async/protobuf/ContractData_pb2.py | 39 + ib_async/protobuf/ContractData_pb2.pyi | 18 + ib_async/protobuf/ContractDescription_pb2.py | 38 + ib_async/protobuf/ContractDescription_pb2.pyi | 16 + ib_async/protobuf/ContractDetails_pb2.py | 42 + ib_async/protobuf/ContractDetails_pb2.pyi | 141 ++ ib_async/protobuf/Contract_pb2.py | 39 + ib_async/protobuf/Contract_pb2.pyi | 55 + .../CurrentTimeInMillisRequest_pb2.py | 37 + .../CurrentTimeInMillisRequest_pb2.pyi | 9 + ib_async/protobuf/CurrentTimeInMillis_pb2.py | 37 + ib_async/protobuf/CurrentTimeInMillis_pb2.pyi | 11 + ib_async/protobuf/CurrentTimeRequest_pb2.py | 37 + ib_async/protobuf/CurrentTimeRequest_pb2.pyi | 9 + ib_async/protobuf/CurrentTime_pb2.py | 37 + ib_async/protobuf/CurrentTime_pb2.pyi | 11 + ib_async/protobuf/DeltaNeutralContract_pb2.py | 37 + .../protobuf/DeltaNeutralContract_pb2.pyi | 15 + .../DepthMarketDataDescription_pb2.py | 37 + .../DepthMarketDataDescription_pb2.pyi | 19 + ib_async/protobuf/DisplayGroupList_pb2.py | 37 + ib_async/protobuf/DisplayGroupList_pb2.pyi | 13 + ib_async/protobuf/DisplayGroupUpdated_pb2.py | 37 + ib_async/protobuf/DisplayGroupUpdated_pb2.pyi | 13 + ib_async/protobuf/ErrorMessage_pb2.py | 37 + ib_async/protobuf/ErrorMessage_pb2.pyi | 19 + ib_async/protobuf/ExecutionDetailsEnd_pb2.py | 37 + ib_async/protobuf/ExecutionDetailsEnd_pb2.pyi | 11 + ib_async/protobuf/ExecutionDetails_pb2.py | 39 + ib_async/protobuf/ExecutionDetails_pb2.pyi | 18 + ib_async/protobuf/ExecutionFilter_pb2.py | 37 + ib_async/protobuf/ExecutionFilter_pb2.pyi | 29 + ib_async/protobuf/ExecutionRequest_pb2.py | 38 + ib_async/protobuf/ExecutionRequest_pb2.pyi | 15 + ib_async/protobuf/Execution_pb2.py | 37 + ib_async/protobuf/Execution_pb2.pyi | 51 + .../protobuf/ExerciseOptionsRequest_pb2.py | 38 + .../protobuf/ExerciseOptionsRequest_pb2.pyi | 29 + ib_async/protobuf/FAReplace_pb2.py | 37 + ib_async/protobuf/FAReplace_pb2.pyi | 15 + ib_async/protobuf/FARequest_pb2.py | 37 + ib_async/protobuf/FARequest_pb2.pyi | 11 + ib_async/protobuf/FamilyCode_pb2.py | 37 + ib_async/protobuf/FamilyCode_pb2.pyi | 13 + ib_async/protobuf/FamilyCodesRequest_pb2.py | 37 + ib_async/protobuf/FamilyCodesRequest_pb2.pyi | 9 + ib_async/protobuf/FamilyCodes_pb2.py | 38 + ib_async/protobuf/FamilyCodes_pb2.pyi | 14 + .../protobuf/FundamentalsDataRequest_pb2.py | 42 + .../protobuf/FundamentalsDataRequest_pb2.pyi | 27 + ib_async/protobuf/FundamentalsData_pb2.py | 37 + ib_async/protobuf/FundamentalsData_pb2.pyi | 13 + ib_async/protobuf/GlobalCancelRequest_pb2.py | 38 + ib_async/protobuf/GlobalCancelRequest_pb2.pyi | 13 + ib_async/protobuf/HeadTimestampRequest_pb2.py | 38 + .../protobuf/HeadTimestampRequest_pb2.pyi | 21 + ib_async/protobuf/HeadTimestamp_pb2.py | 37 + ib_async/protobuf/HeadTimestamp_pb2.pyi | 13 + ib_async/protobuf/HistogramDataEntry_pb2.py | 37 + ib_async/protobuf/HistogramDataEntry_pb2.pyi | 13 + ib_async/protobuf/HistogramDataRequest_pb2.py | 38 + .../protobuf/HistogramDataRequest_pb2.pyi | 19 + ib_async/protobuf/HistogramData_pb2.py | 38 + ib_async/protobuf/HistogramData_pb2.pyi | 16 + ib_async/protobuf/HistoricalDataBar_pb2.py | 37 + ib_async/protobuf/HistoricalDataBar_pb2.pyi | 25 + ib_async/protobuf/HistoricalDataEnd_pb2.py | 37 + ib_async/protobuf/HistoricalDataEnd_pb2.pyi | 15 + .../protobuf/HistoricalDataRequest_pb2.py | 42 + .../protobuf/HistoricalDataRequest_pb2.pyi | 39 + ib_async/protobuf/HistoricalDataUpdate_pb2.py | 38 + .../protobuf/HistoricalDataUpdate_pb2.pyi | 15 + ib_async/protobuf/HistoricalData_pb2.py | 38 + ib_async/protobuf/HistoricalData_pb2.pyi | 16 + ib_async/protobuf/HistoricalNewsEnd_pb2.py | 37 + ib_async/protobuf/HistoricalNewsEnd_pb2.pyi | 13 + .../protobuf/HistoricalNewsRequest_pb2.py | 41 + .../protobuf/HistoricalNewsRequest_pb2.pyi | 32 + ib_async/protobuf/HistoricalNews_pb2.py | 37 + ib_async/protobuf/HistoricalNews_pb2.pyi | 19 + ib_async/protobuf/HistoricalSchedule_pb2.py | 38 + ib_async/protobuf/HistoricalSchedule_pb2.pyi | 22 + ib_async/protobuf/HistoricalSession_pb2.py | 37 + ib_async/protobuf/HistoricalSession_pb2.pyi | 15 + ib_async/protobuf/HistoricalTickBidAsk_pb2.py | 38 + .../protobuf/HistoricalTickBidAsk_pb2.pyi | 23 + ib_async/protobuf/HistoricalTickLast_pb2.py | 38 + ib_async/protobuf/HistoricalTickLast_pb2.pyi | 23 + ib_async/protobuf/HistoricalTick_pb2.py | 37 + ib_async/protobuf/HistoricalTick_pb2.pyi | 15 + .../protobuf/HistoricalTicksBidAsk_pb2.py | 38 + .../protobuf/HistoricalTicksBidAsk_pb2.pyi | 18 + ib_async/protobuf/HistoricalTicksLast_pb2.py | 38 + ib_async/protobuf/HistoricalTicksLast_pb2.pyi | 18 + .../protobuf/HistoricalTicksRequest_pb2.py | 42 + .../protobuf/HistoricalTicksRequest_pb2.pyi | 37 + ib_async/protobuf/HistoricalTicks_pb2.py | 38 + ib_async/protobuf/HistoricalTicks_pb2.pyi | 18 + ib_async/protobuf/IdsRequest_pb2.py | 37 + ib_async/protobuf/IdsRequest_pb2.pyi | 11 + ib_async/protobuf/IneligibilityReason_pb2.py | 37 + ib_async/protobuf/IneligibilityReason_pb2.pyi | 13 + .../protobuf/ManagedAccountsRequest_pb2.py | 37 + .../protobuf/ManagedAccountsRequest_pb2.pyi | 9 + ib_async/protobuf/ManagedAccounts_pb2.py | 37 + ib_async/protobuf/ManagedAccounts_pb2.pyi | 11 + ib_async/protobuf/MarketDataRequest_pb2.py | 42 + ib_async/protobuf/MarketDataRequest_pb2.pyi | 31 + .../protobuf/MarketDataTypeRequest_pb2.py | 37 + .../protobuf/MarketDataTypeRequest_pb2.pyi | 11 + ib_async/protobuf/MarketDataType_pb2.py | 37 + ib_async/protobuf/MarketDataType_pb2.pyi | 13 + ib_async/protobuf/MarketDepthData_pb2.py | 37 + ib_async/protobuf/MarketDepthData_pb2.pyi | 23 + .../MarketDepthExchangesRequest_pb2.py | 37 + .../MarketDepthExchangesRequest_pb2.pyi | 9 + ib_async/protobuf/MarketDepthExchanges_pb2.py | 38 + .../protobuf/MarketDepthExchanges_pb2.pyi | 14 + ib_async/protobuf/MarketDepthL2_pb2.py | 38 + ib_async/protobuf/MarketDepthL2_pb2.pyi | 15 + ib_async/protobuf/MarketDepthRequest_pb2.py | 42 + ib_async/protobuf/MarketDepthRequest_pb2.pyi | 29 + ib_async/protobuf/MarketDepth_pb2.py | 38 + ib_async/protobuf/MarketDepth_pb2.pyi | 15 + ib_async/protobuf/MarketRuleRequest_pb2.py | 37 + ib_async/protobuf/MarketRuleRequest_pb2.pyi | 11 + ib_async/protobuf/MarketRule_pb2.py | 38 + ib_async/protobuf/MarketRule_pb2.pyi | 16 + .../protobuf/MatchingSymbolsRequest_pb2.py | 37 + .../protobuf/MatchingSymbolsRequest_pb2.pyi | 13 + ib_async/protobuf/NewsArticleRequest_pb2.py | 41 + ib_async/protobuf/NewsArticleRequest_pb2.pyi | 26 + ib_async/protobuf/NewsArticle_pb2.py | 37 + ib_async/protobuf/NewsArticle_pb2.pyi | 15 + ib_async/protobuf/NewsBulletin_pb2.py | 37 + ib_async/protobuf/NewsBulletin_pb2.pyi | 17 + ib_async/protobuf/NewsBulletinsRequest_pb2.py | 37 + .../protobuf/NewsBulletinsRequest_pb2.pyi | 11 + ib_async/protobuf/NewsProvider_pb2.py | 37 + ib_async/protobuf/NewsProvider_pb2.pyi | 13 + ib_async/protobuf/NewsProvidersRequest_pb2.py | 37 + .../protobuf/NewsProvidersRequest_pb2.pyi | 9 + ib_async/protobuf/NewsProviders_pb2.py | 38 + ib_async/protobuf/NewsProviders_pb2.pyi | 14 + ib_async/protobuf/NextValidId_pb2.py | 37 + ib_async/protobuf/NextValidId_pb2.pyi | 11 + ib_async/protobuf/OpenOrder_pb2.py | 40 + ib_async/protobuf/OpenOrder_pb2.pyi | 21 + ib_async/protobuf/OpenOrdersEnd_pb2.py | 37 + ib_async/protobuf/OpenOrdersEnd_pb2.pyi | 9 + ib_async/protobuf/OpenOrdersRequest_pb2.py | 37 + ib_async/protobuf/OpenOrdersRequest_pb2.pyi | 9 + ib_async/protobuf/OrderAllocation_pb2.py | 37 + ib_async/protobuf/OrderAllocation_pb2.pyi | 23 + ib_async/protobuf/OrderBound_pb2.py | 37 + ib_async/protobuf/OrderBound_pb2.pyi | 15 + ib_async/protobuf/OrderCancel_pb2.py | 37 + ib_async/protobuf/OrderCancel_pb2.pyi | 15 + ib_async/protobuf/OrderCondition_pb2.py | 37 + ib_async/protobuf/OrderCondition_pb2.pyi | 35 + ib_async/protobuf/OrderState_pb2.py | 38 + ib_async/protobuf/OrderState_pb2.pyi | 72 + ib_async/protobuf/OrderStatus_pb2.py | 37 + ib_async/protobuf/OrderStatus_pb2.pyi | 31 + ib_async/protobuf/Order_pb2.py | 51 + ib_async/protobuf/Order_pb2.pyi | 308 ++++ ib_async/protobuf/PlaceOrderRequest_pb2.py | 39 + ib_async/protobuf/PlaceOrderRequest_pb2.pyi | 18 + ib_async/protobuf/PnLRequest_pb2.py | 37 + ib_async/protobuf/PnLRequest_pb2.pyi | 15 + ib_async/protobuf/PnLSingleRequest_pb2.py | 37 + ib_async/protobuf/PnLSingleRequest_pb2.pyi | 17 + ib_async/protobuf/PnLSingle_pb2.py | 37 + ib_async/protobuf/PnLSingle_pb2.pyi | 21 + ib_async/protobuf/PnL_pb2.py | 37 + ib_async/protobuf/PnL_pb2.pyi | 17 + ib_async/protobuf/PortfolioValue_pb2.py | 38 + ib_async/protobuf/PortfolioValue_pb2.pyi | 27 + ib_async/protobuf/PositionEnd_pb2.py | 37 + ib_async/protobuf/PositionEnd_pb2.pyi | 9 + ib_async/protobuf/PositionMultiEnd_pb2.py | 37 + ib_async/protobuf/PositionMultiEnd_pb2.pyi | 11 + ib_async/protobuf/PositionMulti_pb2.py | 38 + ib_async/protobuf/PositionMulti_pb2.pyi | 23 + ib_async/protobuf/Position_pb2.py | 38 + ib_async/protobuf/Position_pb2.pyi | 19 + .../protobuf/PositionsMultiRequest_pb2.py | 37 + .../protobuf/PositionsMultiRequest_pb2.pyi | 15 + ib_async/protobuf/PositionsRequest_pb2.py | 37 + ib_async/protobuf/PositionsRequest_pb2.pyi | 9 + ib_async/protobuf/PriceIncrement_pb2.py | 37 + ib_async/protobuf/PriceIncrement_pb2.pyi | 13 + .../protobuf/QueryDisplayGroupsRequest_pb2.py | 37 + .../QueryDisplayGroupsRequest_pb2.pyi | 11 + ib_async/protobuf/RealTimeBarTick_pb2.py | 37 + ib_async/protobuf/RealTimeBarTick_pb2.pyi | 27 + ib_async/protobuf/RealTimeBarsRequest_pb2.py | 42 + ib_async/protobuf/RealTimeBarsRequest_pb2.pyi | 31 + ib_async/protobuf/ReceiveFA_pb2.py | 37 + ib_async/protobuf/ReceiveFA_pb2.pyi | 13 + ib_async/protobuf/ReplaceFAEnd_pb2.py | 37 + ib_async/protobuf/ReplaceFAEnd_pb2.pyi | 13 + .../protobuf/RerouteMarketDataRequest_pb2.py | 37 + .../protobuf/RerouteMarketDataRequest_pb2.pyi | 15 + .../protobuf/RerouteMarketDepthRequest_pb2.py | 37 + .../RerouteMarketDepthRequest_pb2.pyi | 15 + ib_async/protobuf/ScannerDataElement_pb2.py | 38 + ib_async/protobuf/ScannerDataElement_pb2.pyi | 25 + ib_async/protobuf/ScannerData_pb2.py | 38 + ib_async/protobuf/ScannerData_pb2.pyi | 16 + .../protobuf/ScannerParametersRequest_pb2.py | 37 + .../protobuf/ScannerParametersRequest_pb2.pyi | 9 + ib_async/protobuf/ScannerParameters_pb2.py | 37 + ib_async/protobuf/ScannerParameters_pb2.pyi | 11 + .../ScannerSubscriptionRequest_pb2.py | 38 + .../ScannerSubscriptionRequest_pb2.pyi | 15 + ib_async/protobuf/ScannerSubscription_pb2.py | 45 + ib_async/protobuf/ScannerSubscription_pb2.pyi | 71 + .../protobuf/SecDefOptParameterEnd_pb2.py | 37 + .../protobuf/SecDefOptParameterEnd_pb2.pyi | 11 + ib_async/protobuf/SecDefOptParameter_pb2.py | 37 + ib_async/protobuf/SecDefOptParameter_pb2.pyi | 25 + .../protobuf/SecDefOptParamsRequest_pb2.py | 37 + .../protobuf/SecDefOptParamsRequest_pb2.pyi | 19 + .../protobuf/SetServerLogLevelRequest_pb2.py | 37 + .../protobuf/SetServerLogLevelRequest_pb2.pyi | 11 + ib_async/protobuf/SmartComponent_pb2.py | 37 + ib_async/protobuf/SmartComponent_pb2.pyi | 15 + .../protobuf/SmartComponentsRequest_pb2.py | 37 + .../protobuf/SmartComponentsRequest_pb2.pyi | 13 + ib_async/protobuf/SmartComponents_pb2.py | 38 + ib_async/protobuf/SmartComponents_pb2.pyi | 16 + ib_async/protobuf/SoftDollarTier_pb2.py | 37 + ib_async/protobuf/SoftDollarTier_pb2.pyi | 15 + .../protobuf/SoftDollarTiersRequest_pb2.py | 37 + .../protobuf/SoftDollarTiersRequest_pb2.pyi | 11 + ib_async/protobuf/SoftDollarTiers_pb2.py | 38 + ib_async/protobuf/SoftDollarTiers_pb2.pyi | 16 + ib_async/protobuf/StartApiRequest_pb2.py | 37 + ib_async/protobuf/StartApiRequest_pb2.pyi | 13 + .../SubscribeToGroupEventsRequest_pb2.py | 37 + .../SubscribeToGroupEventsRequest_pb2.pyi | 13 + ib_async/protobuf/SymbolSamples_pb2.py | 38 + ib_async/protobuf/SymbolSamples_pb2.pyi | 16 + ib_async/protobuf/TickAttribBidAsk_pb2.py | 37 + ib_async/protobuf/TickAttribBidAsk_pb2.pyi | 13 + ib_async/protobuf/TickAttribLast_pb2.py | 37 + ib_async/protobuf/TickAttribLast_pb2.pyi | 13 + ib_async/protobuf/TickByTickData_pb2.py | 40 + ib_async/protobuf/TickByTickData_pb2.pyi | 23 + ib_async/protobuf/TickByTickRequest_pb2.py | 38 + ib_async/protobuf/TickByTickRequest_pb2.pyi | 21 + ib_async/protobuf/TickGeneric_pb2.py | 37 + ib_async/protobuf/TickGeneric_pb2.pyi | 15 + ib_async/protobuf/TickNews_pb2.py | 37 + ib_async/protobuf/TickNews_pb2.pyi | 21 + .../protobuf/TickOptionComputation_pb2.py | 37 + .../protobuf/TickOptionComputation_pb2.pyi | 31 + ib_async/protobuf/TickPrice_pb2.py | 37 + ib_async/protobuf/TickPrice_pb2.pyi | 19 + ib_async/protobuf/TickReqParams_pb2.py | 37 + ib_async/protobuf/TickReqParams_pb2.pyi | 17 + ib_async/protobuf/TickSize_pb2.py | 37 + ib_async/protobuf/TickSize_pb2.pyi | 15 + ib_async/protobuf/TickSnapshotEnd_pb2.py | 37 + ib_async/protobuf/TickSnapshotEnd_pb2.pyi | 11 + ib_async/protobuf/TickString_pb2.py | 37 + ib_async/protobuf/TickString_pb2.pyi | 15 + .../UnsubscribeFromGroupEventsRequest_pb2.py | 37 + .../UnsubscribeFromGroupEventsRequest_pb2.pyi | 11 + .../protobuf/UpdateDisplayGroupRequest_pb2.py | 37 + .../UpdateDisplayGroupRequest_pb2.pyi | 13 + ib_async/protobuf/UserInfoRequest_pb2.py | 37 + ib_async/protobuf/UserInfoRequest_pb2.pyi | 11 + ib_async/protobuf/UserInfo_pb2.py | 37 + ib_async/protobuf/UserInfo_pb2.pyi | 13 + ib_async/protobuf/VerifyCompleted_pb2.py | 37 + ib_async/protobuf/VerifyCompleted_pb2.pyi | 13 + ib_async/protobuf/VerifyMessageApi_pb2.py | 37 + ib_async/protobuf/VerifyMessageApi_pb2.pyi | 11 + ib_async/protobuf/VerifyMessageRequest_pb2.py | 37 + .../protobuf/VerifyMessageRequest_pb2.pyi | 11 + ib_async/protobuf/VerifyRequest_pb2.py | 37 + ib_async/protobuf/VerifyRequest_pb2.pyi | 13 + ib_async/protobuf/WshEventDataRequest_pb2.py | 37 + ib_async/protobuf/WshEventDataRequest_pb2.pyi | 27 + ib_async/protobuf/WshEventData_pb2.py | 37 + ib_async/protobuf/WshEventData_pb2.pyi | 13 + ib_async/protobuf/WshMetaDataRequest_pb2.py | 37 + ib_async/protobuf/WshMetaDataRequest_pb2.pyi | 11 + ib_async/protobuf/WshMetaData_pb2.py | 37 + ib_async/protobuf/WshMetaData_pb2.pyi | 13 + ib_async/protobuf/__init__.py | 0 .../protobuf_converters/account_converters.py | 163 +++ .../contract_converters.py | 529 +++++++ .../historical_data_converters.py | 102 ++ .../protobuf_converters/trade_converter.py | 807 +++++++++++ ib_async/util.py | 29 + ib_async/wrapper.py | 458 +++--- pyproject.toml | 15 +- scripts/fix_proto_imports.py | 48 + 399 files changed, 13908 insertions(+), 1510 deletions(-) create mode 100644 ib_async/message.py create mode 100644 ib_async/protobuf/AccountDataEnd_pb2.py create mode 100644 ib_async/protobuf/AccountDataEnd_pb2.pyi create mode 100644 ib_async/protobuf/AccountDataRequest_pb2.py create mode 100644 ib_async/protobuf/AccountDataRequest_pb2.pyi create mode 100644 ib_async/protobuf/AccountSummaryEnd_pb2.py create mode 100644 ib_async/protobuf/AccountSummaryEnd_pb2.pyi create mode 100644 ib_async/protobuf/AccountSummaryRequest_pb2.py create mode 100644 ib_async/protobuf/AccountSummaryRequest_pb2.pyi create mode 100644 ib_async/protobuf/AccountSummary_pb2.py create mode 100644 ib_async/protobuf/AccountSummary_pb2.pyi create mode 100644 ib_async/protobuf/AccountUpdateMultiEnd_pb2.py create mode 100644 ib_async/protobuf/AccountUpdateMultiEnd_pb2.pyi create mode 100644 ib_async/protobuf/AccountUpdateMulti_pb2.py create mode 100644 ib_async/protobuf/AccountUpdateMulti_pb2.pyi create mode 100644 ib_async/protobuf/AccountUpdateTime_pb2.py create mode 100644 ib_async/protobuf/AccountUpdateTime_pb2.pyi create mode 100644 ib_async/protobuf/AccountUpdatesMultiRequest_pb2.py create mode 100644 ib_async/protobuf/AccountUpdatesMultiRequest_pb2.pyi create mode 100644 ib_async/protobuf/AccountValue_pb2.py create mode 100644 ib_async/protobuf/AccountValue_pb2.pyi create mode 100644 ib_async/protobuf/AllOpenOrdersRequest_pb2.py create mode 100644 ib_async/protobuf/AllOpenOrdersRequest_pb2.pyi create mode 100644 ib_async/protobuf/AutoOpenOrdersRequest_pb2.py create mode 100644 ib_async/protobuf/AutoOpenOrdersRequest_pb2.pyi create mode 100644 ib_async/protobuf/CalculateImpliedVolatilityRequest_pb2.py create mode 100644 ib_async/protobuf/CalculateImpliedVolatilityRequest_pb2.pyi create mode 100644 ib_async/protobuf/CalculateOptionPriceRequest_pb2.py create mode 100644 ib_async/protobuf/CalculateOptionPriceRequest_pb2.pyi create mode 100644 ib_async/protobuf/CancelAccountSummary_pb2.py create mode 100644 ib_async/protobuf/CancelAccountSummary_pb2.pyi create mode 100644 ib_async/protobuf/CancelAccountUpdatesMulti_pb2.py create mode 100644 ib_async/protobuf/CancelAccountUpdatesMulti_pb2.pyi create mode 100644 ib_async/protobuf/CancelCalculateImpliedVolatility_pb2.py create mode 100644 ib_async/protobuf/CancelCalculateImpliedVolatility_pb2.pyi create mode 100644 ib_async/protobuf/CancelCalculateOptionPrice_pb2.py create mode 100644 ib_async/protobuf/CancelCalculateOptionPrice_pb2.pyi create mode 100644 ib_async/protobuf/CancelContractData_pb2.py create mode 100644 ib_async/protobuf/CancelContractData_pb2.pyi create mode 100644 ib_async/protobuf/CancelFundamentalsData_pb2.py create mode 100644 ib_async/protobuf/CancelFundamentalsData_pb2.pyi create mode 100644 ib_async/protobuf/CancelHeadTimestamp_pb2.py create mode 100644 ib_async/protobuf/CancelHeadTimestamp_pb2.pyi create mode 100644 ib_async/protobuf/CancelHistogramData_pb2.py create mode 100644 ib_async/protobuf/CancelHistogramData_pb2.pyi create mode 100644 ib_async/protobuf/CancelHistoricalData_pb2.py create mode 100644 ib_async/protobuf/CancelHistoricalData_pb2.pyi create mode 100644 ib_async/protobuf/CancelHistoricalTicks_pb2.py create mode 100644 ib_async/protobuf/CancelHistoricalTicks_pb2.pyi create mode 100644 ib_async/protobuf/CancelMarketData_pb2.py create mode 100644 ib_async/protobuf/CancelMarketData_pb2.pyi create mode 100644 ib_async/protobuf/CancelMarketDepth_pb2.py create mode 100644 ib_async/protobuf/CancelMarketDepth_pb2.pyi create mode 100644 ib_async/protobuf/CancelNewsBulletins_pb2.py create mode 100644 ib_async/protobuf/CancelNewsBulletins_pb2.pyi create mode 100644 ib_async/protobuf/CancelOrderRequest_pb2.py create mode 100644 ib_async/protobuf/CancelOrderRequest_pb2.pyi create mode 100644 ib_async/protobuf/CancelPnLSingle_pb2.py create mode 100644 ib_async/protobuf/CancelPnLSingle_pb2.pyi create mode 100644 ib_async/protobuf/CancelPnL_pb2.py create mode 100644 ib_async/protobuf/CancelPnL_pb2.pyi create mode 100644 ib_async/protobuf/CancelPositionsMulti_pb2.py create mode 100644 ib_async/protobuf/CancelPositionsMulti_pb2.pyi create mode 100644 ib_async/protobuf/CancelPositions_pb2.py create mode 100644 ib_async/protobuf/CancelPositions_pb2.pyi create mode 100644 ib_async/protobuf/CancelRealTimeBars_pb2.py create mode 100644 ib_async/protobuf/CancelRealTimeBars_pb2.pyi create mode 100644 ib_async/protobuf/CancelScannerSubscription_pb2.py create mode 100644 ib_async/protobuf/CancelScannerSubscription_pb2.pyi create mode 100644 ib_async/protobuf/CancelTickByTick_pb2.py create mode 100644 ib_async/protobuf/CancelTickByTick_pb2.pyi create mode 100644 ib_async/protobuf/CancelWshEventData_pb2.py create mode 100644 ib_async/protobuf/CancelWshEventData_pb2.pyi create mode 100644 ib_async/protobuf/CancelWshMetaData_pb2.py create mode 100644 ib_async/protobuf/CancelWshMetaData_pb2.pyi create mode 100644 ib_async/protobuf/ComboLeg_pb2.py create mode 100644 ib_async/protobuf/ComboLeg_pb2.pyi create mode 100644 ib_async/protobuf/CommissionAndFeesReport_pb2.py create mode 100644 ib_async/protobuf/CommissionAndFeesReport_pb2.pyi create mode 100644 ib_async/protobuf/CompletedOrder_pb2.py create mode 100644 ib_async/protobuf/CompletedOrder_pb2.pyi create mode 100644 ib_async/protobuf/CompletedOrdersEnd_pb2.py create mode 100644 ib_async/protobuf/CompletedOrdersEnd_pb2.pyi create mode 100644 ib_async/protobuf/CompletedOrdersRequest_pb2.py create mode 100644 ib_async/protobuf/CompletedOrdersRequest_pb2.pyi create mode 100644 ib_async/protobuf/ContractDataEnd_pb2.py create mode 100644 ib_async/protobuf/ContractDataEnd_pb2.pyi create mode 100644 ib_async/protobuf/ContractDataRequest_pb2.py create mode 100644 ib_async/protobuf/ContractDataRequest_pb2.pyi create mode 100644 ib_async/protobuf/ContractData_pb2.py create mode 100644 ib_async/protobuf/ContractData_pb2.pyi create mode 100644 ib_async/protobuf/ContractDescription_pb2.py create mode 100644 ib_async/protobuf/ContractDescription_pb2.pyi create mode 100644 ib_async/protobuf/ContractDetails_pb2.py create mode 100644 ib_async/protobuf/ContractDetails_pb2.pyi create mode 100644 ib_async/protobuf/Contract_pb2.py create mode 100644 ib_async/protobuf/Contract_pb2.pyi create mode 100644 ib_async/protobuf/CurrentTimeInMillisRequest_pb2.py create mode 100644 ib_async/protobuf/CurrentTimeInMillisRequest_pb2.pyi create mode 100644 ib_async/protobuf/CurrentTimeInMillis_pb2.py create mode 100644 ib_async/protobuf/CurrentTimeInMillis_pb2.pyi create mode 100644 ib_async/protobuf/CurrentTimeRequest_pb2.py create mode 100644 ib_async/protobuf/CurrentTimeRequest_pb2.pyi create mode 100644 ib_async/protobuf/CurrentTime_pb2.py create mode 100644 ib_async/protobuf/CurrentTime_pb2.pyi create mode 100644 ib_async/protobuf/DeltaNeutralContract_pb2.py create mode 100644 ib_async/protobuf/DeltaNeutralContract_pb2.pyi create mode 100644 ib_async/protobuf/DepthMarketDataDescription_pb2.py create mode 100644 ib_async/protobuf/DepthMarketDataDescription_pb2.pyi create mode 100644 ib_async/protobuf/DisplayGroupList_pb2.py create mode 100644 ib_async/protobuf/DisplayGroupList_pb2.pyi create mode 100644 ib_async/protobuf/DisplayGroupUpdated_pb2.py create mode 100644 ib_async/protobuf/DisplayGroupUpdated_pb2.pyi create mode 100644 ib_async/protobuf/ErrorMessage_pb2.py create mode 100644 ib_async/protobuf/ErrorMessage_pb2.pyi create mode 100644 ib_async/protobuf/ExecutionDetailsEnd_pb2.py create mode 100644 ib_async/protobuf/ExecutionDetailsEnd_pb2.pyi create mode 100644 ib_async/protobuf/ExecutionDetails_pb2.py create mode 100644 ib_async/protobuf/ExecutionDetails_pb2.pyi create mode 100644 ib_async/protobuf/ExecutionFilter_pb2.py create mode 100644 ib_async/protobuf/ExecutionFilter_pb2.pyi create mode 100644 ib_async/protobuf/ExecutionRequest_pb2.py create mode 100644 ib_async/protobuf/ExecutionRequest_pb2.pyi create mode 100644 ib_async/protobuf/Execution_pb2.py create mode 100644 ib_async/protobuf/Execution_pb2.pyi create mode 100644 ib_async/protobuf/ExerciseOptionsRequest_pb2.py create mode 100644 ib_async/protobuf/ExerciseOptionsRequest_pb2.pyi create mode 100644 ib_async/protobuf/FAReplace_pb2.py create mode 100644 ib_async/protobuf/FAReplace_pb2.pyi create mode 100644 ib_async/protobuf/FARequest_pb2.py create mode 100644 ib_async/protobuf/FARequest_pb2.pyi create mode 100644 ib_async/protobuf/FamilyCode_pb2.py create mode 100644 ib_async/protobuf/FamilyCode_pb2.pyi create mode 100644 ib_async/protobuf/FamilyCodesRequest_pb2.py create mode 100644 ib_async/protobuf/FamilyCodesRequest_pb2.pyi create mode 100644 ib_async/protobuf/FamilyCodes_pb2.py create mode 100644 ib_async/protobuf/FamilyCodes_pb2.pyi create mode 100644 ib_async/protobuf/FundamentalsDataRequest_pb2.py create mode 100644 ib_async/protobuf/FundamentalsDataRequest_pb2.pyi create mode 100644 ib_async/protobuf/FundamentalsData_pb2.py create mode 100644 ib_async/protobuf/FundamentalsData_pb2.pyi create mode 100644 ib_async/protobuf/GlobalCancelRequest_pb2.py create mode 100644 ib_async/protobuf/GlobalCancelRequest_pb2.pyi create mode 100644 ib_async/protobuf/HeadTimestampRequest_pb2.py create mode 100644 ib_async/protobuf/HeadTimestampRequest_pb2.pyi create mode 100644 ib_async/protobuf/HeadTimestamp_pb2.py create mode 100644 ib_async/protobuf/HeadTimestamp_pb2.pyi create mode 100644 ib_async/protobuf/HistogramDataEntry_pb2.py create mode 100644 ib_async/protobuf/HistogramDataEntry_pb2.pyi create mode 100644 ib_async/protobuf/HistogramDataRequest_pb2.py create mode 100644 ib_async/protobuf/HistogramDataRequest_pb2.pyi create mode 100644 ib_async/protobuf/HistogramData_pb2.py create mode 100644 ib_async/protobuf/HistogramData_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalDataBar_pb2.py create mode 100644 ib_async/protobuf/HistoricalDataBar_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalDataEnd_pb2.py create mode 100644 ib_async/protobuf/HistoricalDataEnd_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalDataRequest_pb2.py create mode 100644 ib_async/protobuf/HistoricalDataRequest_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalDataUpdate_pb2.py create mode 100644 ib_async/protobuf/HistoricalDataUpdate_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalData_pb2.py create mode 100644 ib_async/protobuf/HistoricalData_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalNewsEnd_pb2.py create mode 100644 ib_async/protobuf/HistoricalNewsEnd_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalNewsRequest_pb2.py create mode 100644 ib_async/protobuf/HistoricalNewsRequest_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalNews_pb2.py create mode 100644 ib_async/protobuf/HistoricalNews_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalSchedule_pb2.py create mode 100644 ib_async/protobuf/HistoricalSchedule_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalSession_pb2.py create mode 100644 ib_async/protobuf/HistoricalSession_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalTickBidAsk_pb2.py create mode 100644 ib_async/protobuf/HistoricalTickBidAsk_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalTickLast_pb2.py create mode 100644 ib_async/protobuf/HistoricalTickLast_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalTick_pb2.py create mode 100644 ib_async/protobuf/HistoricalTick_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalTicksBidAsk_pb2.py create mode 100644 ib_async/protobuf/HistoricalTicksBidAsk_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalTicksLast_pb2.py create mode 100644 ib_async/protobuf/HistoricalTicksLast_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalTicksRequest_pb2.py create mode 100644 ib_async/protobuf/HistoricalTicksRequest_pb2.pyi create mode 100644 ib_async/protobuf/HistoricalTicks_pb2.py create mode 100644 ib_async/protobuf/HistoricalTicks_pb2.pyi create mode 100644 ib_async/protobuf/IdsRequest_pb2.py create mode 100644 ib_async/protobuf/IdsRequest_pb2.pyi create mode 100644 ib_async/protobuf/IneligibilityReason_pb2.py create mode 100644 ib_async/protobuf/IneligibilityReason_pb2.pyi create mode 100644 ib_async/protobuf/ManagedAccountsRequest_pb2.py create mode 100644 ib_async/protobuf/ManagedAccountsRequest_pb2.pyi create mode 100644 ib_async/protobuf/ManagedAccounts_pb2.py create mode 100644 ib_async/protobuf/ManagedAccounts_pb2.pyi create mode 100644 ib_async/protobuf/MarketDataRequest_pb2.py create mode 100644 ib_async/protobuf/MarketDataRequest_pb2.pyi create mode 100644 ib_async/protobuf/MarketDataTypeRequest_pb2.py create mode 100644 ib_async/protobuf/MarketDataTypeRequest_pb2.pyi create mode 100644 ib_async/protobuf/MarketDataType_pb2.py create mode 100644 ib_async/protobuf/MarketDataType_pb2.pyi create mode 100644 ib_async/protobuf/MarketDepthData_pb2.py create mode 100644 ib_async/protobuf/MarketDepthData_pb2.pyi create mode 100644 ib_async/protobuf/MarketDepthExchangesRequest_pb2.py create mode 100644 ib_async/protobuf/MarketDepthExchangesRequest_pb2.pyi create mode 100644 ib_async/protobuf/MarketDepthExchanges_pb2.py create mode 100644 ib_async/protobuf/MarketDepthExchanges_pb2.pyi create mode 100644 ib_async/protobuf/MarketDepthL2_pb2.py create mode 100644 ib_async/protobuf/MarketDepthL2_pb2.pyi create mode 100644 ib_async/protobuf/MarketDepthRequest_pb2.py create mode 100644 ib_async/protobuf/MarketDepthRequest_pb2.pyi create mode 100644 ib_async/protobuf/MarketDepth_pb2.py create mode 100644 ib_async/protobuf/MarketDepth_pb2.pyi create mode 100644 ib_async/protobuf/MarketRuleRequest_pb2.py create mode 100644 ib_async/protobuf/MarketRuleRequest_pb2.pyi create mode 100644 ib_async/protobuf/MarketRule_pb2.py create mode 100644 ib_async/protobuf/MarketRule_pb2.pyi create mode 100644 ib_async/protobuf/MatchingSymbolsRequest_pb2.py create mode 100644 ib_async/protobuf/MatchingSymbolsRequest_pb2.pyi create mode 100644 ib_async/protobuf/NewsArticleRequest_pb2.py create mode 100644 ib_async/protobuf/NewsArticleRequest_pb2.pyi create mode 100644 ib_async/protobuf/NewsArticle_pb2.py create mode 100644 ib_async/protobuf/NewsArticle_pb2.pyi create mode 100644 ib_async/protobuf/NewsBulletin_pb2.py create mode 100644 ib_async/protobuf/NewsBulletin_pb2.pyi create mode 100644 ib_async/protobuf/NewsBulletinsRequest_pb2.py create mode 100644 ib_async/protobuf/NewsBulletinsRequest_pb2.pyi create mode 100644 ib_async/protobuf/NewsProvider_pb2.py create mode 100644 ib_async/protobuf/NewsProvider_pb2.pyi create mode 100644 ib_async/protobuf/NewsProvidersRequest_pb2.py create mode 100644 ib_async/protobuf/NewsProvidersRequest_pb2.pyi create mode 100644 ib_async/protobuf/NewsProviders_pb2.py create mode 100644 ib_async/protobuf/NewsProviders_pb2.pyi create mode 100644 ib_async/protobuf/NextValidId_pb2.py create mode 100644 ib_async/protobuf/NextValidId_pb2.pyi create mode 100644 ib_async/protobuf/OpenOrder_pb2.py create mode 100644 ib_async/protobuf/OpenOrder_pb2.pyi create mode 100644 ib_async/protobuf/OpenOrdersEnd_pb2.py create mode 100644 ib_async/protobuf/OpenOrdersEnd_pb2.pyi create mode 100644 ib_async/protobuf/OpenOrdersRequest_pb2.py create mode 100644 ib_async/protobuf/OpenOrdersRequest_pb2.pyi create mode 100644 ib_async/protobuf/OrderAllocation_pb2.py create mode 100644 ib_async/protobuf/OrderAllocation_pb2.pyi create mode 100644 ib_async/protobuf/OrderBound_pb2.py create mode 100644 ib_async/protobuf/OrderBound_pb2.pyi create mode 100644 ib_async/protobuf/OrderCancel_pb2.py create mode 100644 ib_async/protobuf/OrderCancel_pb2.pyi create mode 100644 ib_async/protobuf/OrderCondition_pb2.py create mode 100644 ib_async/protobuf/OrderCondition_pb2.pyi create mode 100644 ib_async/protobuf/OrderState_pb2.py create mode 100644 ib_async/protobuf/OrderState_pb2.pyi create mode 100644 ib_async/protobuf/OrderStatus_pb2.py create mode 100644 ib_async/protobuf/OrderStatus_pb2.pyi create mode 100644 ib_async/protobuf/Order_pb2.py create mode 100644 ib_async/protobuf/Order_pb2.pyi create mode 100644 ib_async/protobuf/PlaceOrderRequest_pb2.py create mode 100644 ib_async/protobuf/PlaceOrderRequest_pb2.pyi create mode 100644 ib_async/protobuf/PnLRequest_pb2.py create mode 100644 ib_async/protobuf/PnLRequest_pb2.pyi create mode 100644 ib_async/protobuf/PnLSingleRequest_pb2.py create mode 100644 ib_async/protobuf/PnLSingleRequest_pb2.pyi create mode 100644 ib_async/protobuf/PnLSingle_pb2.py create mode 100644 ib_async/protobuf/PnLSingle_pb2.pyi create mode 100644 ib_async/protobuf/PnL_pb2.py create mode 100644 ib_async/protobuf/PnL_pb2.pyi create mode 100644 ib_async/protobuf/PortfolioValue_pb2.py create mode 100644 ib_async/protobuf/PortfolioValue_pb2.pyi create mode 100644 ib_async/protobuf/PositionEnd_pb2.py create mode 100644 ib_async/protobuf/PositionEnd_pb2.pyi create mode 100644 ib_async/protobuf/PositionMultiEnd_pb2.py create mode 100644 ib_async/protobuf/PositionMultiEnd_pb2.pyi create mode 100644 ib_async/protobuf/PositionMulti_pb2.py create mode 100644 ib_async/protobuf/PositionMulti_pb2.pyi create mode 100644 ib_async/protobuf/Position_pb2.py create mode 100644 ib_async/protobuf/Position_pb2.pyi create mode 100644 ib_async/protobuf/PositionsMultiRequest_pb2.py create mode 100644 ib_async/protobuf/PositionsMultiRequest_pb2.pyi create mode 100644 ib_async/protobuf/PositionsRequest_pb2.py create mode 100644 ib_async/protobuf/PositionsRequest_pb2.pyi create mode 100644 ib_async/protobuf/PriceIncrement_pb2.py create mode 100644 ib_async/protobuf/PriceIncrement_pb2.pyi create mode 100644 ib_async/protobuf/QueryDisplayGroupsRequest_pb2.py create mode 100644 ib_async/protobuf/QueryDisplayGroupsRequest_pb2.pyi create mode 100644 ib_async/protobuf/RealTimeBarTick_pb2.py create mode 100644 ib_async/protobuf/RealTimeBarTick_pb2.pyi create mode 100644 ib_async/protobuf/RealTimeBarsRequest_pb2.py create mode 100644 ib_async/protobuf/RealTimeBarsRequest_pb2.pyi create mode 100644 ib_async/protobuf/ReceiveFA_pb2.py create mode 100644 ib_async/protobuf/ReceiveFA_pb2.pyi create mode 100644 ib_async/protobuf/ReplaceFAEnd_pb2.py create mode 100644 ib_async/protobuf/ReplaceFAEnd_pb2.pyi create mode 100644 ib_async/protobuf/RerouteMarketDataRequest_pb2.py create mode 100644 ib_async/protobuf/RerouteMarketDataRequest_pb2.pyi create mode 100644 ib_async/protobuf/RerouteMarketDepthRequest_pb2.py create mode 100644 ib_async/protobuf/RerouteMarketDepthRequest_pb2.pyi create mode 100644 ib_async/protobuf/ScannerDataElement_pb2.py create mode 100644 ib_async/protobuf/ScannerDataElement_pb2.pyi create mode 100644 ib_async/protobuf/ScannerData_pb2.py create mode 100644 ib_async/protobuf/ScannerData_pb2.pyi create mode 100644 ib_async/protobuf/ScannerParametersRequest_pb2.py create mode 100644 ib_async/protobuf/ScannerParametersRequest_pb2.pyi create mode 100644 ib_async/protobuf/ScannerParameters_pb2.py create mode 100644 ib_async/protobuf/ScannerParameters_pb2.pyi create mode 100644 ib_async/protobuf/ScannerSubscriptionRequest_pb2.py create mode 100644 ib_async/protobuf/ScannerSubscriptionRequest_pb2.pyi create mode 100644 ib_async/protobuf/ScannerSubscription_pb2.py create mode 100644 ib_async/protobuf/ScannerSubscription_pb2.pyi create mode 100644 ib_async/protobuf/SecDefOptParameterEnd_pb2.py create mode 100644 ib_async/protobuf/SecDefOptParameterEnd_pb2.pyi create mode 100644 ib_async/protobuf/SecDefOptParameter_pb2.py create mode 100644 ib_async/protobuf/SecDefOptParameter_pb2.pyi create mode 100644 ib_async/protobuf/SecDefOptParamsRequest_pb2.py create mode 100644 ib_async/protobuf/SecDefOptParamsRequest_pb2.pyi create mode 100644 ib_async/protobuf/SetServerLogLevelRequest_pb2.py create mode 100644 ib_async/protobuf/SetServerLogLevelRequest_pb2.pyi create mode 100644 ib_async/protobuf/SmartComponent_pb2.py create mode 100644 ib_async/protobuf/SmartComponent_pb2.pyi create mode 100644 ib_async/protobuf/SmartComponentsRequest_pb2.py create mode 100644 ib_async/protobuf/SmartComponentsRequest_pb2.pyi create mode 100644 ib_async/protobuf/SmartComponents_pb2.py create mode 100644 ib_async/protobuf/SmartComponents_pb2.pyi create mode 100644 ib_async/protobuf/SoftDollarTier_pb2.py create mode 100644 ib_async/protobuf/SoftDollarTier_pb2.pyi create mode 100644 ib_async/protobuf/SoftDollarTiersRequest_pb2.py create mode 100644 ib_async/protobuf/SoftDollarTiersRequest_pb2.pyi create mode 100644 ib_async/protobuf/SoftDollarTiers_pb2.py create mode 100644 ib_async/protobuf/SoftDollarTiers_pb2.pyi create mode 100644 ib_async/protobuf/StartApiRequest_pb2.py create mode 100644 ib_async/protobuf/StartApiRequest_pb2.pyi create mode 100644 ib_async/protobuf/SubscribeToGroupEventsRequest_pb2.py create mode 100644 ib_async/protobuf/SubscribeToGroupEventsRequest_pb2.pyi create mode 100644 ib_async/protobuf/SymbolSamples_pb2.py create mode 100644 ib_async/protobuf/SymbolSamples_pb2.pyi create mode 100644 ib_async/protobuf/TickAttribBidAsk_pb2.py create mode 100644 ib_async/protobuf/TickAttribBidAsk_pb2.pyi create mode 100644 ib_async/protobuf/TickAttribLast_pb2.py create mode 100644 ib_async/protobuf/TickAttribLast_pb2.pyi create mode 100644 ib_async/protobuf/TickByTickData_pb2.py create mode 100644 ib_async/protobuf/TickByTickData_pb2.pyi create mode 100644 ib_async/protobuf/TickByTickRequest_pb2.py create mode 100644 ib_async/protobuf/TickByTickRequest_pb2.pyi create mode 100644 ib_async/protobuf/TickGeneric_pb2.py create mode 100644 ib_async/protobuf/TickGeneric_pb2.pyi create mode 100644 ib_async/protobuf/TickNews_pb2.py create mode 100644 ib_async/protobuf/TickNews_pb2.pyi create mode 100644 ib_async/protobuf/TickOptionComputation_pb2.py create mode 100644 ib_async/protobuf/TickOptionComputation_pb2.pyi create mode 100644 ib_async/protobuf/TickPrice_pb2.py create mode 100644 ib_async/protobuf/TickPrice_pb2.pyi create mode 100644 ib_async/protobuf/TickReqParams_pb2.py create mode 100644 ib_async/protobuf/TickReqParams_pb2.pyi create mode 100644 ib_async/protobuf/TickSize_pb2.py create mode 100644 ib_async/protobuf/TickSize_pb2.pyi create mode 100644 ib_async/protobuf/TickSnapshotEnd_pb2.py create mode 100644 ib_async/protobuf/TickSnapshotEnd_pb2.pyi create mode 100644 ib_async/protobuf/TickString_pb2.py create mode 100644 ib_async/protobuf/TickString_pb2.pyi create mode 100644 ib_async/protobuf/UnsubscribeFromGroupEventsRequest_pb2.py create mode 100644 ib_async/protobuf/UnsubscribeFromGroupEventsRequest_pb2.pyi create mode 100644 ib_async/protobuf/UpdateDisplayGroupRequest_pb2.py create mode 100644 ib_async/protobuf/UpdateDisplayGroupRequest_pb2.pyi create mode 100644 ib_async/protobuf/UserInfoRequest_pb2.py create mode 100644 ib_async/protobuf/UserInfoRequest_pb2.pyi create mode 100644 ib_async/protobuf/UserInfo_pb2.py create mode 100644 ib_async/protobuf/UserInfo_pb2.pyi create mode 100644 ib_async/protobuf/VerifyCompleted_pb2.py create mode 100644 ib_async/protobuf/VerifyCompleted_pb2.pyi create mode 100644 ib_async/protobuf/VerifyMessageApi_pb2.py create mode 100644 ib_async/protobuf/VerifyMessageApi_pb2.pyi create mode 100644 ib_async/protobuf/VerifyMessageRequest_pb2.py create mode 100644 ib_async/protobuf/VerifyMessageRequest_pb2.pyi create mode 100644 ib_async/protobuf/VerifyRequest_pb2.py create mode 100644 ib_async/protobuf/VerifyRequest_pb2.pyi create mode 100644 ib_async/protobuf/WshEventDataRequest_pb2.py create mode 100644 ib_async/protobuf/WshEventDataRequest_pb2.pyi create mode 100644 ib_async/protobuf/WshEventData_pb2.py create mode 100644 ib_async/protobuf/WshEventData_pb2.pyi create mode 100644 ib_async/protobuf/WshMetaDataRequest_pb2.py create mode 100644 ib_async/protobuf/WshMetaDataRequest_pb2.pyi create mode 100644 ib_async/protobuf/WshMetaData_pb2.py create mode 100644 ib_async/protobuf/WshMetaData_pb2.pyi create mode 100644 ib_async/protobuf/__init__.py create mode 100644 ib_async/protobuf_converters/account_converters.py create mode 100644 ib_async/protobuf_converters/contract_converters.py create mode 100644 ib_async/protobuf_converters/historical_data_converters.py create mode 100644 ib_async/protobuf_converters/trade_converter.py create mode 100644 scripts/fix_proto_imports.py diff --git a/.gitignore b/.gitignore index 49ce83f7..6209c949 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ ib_async.egg-info poetry.lock *.csv *.json +proto/* +**/__pycache__/ +#pythonclient/* diff --git a/README.md b/README.md index 713f058a..21cf50c6 100644 --- a/README.md +++ b/README.md @@ -496,26 +496,39 @@ poetry run ruff format poetry run ruff check --fix ``` +### Generate protobuf dependencies + +Build protobuf files + +Copy `*.proto` files from `twsapi` into `ib_async/proto`, then run: + +```bash +poetry run python -m grpc_tools.protoc -I=proto --python_out=ib_async/protobuf --pyi_out=ib_async/protobuf proto/*.proto +# +poetry run python scripts/fix_proto_imports.py +``` + ### Local Development 1. Clone the repository: + ```bash git clone https://github.com/ib-api-reloaded/ib_async.git cd ib_async ``` -2. Install dependencies: +1. Install dependencies: ```bash poetry install --with=dev,docs ``` -3. Make your changes and run tests: +1. Make your changes and run tests: ```bash poetry run pytest poetry run mypy ib_async ``` -4. Submit a pull request with: +1. Submit a pull request with: - Clear description of changes - Tests for new functionality - Updated documentation if needed diff --git a/ib_async/client.py b/ib_async/client.py index b60f1ccf..1157b8bb 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -10,12 +10,72 @@ from typing import Any, Callable, Deque, List, Optional from eventkit import Event +from google.protobuf.message import Message from .connection import Connection from .contract import Contract from .decoder import Decoder +from .message import MessageId from .objects import ConnectionStats, WshEventData -from .util import dataclassAsTuple, getLoop, run, UNSET_DOUBLE, UNSET_INTEGER +from .protobuf.AccountSummaryRequest_pb2 import ( + AccountSummaryRequest as AccountSummaryRequestProto, +) +from .protobuf.AllOpenOrdersRequest_pb2 import ( + AllOpenOrdersRequest as AllOpenOrdersRequestProto, +) +from .protobuf.AutoOpenOrdersRequest_pb2 import ( + AutoOpenOrdersRequest as AutoOpenOrdersRequestProto, +) +from .protobuf.CancelAccountSummary_pb2 import ( + CancelAccountSummary as CancelAccountSummaryProto, +) +from .protobuf.CancelOrderRequest_pb2 import ( + CancelOrderRequest as CancelOrderRequestProto, +) +from .protobuf.CancelPositions_pb2 import CancelPositions as CancelPositionsProto +from .protobuf.CancelHeadTimestamp_pb2 import ( + CancelHeadTimestamp as CancelHeadTimestampProto, +) +from .protobuf.CompletedOrdersRequest_pb2 import ( + CompletedOrdersRequest as CompletedOrdersRequestProto, +) +from .protobuf.CancelHistoricalData_pb2 import ( + CancelHistoricalData as CancelHistoricalDataProto, +) +from .protobuf.ContractDataRequest_pb2 import ( + ContractDataRequest as ContractDataRequestProto, +) +from .protobuf.CurrentTimeInMillisRequest_pb2 import ( + CurrentTimeInMillisRequest as CurrentTimeInMillisRequestProto, +) +from .protobuf.CurrentTimeRequest_pb2 import ( + CurrentTimeRequest as CurrentTimeRequestProto, +) +from .protobuf.IdsRequest_pb2 import IdsRequest as IdsRequestProto +from .protobuf.ManagedAccountsRequest_pb2 import ( + ManagedAccountsRequest as ManagedAccountsRequestProto, +) +from .protobuf.OpenOrdersRequest_pb2 import OpenOrdersRequest as OpenOrdersRequestProto + +from .protobuf.PositionsRequest_pb2 import PositionsRequest as PositionsRequestProto +from .protobuf.StartApiRequest_pb2 import StartApiRequest as StartApiRequestProto +from .protobuf_converters.account_converters import ( + createAccountDataRequestProto, + createAccountMultiRequestProto, + createCancelAccMultiRequestProto, +) +from .protobuf_converters.contract_converters import ( + createContractProto, + createMarketRuleRequestProto, + createMatchingSymbolsRequestProto, + createSecDefOptParamsRequestProto, +) +from .protobuf_converters.historical_data_converters import ( + createHeadTimestampRequestProto, + createHistoricalDataRequestProto, +) +from .protobuf_converters.trade_converter import createExecutionRequestProto +from .util import UNSET_DOUBLE, UNSET_INTEGER, dataclassAsTuple, getLoop, run class Client: @@ -84,8 +144,8 @@ class Client: MaxRequests = 45 RequestsInterval = 1 - MinClientVersion = 157 - MaxClientVersion = 178 + MinClientVersion = 100 + MaxClientVersion = 216 # Updated to support Protobuf DISCONNECTED, CONNECTING, CONNECTED = range(3) @@ -113,6 +173,7 @@ def __init__(self, wrapper): self.clientId = -1 self.optCapab = "" self.connectOptions = b"" + self._hasReqId = False # Initialize _hasReqId in __init__ self.reset() def reset(self): @@ -133,6 +194,10 @@ def reset(self): def serverVersion(self) -> int: return self._serverVersion + def useProtoBuf(self) -> bool: + result = self.serverVersion() >= 201 # min protobuf server version + return result + def run(self): loop = getLoop() loop.run_forever() @@ -207,7 +272,7 @@ def connect( async def connectAsync(self, host, port, clientId, timeout=2.0): try: self._logger.info( - f"Connecting to {host}:{port} with clientId {clientId}..." + "Connecting to %s:%s with clientId %s...", host, port, clientId ) self.host = host self.port = int(port) @@ -243,117 +308,9 @@ def disconnect(self): self.conn.disconnect() self.reset() - def send(self, *fields, makeEmpty=True): - """Serialize and send the given fields using the IB socket protocol. - - if 'makeEmpty' is True (default), then the IBKR values representing "no value" - become the empty string.""" - if not self.isConnected(): - raise ConnectionError("Not connected") - - # fmt: off - FORMAT_HANDLERS: dict[Any, Callable[[Any], str]] = { - # Contracts are formatted in IBKR null delimiter format - Contract: lambda c: "\0".join([ - str(f) - for f in ( - c.conId, - c.symbol, - c.secType, - c.lastTradeDateOrContractMonth, - c.strike, - c.right, - c.multiplier, - c.exchange, - c.primaryExchange, - c.currency, - c.localSymbol, - c.tradingClass, - ) - ]), - - # Float conversion has 3 stages: - # - Convert 'IBKR unset' double to empty (if requested) - # - Convert infinity to 'Infinite' string (if appropriate) - # - else, convert float to string normally - float: lambda f: "" - if (makeEmpty and f == UNSET_DOUBLE) - else ("Infinite" if (f == math.inf) else str(f)), - - # Int conversion has 2 stages: - # - Convert 'IBKR unset' to empty (if requested) - # - else, convert int to string normally - int: lambda f: "" if makeEmpty and f == UNSET_INTEGER else str(f), - - # None is always just an empty string. - # (due to a quirk of Python, 'type(None)' is how you properly generate the NoneType value) - type(None): lambda _: "", - - # Strings are always strings - str: lambda s: s, - - # Bools become strings "1" or "0" - bool: lambda b: "1" if b else "0", - - # Lists of tags become semicolon-appended KV pairs - list: lambda lst: "".join([f"{v.tag}={v.value};" for v in lst]), - } - # fmt: on - - # start of new message - msg = io.StringIO() - - for field in fields: - # Fetch type converter for this field (falls back to 'str(field)' as a default for unmatched types) - # (extra `isinstance()` wrapper needed here because Contract subclasses are their own type, but we want - # to only match against the Contract parent class for formatting operations) - convert = FORMAT_HANDLERS.get( - Contract if isinstance(field, Contract) else type(field), str - ) - - # Convert field to IBKR protocol string part - s = convert(field) - - # Append converted IBKR protocol string to message buffer - msg.write(s) - msg.write("\0") - - generated = msg.getvalue() - self.sendMsg(generated) - - def sendMsg(self, msg: str): - loop = getLoop() - t = loop.time() - times = self._timeQ - msgs = self._msgQ - while times and t - times[0] > self.RequestsInterval: - times.popleft() - - if msg: - msgs.append(msg) - - while msgs and (len(times) < self.MaxRequests or not self.MaxRequests): - msg = msgs.popleft() - self.conn.sendMsg(self._prefix(msg.encode())) - times.append(t) - if self._logger.isEnabledFor(logging.DEBUG): - self._logger.debug(">>> %s", msg[:-1].replace("\0", ",")) - - if msgs: - if not self._isThrottling: - self._isThrottling = True - self.throttleStart.emit() - self._logger.debug("Started to throttle requests") - loop.call_at(times[0] + self.RequestsInterval, self.sendMsg, None) - else: - if self._isThrottling: - self._isThrottling = False - self.throttleEnd.emit() - self._logger.debug("Stopped to throttle requests") - def _prefix(self, msg): # prefix a message with its length - return struct.pack(">I", len(msg)) + msg + return struct.pack(f">I{len(msg)}s", len(msg), msg) def _onSocketHasData(self, data): debug = self._logger.isEnabledFor(logging.DEBUG) @@ -364,54 +321,54 @@ def _onSocketHasData(self, data): self._numBytesRecv += len(data) while True: - if len(self._data) <= 4: + if len(self._data) < 4: + # Not enough data for the length prefix break - # 4 byte prefix tells the message length + # 4-byte prefix tells the message length msgEnd = 4 + struct.unpack(">I", self._data[:4])[0] if len(self._data) < msgEnd: - # insufficient data for now + # Incomplete message in buffer break - msg = self._data[4:msgEnd].decode(errors="backslashreplace") + payload = self._data[4:msgEnd] self._data = self._data[msgEnd:] - fields = msg.split("\0") - fields.pop() # pop off last empty element self._numMsgRecv += 1 - if debug: - self._logger.debug("<<< %s", ",".join(fields)) + if not self._serverVersion: + # First message is always the legacy server version string + fields = payload.decode(errors="backslashreplace").split("\0") + fields.pop() + + if debug: + self._logger.debug("<<< %s", ",".join(fields)) - if not self._serverVersion and len(fields) == 2: - # this concludes the handshake version, _connTime = fields self._serverVersion = int(version) - if self._serverVersion < self.MinClientVersion: - self._onSocketDisconnected("TWS/gateway version must be >= 972") + self._logger.info( + "Established connection, server version %s", self._serverVersion + ) + + if self._serverVersion < 201: # MIN_SERVER_VER_PROTOBUF + self._onSocketDisconnected( + f"Server version {self._serverVersion} does not support Protobuf. Disconnecting." + ) return + self.decoder.serverVersion = self._serverVersion self.connState = Client.CONNECTED self.startApi() - self.wrapper.connectAck() - self._logger.info(f"Logged on to server version {self._serverVersion}") + self._logger.info("Logged on to server version %s", self._serverVersion) else: if not self._apiReady: - # snoop for nextValidId and managedAccounts response, - # when both are in then the client is ready - msgId = int(fields[0]) - if msgId == 9: - _, _, validId = fields - self.updateReqId(int(validId)) - self._hasReqId = True - elif msgId == 15: - _, _, accts = fields - self._accounts = [a for a in accts.split(",") if a] if self._hasReqId and self._accounts: self._apiReady = True self.apiStart.emit() - - # decode and handle the message - self.decoder.interpret(fields) + if debug: + self._logger.debug("<<< %r", payload) + # After handshake, all incoming messages are treated as Protobuf + # The entire payload (ID + data) is passed to the decoder + self.decoder.processProtoBuf(payload) if self._tcpDataProcessed: self._tcpDataProcessed() @@ -719,29 +676,40 @@ def cancelOrder(self, orderId, manualCancelOrderTime=""): self.send(*fields) def reqOpenOrders(self): - self.send(5, 1) + openOrdersRequestProto = OpenOrdersRequestProto() + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_OPEN_ORDERS), + openOrdersRequestProto, + ) def reqAccountUpdates(self, subscribe, acctCode): - self.send(6, 2, subscribe, acctCode) + proto = createAccountDataRequestProto(subscribe, acctCode) + self.sendProto(MessageId.to_protobuf(MessageId.OUT.REQ_ACCT_DATA), proto) def reqExecutions(self, reqId, execFilter): - self.send( - 7, - 3, - reqId, - execFilter.clientId, - execFilter.acctCode, - execFilter.time, - execFilter.symbol, - execFilter.secType, - execFilter.exchange, - execFilter.side, + executionRequestProto = createExecutionRequestProto(reqId, execFilter) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_EXECUTIONS), executionRequestProto ) - def reqIds(self, numIds): - self.send(8, 1, numIds) + def reqIds(self, numIds: int): + """Request a new request ID.""" + self._logger.debug("Calling reqIds (Protobuf)") + proto = IdsRequestProto() + proto.numIds = numIds + self.sendProto(MessageId.to_protobuf(MessageId.OUT.REQ_IDS), proto) def reqContractDetails(self, reqId, contract): + contractDetailsRequestProto = ContractDataRequestProto() + contractDetailsRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + contractDetailsRequestProto.contract.CopyFrom(contractProto) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_CONTRACT_DATA), + contractDetailsRequestProto, + ) + + def reqContractDetailsLegacy(self, reqId, contract): fields = [ 9, 8, @@ -757,6 +725,128 @@ def reqContractDetails(self, reqId, contract): self.send(*fields) + def sendProto(self, msgId: int, proto: Message): + # TODO implement throtling + body = proto.SerializeToString() + header = msgId.to_bytes(4, "big") + msg = header + body + self.conn.sendMsg(self._prefix(msg)) + if self._logger.isEnabledFor(logging.DEBUG): + self._logger.debug( + "Sending Protobuf message: msgId=%s, proto=%r, body=%r", + msgId, + proto, + body, + ) + + def send(self, *fields, makeEmpty=True): + """Serialize and send the given fields using the IB socket protocol. + + if 'makeEmpty' is True (default), then the IBKR values representing "no value" + become the empty string.""" + if not self.isConnected(): + raise ConnectionError("Not connected") + + # fmt: off + FORMAT_HANDLERS: dict[Any, Callable[[Any], str]] = { + # Contracts are formatted in IBKR null delimiter format + Contract: lambda c: "\0".join([ + str(f) + for f in ( + c.conId, + c.symbol, + c.secType, + c.lastTradeDateOrContractMonth, + c.strike, + c.right, + c.multiplier, + c.exchange, + c.primaryExchange, + c.currency, + c.localSymbol, + c.tradingClass, + ) + ]), + + # Float conversion has 3 stages: + # - Convert 'IBKR unset' double to empty (if requested) + # - Convert infinity to 'Infinite' string (if appropriate) + # - else, convert float to string normally + float: lambda f: "" + if (makeEmpty and f == UNSET_DOUBLE) + else ("Infinite" if (f == math.inf) else str(f)), + + # Int conversion has 2 stages: + # - Convert 'IBKR unset' to empty (if requested) + # - else, convert int to string normally + int: lambda f: "" if makeEmpty and f == UNSET_INTEGER else str(f), + + # None is always just an empty string. + # (due to a quirk of Python, 'type(None)' is how you properly generate the NoneType value) + type(None): lambda _: "", + + # Strings are always strings + str: lambda s: s, + + # Bools become strings "1" or "0" + bool: lambda b: "1" if b else "0", + + # Lists of tags become semicolon-appended KV pairs + list: lambda lst: "".join([f"{v.tag}={v.value};" for v in lst]), + } + # fmt: on + + # start of new message + msg = io.StringIO() + + for field in fields: + # Fetch type converter for this field (falls back to 'str(field)' as a default for unmatched types) + # (extra `isinstance()` wrapper needed here because Contract subclasses are their own type, but we want + # to only match against the Contract parent class for formatting operations) + convert = FORMAT_HANDLERS.get( + Contract if isinstance(field, Contract) else type(field), str + ) + + # Convert field to IBKR protocol string part + s = convert(field) + + # Append converted IBKR protocol string to message buffer + msg.write(s) + msg.write("\0") + + generated = msg.getvalue() + self.sendMsg(generated) + + def sendMsg(self, msg: str): + loop = getLoop() + t = loop.time() + times = self._timeQ + msgs = self._msgQ + while times and t - times[0] > self.RequestsInterval: + times.popleft() + + if msg: + msgs.append(msg) + + while msgs and (len(times) < self.MaxRequests or not self.MaxRequests): + msg = msgs.popleft() + self.conn.sendMsg(self._prefix(msg.encode())) + times.append(t) + if self._logger.isEnabledFor(logging.DEBUG): + self._logger.debug(">>> %s", msg[:-1].replace("\0", ",")) + + if msgs: + if not self._isThrottling: + self._isThrottling = True + self.throttleStart.emit() + self._logger.debug("Started to throttle requests") + loop.call_at(times[0] + self.RequestsInterval, self.sendMsg, None) + else: + if self._isThrottling: + self._isThrottling = False + self.throttleEnd.emit() + self._logger.debug("Stopped to throttle requests") + def reqMktDepth(self, reqId, contract, numRows, isSmartDepth, mktDepthOptions): self.send( 10, @@ -791,14 +881,25 @@ def cancelNewsBulletins(self): def setServerLogLevel(self, logLevel): self.send(14, 1, logLevel) - def reqAutoOpenOrders(self, bAutoBind): - self.send(15, 1, bAutoBind) + def reqAutoOpenOrders(self, bAutoBind: bool): + autoOpenOrdersRequestProto = AutoOpenOrdersRequestProto() + autoOpenOrdersRequestProto.autoBind = bAutoBind + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_AUTO_OPEN_ORDERS), + autoOpenOrdersRequestProto, + ) def reqAllOpenOrders(self): - self.send(16, 1) + allOpenOrdersRequestProto = AllOpenOrdersRequestProto() + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_ALL_OPEN_ORDERS), + allOpenOrdersRequestProto, + ) def reqManagedAccts(self): - self.send(17, 1) + self._logger.debug("Calling reqManagedAccts (Protobuf)") + proto = ManagedAccountsRequestProto() + self.sendProto(MessageId.to_protobuf(MessageId.OUT.REQ_MANAGED_ACCTS), proto) def requestFA(self, faData): self.send(18, 1, faData) @@ -819,27 +920,28 @@ def reqHistoricalData( keepUpToDate, chartOptions, ): - fields = [ - 20, + historicalDataRequestProto = createHistoricalDataRequestProto( reqId, contract, - contract.includeExpired, endDateTime, - barSizeSetting, durationStr, - useRTH, + barSizeSetting, whatToShow, + useRTH, formatDate, - ] - - if contract.secType == "BAG": - legs = contract.comboLegs or [] - fields += [len(legs)] - for leg in legs: - fields += [leg.conId, leg.ratio, leg.action, leg.exchange] + keepUpToDate, + chartOptions, + ) - fields += [keepUpToDate, chartOptions] - self.send(*fields) + # if contract.secType == "BAG": + # legs = contract.comboLegs or [] + # fields += [len(legs)] + # for leg in legs: + # fields += [leg.conId, leg.ratio, leg.action, leg.exchange] + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_HISTORICAL_DATA), + historicalDataRequestProto, + ) def exerciseOptions( self, reqId, contract, exerciseAction, exerciseQuantity, account, override @@ -908,10 +1010,25 @@ def reqScannerParameters(self): self.send(24, 1) def cancelHistoricalData(self, reqId): - self.send(25, 1, reqId) + cancelRequest = CancelHistoricalDataProto() + cancelRequest.reqId = reqId + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.CANCEL_HISTORICAL_DATA), + cancelRequest) def reqCurrentTime(self): - self.send(49, 1) + currentTimeRequestProto = CurrentTimeRequestProto() + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_CURRENT_TIME), + currentTimeRequestProto, + ) + + def reqCurrentTimeMili(self): + currentTimeMiliRequestProto = CurrentTimeInMillisRequestProto() + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_CURRENT_TIME_IN_MILLIS), + currentTimeMiliRequestProto, + ) def reqRealTimeBars( self, reqId, contract, barSize, whatToShow, useRTH, realTimeBarsOptions @@ -985,16 +1102,26 @@ def reqMarketDataType(self, marketDataType): self.send(59, 1, marketDataType) def reqPositions(self): - self.send(61, 1) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_POSITIONS), PositionsRequestProto() + ) + + def cancelPositions(self): + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.CANCEL_POSITIONS), + CancelPositionsProto(), + ) def reqAccountSummary(self, reqId, groupName, tags): - self.send(62, 1, reqId, groupName, tags) + proto = AccountSummaryRequestProto(reqId=reqId, group=groupName, tags=tags) + self.sendProto(MessageId.to_protobuf(MessageId.OUT.REQ_ACCOUNT_SUMMARY), proto) def cancelAccountSummary(self, reqId): - self.send(63, 1, reqId) - - def cancelPositions(self): - self.send(64, 1) + cancelAccountSummaryProto = CancelAccountSummaryProto(reqId=reqId) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.CANCEL_ACCOUNT_SUMMARY), + cancelAccountSummaryProto, + ) def verifyRequest(self, apiName, apiVersion): self.send(65, 1, apiName, apiVersion) @@ -1015,7 +1142,14 @@ def unsubscribeFromGroupEvents(self, reqId): self.send(70, 1, reqId) def startApi(self): - self.send(71, 2, self.clientId, self.optCapab) + startApiRequestProto = StartApiRequestProto() + if self.clientId >= 0: # Assuming 0 or negative clientId is invalid + startApiRequestProto.clientId = self.clientId + if self.optCapab: # Only set if not an empty string + startApiRequestProto.optionalCapabilities = self.optCapab + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.START_API), startApiRequestProto + ) def verifyAndAuthRequest(self, apiName, apiVersion, opaqueIsvKey): self.send(72, 1, apiName, apiVersion, opaqueIsvKey) @@ -1029,11 +1163,22 @@ def reqPositionsMulti(self, reqId, account, modelCode): def cancelPositionsMulti(self, reqId): self.send(75, 1, reqId) - def reqAccountUpdatesMulti(self, reqId, account, modelCode, ledgerAndNLV): - self.send(76, 1, reqId, account, modelCode, ledgerAndNLV) + def reqAccountUpdatesMulti( + self, reqId: int, account: str, modelCode: str, ledgerAndNLV: bool + ): + reqAccUpdatesMultiProto = createAccountMultiRequestProto( + reqId, account, modelCode, ledgerAndNLV + ) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_ACCOUNT_UPDATES_MULTI), + reqAccUpdatesMultiProto, + ) def cancelAccountUpdatesMulti(self, reqId): - self.send(77, 1, reqId) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.CANCEL_ACCOUNT_UPDATES_MULTI), + createCancelAccMultiRequestProto(reqId), + ) def reqSecDefOptParams( self, @@ -1043,14 +1188,17 @@ def reqSecDefOptParams( underlyingSecType, underlyingConId, ): - self.send( - 78, + secDefOptParamsRequestProto = createSecDefOptParamsRequestProto( reqId, underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId, ) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_SEC_DEF_OPT_PARAMS), + secDefOptParamsRequestProto, + ) def reqSoftDollarTiers(self, reqId): self.send(79, reqId) @@ -1059,7 +1207,11 @@ def reqFamilyCodes(self): self.send(80) def reqMatchingSymbols(self, reqId, pattern): - self.send(81, reqId, pattern) + matchingSymbolsRequestProto = createMatchingSymbolsRequestProto(reqId, pattern) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_MATCHING_SYMBOLS), + matchingSymbolsRequestProto, + ) def reqMktDepthExchanges(self): self.send(82) @@ -1095,8 +1247,20 @@ def reqHistoricalNews( ) def reqHeadTimeStamp(self, reqId, contract, whatToShow, useRTH, formatDate): - self.send( - 87, reqId, contract, contract.includeExpired, useRTH, whatToShow, formatDate + headTimestampRequestProto = createHeadTimestampRequestProto( + reqId, contract, whatToShow, useRTH != 0, formatDate + ) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_HEAD_TIMESTAMP), + headTimestampRequestProto, + ) + + def cancelHeadTimeStamp(self, reqId): + cancelHeadTimestampRequestProto = CancelHeadTimestampProto() + cancelHeadTimestampRequestProto.reqId = reqId + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.CANCEL_HEAD_TIMESTAMP), + cancelHeadTimestampRequestProto, ) def reqHistogramData(self, tickerId, contract, useRTH, timePeriod): @@ -1105,11 +1269,11 @@ def reqHistogramData(self, tickerId, contract, useRTH, timePeriod): def cancelHistogramData(self, tickerId): self.send(89, tickerId) - def cancelHeadTimeStamp(self, reqId): - self.send(90, reqId) - - def reqMarketRule(self, marketRuleId): - self.send(91, marketRuleId) + def reqMarketRule(self, marketRuleId: int): + marketRuleRequestProto = createMarketRuleRequestProto(marketRuleId) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_MARKET_RULE), marketRuleRequestProto + ) def reqPnL(self, reqId, account, modelCode): self.send(92, reqId, account, modelCode) @@ -1155,8 +1319,13 @@ def reqTickByTickData(self, reqId, contract, tickType, numberOfTicks, ignoreSize def cancelTickByTickData(self, reqId): self.send(98, reqId) - def reqCompletedOrders(self, apiOnly): - self.send(99, apiOnly) + def reqCompletedOrders(self, apiOnly: bool): + completedOrdersRequestProto = CompletedOrdersRequestProto() + completedOrdersRequestProto.apiOnly = apiOnly + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_COMPLETED_ORDERS), + completedOrdersRequestProto, + ) def reqWshMetaData(self, reqId): self.send(100, reqId) diff --git a/ib_async/connection.py b/ib_async/connection.py index baebffdb..dd991fc7 100644 --- a/ib_async/connection.py +++ b/ib_async/connection.py @@ -18,6 +18,7 @@ class Connection(asyncio.Protocol): Is emitted on socket disconnect, with an error message in case of error, or an empty string in case of a normal disconnect. """ + transport:asyncio.Transport|None def __init__(self): self.hasData = Event("hasData") diff --git a/ib_async/contract.py b/ib_async/contract.py index 3c40d11b..18a98efe 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -2,11 +2,30 @@ import datetime as dt from dataclasses import dataclass, field +from enum import Enum from typing import List, NamedTuple, Optional import ib_async.util as util +class FundAssetType(Enum): + NoneItem = ("None", "None") + Others = (("000", "Others"),) + MoneyMarket = ("001", "Money Market") + FixedIncome = ("002", "Fixed Income") + MultiAsset = ("003", "Multi-asset") + Equity = ("004", "Equity") + Sector = ("005", "Sector") + Guaranteed = ("006", "Guaranteed") + Alternative = ("007", "Alternative") + + +class FundDistributionPolicyIndicator(Enum): + NoneItem = ("None", "None") + AccumulationFund = ("N", "Accumulation Fund") + IncomeFund = ("Y", "Income Fund") + + @dataclass class Contract: """ @@ -82,10 +101,11 @@ class Contract: price for Delta-Neutral combo orders. """ - secType: str = "" conId: int = 0 symbol: str = "" + secType: str = "" lastTradeDateOrContractMonth: str = "" + lastTradeDate = "" strike: float = 0.0 right: str = "" multiplier: str = "" @@ -100,7 +120,7 @@ class Contract: description: str = "" issuerId: str = "" comboLegsDescrip: str = "" - comboLegs: List["ComboLeg"] = field(default_factory=list) + comboLegs: list["ComboLeg"] = field(default_factory=list) deltaNeutralContract: Optional["DeltaNeutralContract"] = None @staticmethod @@ -250,7 +270,7 @@ def __init__( """ Contract.__init__( self, - "OPT", + secType="OPT", symbol=symbol, lastTradeDateOrContractMonth=lastTradeDateOrContractMonth, strike=strike, @@ -290,7 +310,7 @@ def __init__( """ Contract.__init__( self, - "FUT", + secType="FUT", symbol=symbol, lastTradeDateOrContractMonth=lastTradeDateOrContractMonth, exchange=exchange, @@ -323,7 +343,7 @@ def __init__( """ Contract.__init__( self, - "CONTFUT", + secType="CONTFUT", symbol=symbol, exchange=exchange, localSymbol=localSymbol, @@ -357,7 +377,7 @@ def __init__( currency = currency or pair[3:] Contract.__init__( - self, "CASH", symbol=symbol, exchange=exchange, currency=currency, **kwargs + self, secType="CASH", symbol=symbol, exchange=exchange, currency=currency, **kwargs ) def __repr__(self): @@ -392,7 +412,7 @@ def __init__( currency: Underlying currency. """ Contract.__init__( - self, "IND", symbol=symbol, exchange=exchange, currency=currency, **kwargs + self, secType="IND", symbol=symbol, exchange=exchange, currency=currency, **kwargs ) @@ -409,7 +429,7 @@ def __init__( currency: Underlying currency. """ Contract.__init__( - self, "CFD", symbol=symbol, exchange=exchange, currency=currency, **kwargs + self, secType="CFD", symbol=symbol, exchange=exchange, currency=currency, **kwargs ) @@ -426,14 +446,14 @@ def __init__( currency: Underlying currency. """ Contract.__init__( - self, "CMDTY", symbol=symbol, exchange=exchange, currency=currency, **kwargs + self, secType="CMDTY", symbol=symbol, exchange=exchange, currency=currency, **kwargs ) class Bond(Contract): def __init__(self, **kwargs): """Bond.""" - Contract.__init__(self, "BOND", **kwargs) + Contract.__init__(self, secType="BOND", **kwargs) class FuturesOption(Contract): @@ -467,7 +487,7 @@ def __init__( """ Contract.__init__( self, - "FOP", + secType="FOP", symbol=symbol, lastTradeDateOrContractMonth=lastTradeDateOrContractMonth, strike=strike, @@ -482,19 +502,19 @@ def __init__( class MutualFund(Contract): def __init__(self, **kwargs): """Mutual fund.""" - Contract.__init__(self, "FUND", **kwargs) + Contract.__init__(self, secType="FUND", **kwargs) class Warrant(Contract): def __init__(self, **kwargs): """Warrant option.""" - Contract.__init__(self, "WAR", **kwargs) + Contract.__init__(self, secType="WAR", **kwargs) class Bag(Contract): def __init__(self, **kwargs): """Bag contract.""" - Contract.__init__(self, "BAG", **kwargs) + Contract.__init__(self, secType="BAG", **kwargs) class Crypto(Contract): @@ -595,6 +615,28 @@ class ContractDetails: nextOptionType: str = "" nextOptionPartial: bool = False notes: str = "" + # FUND values + fundName = "" + fundFamily = "" + fundType = "" + fundFrontLoad = "" + fundBackLoad = "" + fundBackLoadTimeInterval = "" + fundManagementFee = "" + fundClosed = False + fundClosedForNewInvestors = False + fundClosedForNewMoney = False + fundNotifyAmount = "" + fundMinimumInitialPurchase = "" + fundSubsequentMinimumPurchase = "" + fundBlueSkyStates = "" + fundBlueSkyTerritories = "" + fundDistributionPolicyIndicator = FundDistributionPolicyIndicator.NoneItem + fundAssetType = FundAssetType.NoneItem + ineligibilityReasonList = None + eventContract1 = "" + eventContractDescription1 = "" + eventContractDescription2 = "" def tradingSessions(self) -> List[TradingSession]: return self._parseSessions(self.tradingHours) diff --git a/ib_async/decoder.py b/ib_async/decoder.py index 0b843919..f0011a59 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -1,22 +1,19 @@ """Deserialize and dispatch messages.""" -import dataclasses import logging from datetime import datetime -from typing import Any, cast +from typing import Any from .contract import ( - ComboLeg, Contract, ContractDescription, ContractDetails, DeltaNeutralContract, ) +from .message import MessageId from .objects import ( BarData, - CommissionReport, DepthMktDataDescription, - Execution, FamilyCode, HistogramData, HistoricalSession, @@ -31,317 +28,340 @@ TickAttribBidAsk, TickAttribLast, ) -from .order import Order, OrderComboLeg, OrderCondition, OrderState -from .util import parseIBDatetime, UNSET_DOUBLE, ZoneInfo +from .order import OrderStatus +from .protobuf.AccountDataEnd_pb2 import AccountDataEnd as AccountDataEndProto +from .protobuf.AccountSummary_pb2 import AccountSummary as AccountSummaryProto +from .protobuf.AccountSummaryEnd_pb2 import ( + AccountSummaryEnd as AccountSummaryEndProto, +) +from .protobuf.AccountUpdateMulti_pb2 import ( + AccountUpdateMulti as AccountUpdateMultiProto, +) +from .protobuf.AccountUpdateMultiEnd_pb2 import ( + AccountUpdateMultiEnd as AccountUpdateMultiEndProto, +) +from .protobuf.AccountUpdateTime_pb2 import AccountUpdateTime as AccountUpdateTimeProto +from .protobuf.AccountValue_pb2 import AccountValue as AccountValueProto +from .protobuf.CommissionAndFeesReport_pb2 import ( + CommissionAndFeesReport as CommissionReportProto, +) +from .protobuf.CompletedOrder_pb2 import CompletedOrder as CompletedOrderProto +from .protobuf.CompletedOrdersEnd_pb2 import ( + CompletedOrdersEnd as CompletedOrdersEndProto, +) +from .protobuf.ContractData_pb2 import ContractData as ContractDataProto +from .protobuf.ContractDataEnd_pb2 import ContractDataEnd as ContractDataEndProto +from .protobuf.CurrentTime_pb2 import CurrentTime as CurrentTimeProto +from .protobuf.CurrentTimeInMillis_pb2 import ( + CurrentTimeInMillis as CurrentTimeInMillisProto, +) +from .protobuf.ErrorMessage_pb2 import ErrorMessage as ErrorMessageProto +from .protobuf.ExecutionDetails_pb2 import ExecutionDetails as ExecutionDetailsProto +from .protobuf.ExecutionDetailsEnd_pb2 import ( + ExecutionDetailsEnd as ExecutionDetailsEndProto, +) +from .protobuf.HeadTimestamp_pb2 import HeadTimestamp as HeadTimestampProto +from .protobuf.HistoricalData_pb2 import HistoricalData as HistoricalDataProto +from .protobuf.HistoricalDataEnd_pb2 import HistoricalDataEnd as HistoricalDataEndProto +from .protobuf.ManagedAccounts_pb2 import ManagedAccounts as ManagedAccountsProto +from .protobuf.MarketRule_pb2 import MarketRule as MarketRuleProto +from .protobuf.NextValidId_pb2 import NextValidId as NextValidIdProto +from .protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto +from .protobuf.OpenOrdersEnd_pb2 import OpenOrdersEnd as OpenOrderEndProto +from .protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto +from .protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto +from .protobuf.Position_pb2 import Position as PositionProto +from .protobuf.PositionEnd_pb2 import PositionEnd as PositionEndProto +from .protobuf.SecDefOptParameter_pb2 import ( + SecDefOptParameter as SecDefOptParameterProto, +) +from .protobuf.SecDefOptParameterEnd_pb2 import ( + SecDefOptParameterEnd as SecDefOptParameterEndProto, +) +from .protobuf.SymbolSamples_pb2 import SymbolSamples as SymbolSamplesProto +from .protobuf_converters.account_converters import ( + createAccountSummary, + createAccountValue, + createAccountValueFromUpdateMulti, + createPortfolioItem, + createPosition, +) +from .protobuf_converters.contract_converters import ( + createContractDescription, + createContractDetails, + createOptionChain, +) +from .protobuf_converters.trade_converter import ( + createCommissionReport, + createContract, + createExecution, + createFill, + createOrder, + createOrderState, + createOrderStatus, + createTradeFromOpenOrder, +) +from .protobuf_converters.historical_data_converters import createBarDataList +from .util import UNSET_DOUBLE, ZoneInfo, parseIBDatetime from .wrapper import Wrapper class Decoder: """Decode IB messages and invoke corresponding wrapper methods.""" + PROTOBUF_MESSAGE_HANDLERS = { + # Handles NEXT_VALID_ID message during handshake + MessageId.IN.NEXT_VALID_ID: (NextValidIdProto, "nextValidIdProto"), + # Handles incoming contract details for reqContractDetails + MessageId.IN.CONTRACT_DATA: (ContractDataProto, "contractDetailsProto"), + # Signals the end of a contract details stream + MessageId.IN.CONTRACT_DATA_END: ( + ContractDataEndProto, + "contractDetailsEndProto", + ), + # Handles the response for reqMarketRule + MessageId.IN.MARKET_RULE: (MarketRuleProto, "marketRuleProto"), + # Handles the response for reqMatchingSymbols + MessageId.IN.SYMBOL_SAMPLES: (SymbolSamplesProto, "symbolSamplesProto"), + # Handles incoming security definition option parameters for reqSecDefOptParams + MessageId.IN.SECURITY_DEFINITION_OPTION_PARAMETER: ( + SecDefOptParameterProto, + "secDefOptParameterProto", + ), + # Signals the end of a security definition option parameters stream + MessageId.IN.SECURITY_DEFINITION_OPTION_PARAMETER_END: ( + SecDefOptParameterEndProto, + "secDefOptParameterEndProto", + ), + # Handles incoming error messages + MessageId.IN.ERR_MSG: (ErrorMessageProto, "errorMessageProto"), + # Handles incoming managed accounts list + MessageId.IN.MANAGED_ACCTS: (ManagedAccountsProto, "managedAccountsProto"), + # Handles incoming account value updates + MessageId.IN.ACCT_VALUE: (AccountValueProto, "accountValueProto"), + MessageId.IN.ACCT_DOWNLOAD_END: ( + AccountDataEndProto, + "accountDownloadEnd", + ), + # Handles incoming position updates + MessageId.IN.POSITION_DATA: (PositionProto, "positionProto"), + # Signals the end of a position stream + MessageId.IN.POSITION_END: (PositionEndProto, "positionEndProto"), + # Handles incoming multi-account updates + MessageId.IN.ACCOUNT_UPDATE_MULTI: ( + AccountUpdateMultiProto, + "accountUpdateMultiProto", + ), + # Signals the end of a multi-account update stream + MessageId.IN.ACCOUNT_UPDATE_MULTI_END: ( + AccountUpdateMultiEndProto, + "accountUpdateMultiEndProto", + ), + MessageId.IN.OPEN_ORDER: (OpenOrderProto, "openOrderProto"), + MessageId.IN.OPEN_ORDER_END: (OpenOrderEndProto, "openOrderEndProto"), + MessageId.IN.EXECUTION_DATA: (ExecutionDetailsProto, "execDetailsProto"), + MessageId.IN.EXECUTION_DATA_END: ( + ExecutionDetailsEndProto, + "execDetailsEndProto", + ), + MessageId.IN.ACCOUNT_SUMMARY: (AccountSummaryProto, "accountSummaryProto"), + MessageId.IN.ACCOUNT_SUMMARY_END: ( + AccountSummaryEndProto, + "accountSummaryEndProto", + ), + MessageId.IN.ORDER_STATUS: (OrderStatusProto, "orderStatusProto"), + MessageId.IN.COMPLETED_ORDER: (CompletedOrderProto, "completedOrderProto"), + MessageId.IN.COMPLETED_ORDERS_END: ( + CompletedOrdersEndProto, + "completedOrdersEndProto", + ), + MessageId.IN.PORTFOLIO_VALUE: (PortfolioValueProto, "updatePortfolioProto"), + MessageId.IN.ACCT_UPDATE_TIME: ( + AccountUpdateTimeProto, + "updateAccountTimeProto", + ), + MessageId.IN.COMMISSION_AND_FEES_REPORT: ( + CommissionReportProto, + "commissionReportProto", + ), + MessageId.IN.CURRENT_TIME: (CurrentTimeProto, "currentTimeProto"), + MessageId.IN.CURRENT_TIME_IN_MILLIS: ( + CurrentTimeInMillisProto, + "currentTimeMiliProto", + ), + MessageId.IN.HEAD_TIMESTAMP: (HeadTimestampProto,"headTimestampProto"), + MessageId.IN.HISTORICAL_DATA: (HistoricalDataProto, "historicalDataProto"), + MessageId.IN.HISTORICAL_DATA_END: (HistoricalDataEndProto, "historicalDataProtoEnd"), + } + def __init__(self, wrapper: Wrapper, serverVersion: int): self.wrapper = wrapper self.serverVersion = serverVersion self.logger = logging.getLogger("ib_async.Decoder") - self.handlers = { - 1: self.priceSizeTick, - 2: self.wrap("tickSize", [int, int, float]), - 3: self.wrap( - "orderStatus", - [int, str, float, float, float, int, int, float, int, str, float], - skip=1, - ), - 4: self.errorMsg, - 5: self.openOrder, - 6: self.wrap("updateAccountValue", [str, str, str, str]), - 7: self.updatePortfolio, - 8: self.wrap("updateAccountTime", [str]), - 9: self.wrap("nextValidId", [int]), - 10: self.contractDetails, - 11: self.execDetails, - 12: self.wrap("updateMktDepth", [int, int, int, int, float, float]), - 13: self.wrap( - "updateMktDepthL2", [int, int, str, int, int, float, float, bool] - ), - 14: self.wrap("updateNewsBulletin", [int, int, str, str]), - 15: self.wrap("managedAccounts", [str]), - 16: self.wrap("receiveFA", [int, str]), - 17: self.historicalData, - 18: self.bondContractDetails, - 19: self.wrap("scannerParameters", [str]), - 20: self.scannerData, - 21: self.tickOptionComputation, - 45: self.wrap("tickGeneric", [int, int, float]), - 46: self.wrap("tickString", [int, int, str]), - 47: self.wrap( - "tickEFP", [int, int, float, str, float, int, str, float, float] - ), - 49: self.wrap("currentTime", [int]), - 50: self.wrap( - "realtimeBar", [int, int, float, float, float, float, float, float, int] - ), - 51: self.wrap("fundamentalData", [int, str]), - 52: self.wrap("contractDetailsEnd", [int]), - 53: self.wrap("openOrderEnd", []), - 54: self.wrap("accountDownloadEnd", [str]), - 55: self.wrap("execDetailsEnd", [int]), - 56: self.deltaNeutralValidation, - 57: self.wrap("tickSnapshotEnd", [int]), - 58: self.wrap("marketDataType", [int, int]), - 59: self.commissionReport, - 61: self.position, - 62: self.wrap("positionEnd", []), - 63: self.wrap("accountSummary", [int, str, str, str, str]), - 64: self.wrap("accountSummaryEnd", [int]), - 65: self.wrap("verifyMessageAPI", [str]), - 66: self.wrap("verifyCompleted", [bool, str]), - 67: self.wrap("displayGroupList", [int, str]), - 68: self.wrap("displayGroupUpdated", [int, str]), - 69: self.wrap("verifyAndAuthMessageAPI", [str, str]), - 70: self.wrap("verifyAndAuthCompleted", [bool, str]), - 71: self.positionMulti, - 72: self.wrap("positionMultiEnd", [int]), - 73: self.wrap("accountUpdateMulti", [int, str, str, str, str, str]), - 74: self.wrap("accountUpdateMultiEnd", [int]), - 75: self.securityDefinitionOptionParameter, - 76: self.wrap("securityDefinitionOptionParameterEnd", [int], skip=1), - 77: self.softDollarTiers, - 78: self.familyCodes, - 79: self.symbolSamples, - 80: self.mktDepthExchanges, - 81: self.wrap("tickReqParams", [int, float, str, int], skip=1), - 82: self.smartComponents, - 83: self.wrap("newsArticle", [int, int, str], skip=1), - 84: self.wrap("tickNews", [int, int, str, str, str, str], skip=1), - 85: self.newsProviders, - 86: self.wrap("historicalNews", [int, str, str, str, str], skip=1), - 87: self.wrap("historicalNewsEnd", [int, bool], skip=1), - 88: self.wrap("headTimestamp", [int, str], skip=1), - 89: self.histogramData, - 90: self.historicalDataUpdate, - 91: self.wrap("rerouteMktDataReq", [int, int, str], skip=1), - 92: self.wrap("rerouteMktDepthReq", [int, int, str], skip=1), - 93: self.marketRule, - 94: self.wrap("pnl", [int, float, float, float], skip=1), - 95: self.wrap( - "pnlSingle", [int, float, float, float, float, float], skip=1 - ), - 96: self.historicalTicks, - 97: self.historicalTicksBidAsk, - 98: self.historicalTicksLast, - 99: self.tickByTick, - 100: self.wrap("orderBound", [int, int, int], skip=1), - 101: self.completedOrder, - 102: self.wrap("completedOrdersEnd", [], skip=1), - 103: self.wrap("replaceFAEnd", [int, str], skip=1), - 104: self.wrap("wshMetaData", [int, str], skip=1), - 105: self.wrap("wshEventData", [int, str], skip=1), - 106: self.historicalSchedule, - 107: self.wrap("userInfo", [int, str], skip=1), - } - - def wrap(self, methodName, types, skip=2): - """ - Create a message handler that invokes a wrapper method - with the in-order message fields as parameters, skipping over - the first ``skip`` fields, and parsed according to the ``types`` list. - """ - - def handler(fields): - method = getattr(self.wrapper, methodName, None) - if method: - try: - args = [ - ( - field - if typ is str - else ( - int(field or 0) - if typ is int - else ( - float(field or 0) - if typ is float - else bool(int(field or 0)) - ) - ) - ) - for (typ, field) in zip(types, fields[skip:]) - ] - method(*args) - except Exception: - self.logger.exception(f"Error for {methodName}({args}):") - - return handler - - def interpret(self, fields): - """Decode fields and invoke corresponding wrapper method.""" + + def processProtoBuf(self, payload: bytes): + """Process a binary Protobuf message payload.""" try: - msgId = int(fields[0]) - handler = self.handlers[msgId] - handler(fields) + msgId_raw = int.from_bytes(payload[:4], "big") + data = payload[4:] + msgId = MessageId.from_protobuf(msgId_raw) + handler_info = self.PROTOBUF_MESSAGE_HANDLERS.get(msgId) + if handler_info: + proto_class, handler_method_name = handler_info + proto_message = proto_class() + proto_message.ParseFromString(data) + handler_method = getattr(self, handler_method_name) + handler_method(proto_message) + else: + self.logger.warning("Unknown Protobuf message id: %s", msgId) except Exception: - self.logger.exception(f"Error handling fields: {fields}") - - def parse(self, obj): - """Parse the object's properties according to its default types.""" - for field in dataclasses.fields(obj): - typ = type(field.default) - if typ is str: - continue - v = getattr(obj, field.name) - - if typ is int: - setattr(obj, field.name, int(v) if v else field.default) - elif typ is float: - setattr(obj, field.name, float(v) if v else field.default) - elif typ is bool: - setattr(obj, field.name, bool(int(v)) if v else field.default) + self.logger.exception("Error processing Protobuf message id: %s", msgId) + + def errorMessageProto(self, msg: ErrorMessageProto): + reqId = msg.id + errorCode = msg.errorCode + errorMsg = msg.errorMsg + advancedOrderRejectJson = msg.advancedOrderRejectJson + + self.wrapper.error(reqId, errorCode, errorMsg, advancedOrderRejectJson) + + def contractDetailsProto(self, msg: ContractDataProto): + reqId = msg.reqId + contractDetails = createContractDetails(msg) + self.wrapper.contractDetails(reqId, contractDetails) + + def contractDetailsEndProto(self, msg: ContractDataEndProto): + self.wrapper.contractDetailsEnd(msg.reqId) + + def symbolSamplesProto(self, msg: SymbolSamplesProto): + reqId = msg.reqId + contractDescriptions: list[ContractDescription] = [] + for contractDescriptionProto in msg.contractDescriptions: + contractDescriptions.append( + createContractDescription(contractDescriptionProto) + ) + self.wrapper.symbolSamples(reqId, contractDescriptions) + + def marketRuleProto(self, msg: MarketRuleProto): + reqId = msg.marketRuleId + priceIncrements: list[PriceIncrement] = [] + for priceIncrementProto in msg.priceIncrements: + priceIncrements.append( + PriceIncrement( + lowEdge=priceIncrementProto.lowEdge, + increment=priceIncrementProto.increment, + ) + ) + self.wrapper.marketRule(reqId, priceIncrements) - def priceSizeTick(self, fields): - _, _, reqId, tickType, price, size, _ = fields + def secDefOptParameterProto(self, msg: SecDefOptParameterProto): + reqId = msg.reqId + optionChain = createOptionChain(msg) + self.wrapper.response_bus.emit(reqId, optionChain) - if price: - self.wrapper.priceSizeTick( - int(reqId), int(tickType), float(price), float(size or 0) - ) + def secDefOptParameterEndProto(self, msg: SecDefOptParameterEndProto): + self.wrapper.response_bus.emit(msg.reqId, None) - def errorMsg(self, fields): - _, _, reqId, errorCode, errorString, *fields = fields - advancedOrderRejectJson = "" - if self.serverVersion >= 166: - advancedOrderRejectJson, *fields = fields + def managedAccountsProto(self, msg: ManagedAccountsProto): + accountsList = msg.accountsList + self.wrapper.managedAccounts(accountsList) - self.wrapper.error( - int(reqId), int(errorCode), errorString, advancedOrderRejectJson - ) + def accountValueProto(self, msg: AccountValueProto): + accountValue = createAccountValue(msg) + self.wrapper.updateAccountValue(accountValue) - def updatePortfolio(self, fields): - c = Contract() - ( - _, - _, - c.conId, - c.symbol, - c.secType, - c.lastTradeDateOrContractMonth, - c.strike, - c.right, - c.multiplier, - c.primaryExchange, - c.currency, - c.localSymbol, - c.tradingClass, - position, - marketPrice, - marketValue, - averageCost, - unrealizedPNL, - realizedPNL, - accountName, - ) = fields + def accountDownloadEnd(self, msg: str): + self.wrapper.accountDownloadEnd(msg) - self.parse(c) - self.wrapper.updatePortfolio( - c, - float(position), - float(marketPrice), - float(marketValue), - float(averageCost), - float(unrealizedPNL), - float(realizedPNL), - accountName, - ) + def positionProto(self, msg: PositionProto): + position = createPosition(msg) + self.wrapper.position(position) - def contractDetails(self, fields): - cd = ContractDetails() - cd.contract = c = Contract() - if self.serverVersion < 164: - fields.pop(0) - ( - _, - reqId, - c.symbol, - c.secType, - lastTimes, - c.strike, - c.right, - c.exchange, - c.currency, - c.localSymbol, - cd.marketName, - c.tradingClass, - c.conId, - cd.minTick, - *fields, - ) = fields - if self.serverVersion < 164: - fields.pop(0) # obsolete mdSizeMultiplier + def positionEndProto(self, msg: PositionEndProto): + self.wrapper.positionEnd() - ( - c.multiplier, - cd.orderTypes, - cd.validExchanges, - cd.priceMagnifier, - cd.underConId, - cd.longName, - c.primaryExchange, - cd.contractMonth, - cd.industry, - cd.category, - cd.subcategory, - cd.timeZoneId, - cd.tradingHours, - cd.liquidHours, - cd.evRule, - cd.evMultiplier, - numSecIds, - *fields, - ) = fields + def updatePortfolioProto(self, msg: PortfolioValueProto): + portfolioItem = createPortfolioItem(msg) + self.wrapper.updatePortfolio(portfolioItem) - numSecIds = int(numSecIds) - if numSecIds > 0: - cd.secIdList = [] - for _ in range(numSecIds): - tag, value, *fields = fields - cd.secIdList += [TagValue(tag, value)] + def accountUpdateMultiProto(self, msg: AccountUpdateMultiProto): + accountValue = createAccountValueFromUpdateMulti(msg) + self.wrapper.accountUpdateMulti(msg.reqId, accountValue) - ( - cd.aggGroup, - cd.underSymbol, - cd.underSecType, - cd.marketRuleIds, - cd.realExpirationDate, - cd.stockType, - *fields, - ) = fields + def accountUpdateMultiEndProto(self, msg: AccountUpdateMultiEndProto): + self.wrapper.accountUpdateMultiEnd(msg.reqId) - if self.serverVersion == 163: - cd.suggestedSizeIncrement, *fields = fields + def accountSummaryProto(self, msg: AccountSummaryProto): + accountValue = createAccountSummary(msg) + self.wrapper.accountSummary(msg.reqId, accountValue) - if self.serverVersion >= 164: - ( - cd.minSize, - cd.sizeIncrement, - cd.suggestedSizeIncrement, - # cd.minCashQtySize, - *fields, - ) = fields + def accountSummaryEndProto(self, msg: AccountSummaryEndProto): + self.wrapper.accountSummaryEnd(msg.reqId) - times = lastTimes.split("-" if "-" in lastTimes else None) + def nextValidIdProto(self, msg: NextValidIdProto): + self.wrapper.nextValidId(msg.orderId) - if len(times) > 0: - c.lastTradeDateOrContractMonth = times[0] + def openOrderProto(self, msg: OpenOrderProto): + trade = createTradeFromOpenOrder(msg) + self.wrapper.openOrder(trade) - if len(times) > 1: - cd.lastTradeTime = times[1] + def openOrderEndProto(self, msg: OpenOrderEndProto): + self.wrapper.openOrderEnd() - if len(times) > 2: - cd.timeZoneId = times[2] + def execDetailsProto(self, msg: ExecutionDetailsProto): + fill = createFill(msg) + self.wrapper.execDetails(msg.reqId, fill) - cd.longName = cd.longName.encode().decode("unicode-escape") - self.parse(cd) - self.parse(c) - self.wrapper.contractDetails(int(reqId), cd) + def execDetailsEndProto(self, msg: ExecutionDetailsEndProto): + self.wrapper.execDetailsEnd(msg.reqId) + + def orderStatusProto(self, msg: OrderStatusProto): + orderStatus = createOrderStatus(msg) + self.wrapper.orderStatus(orderStatus) + + def completedOrderProto(self, msg: CompletedOrderProto): + contract = createContract(msg.contract) + order = createOrder(msg.order.orderId, msg.contract, msg.order) + order_status = OrderStatus(orderId=order.orderId, status=msg.orderState.status) + self.wrapper.completedOrder(contract, order, order_status) + + def completedOrdersEndProto(self, msg: CompletedOrdersEndProto): + self.wrapper.completedOrdersEnd() + + def updateAccountTimeProto(self, msg: AccountUpdateTimeProto): + time = msg.timeStamp if msg.HasField("timeStamp") else "" + self.wrapper.updateAccountTime(time) + + def commissionReportProto(self, msg: CommissionReportProto): + commissionReport = createCommissionReport(msg) + self.wrapper.commissionReport(commissionReport) + + def currentTimeProto(self, msg: CurrentTimeProto): + self.wrapper.currentTime(msg.currentTime) + + def currentTimeMiliProto(self, msg: CurrentTimeInMillisProto): + self.wrapper.currentTimeMili(msg.currentTimeInMillis) + + def headTimestampProto(self, msg: HeadTimestampProto): + self.wrapper.headTimestamp(msg.reqId, msg.headTimestamp) + + + def historicalDataProto(self, msg: HistoricalDataProto): + bar_data = createBarDataList(msg.historicalDataBars) + self.wrapper.historicalData(msg.reqId, bar_data) + + def historicalDataProtoEnd(self, msg: HistoricalDataEndProto): + self.wrapper.historicalDataEnd(msg.reqId, msg.startDateStr, msg.endDateStr) + + ##################### legacy methods ########################################## + + def priceSizeTick(self, fields): + _, _, reqId, tickType, price, size, _ = fields + + if price: + self.wrapper.priceSizeTick( + int(reqId), int(tickType), float(price), float(size or 0) + ) def bondContractDetails(self, fields): cd = ContractDetails() @@ -423,57 +443,6 @@ def bondContractDetails(self, fields): self.parse(c) self.wrapper.bondContractDetails(int(reqId), cd) - def execDetails(self, fields): - c = Contract() - ex = Execution() - ( - _, - reqId, - ex.orderId, - c.conId, - c.symbol, - c.secType, - c.lastTradeDateOrContractMonth, - c.strike, - c.right, - c.multiplier, - c.exchange, - c.currency, - c.localSymbol, - c.tradingClass, - ex.execId, - timeStr, - ex.acctNumber, - ex.exchange, - ex.side, - ex.shares, - ex.price, - ex.permId, - ex.clientId, - ex.liquidation, - ex.cumQty, - ex.avgPrice, - ex.orderRef, - ex.evRule, - ex.evMultiplier, - ex.modelCode, - ex.lastLiquidity, - *fields, - ) = fields - if self.serverVersion >= 178: - ex.pendingPriceRevision, *fields = fields - - self.parse(c) - self.parse(ex) - time = cast(datetime, parseIBDatetime(timeStr)) - if not time.tzinfo: - tz = self.wrapper.ib.TimezoneTWS - if tz: - time = time.replace(tzinfo=ZoneInfo(str(tz))) - - ex.time = time.astimezone(self.wrapper.defaultTimezone) - self.wrapper.execDetails(int(reqId), c, ex) - def historicalData(self, fields): _, reqId, startDateStr, endDateStr, numBars, *fields = fields get = iter(fields).__next__ @@ -570,81 +539,6 @@ def deltaNeutralValidation(self, fields): DeltaNeutralContract(int(conId), float(delta or 0), float(price or 0)), ) - def commissionReport(self, fields): - ( - _, - _, - execId, - commission, - currency, - realizedPNL, - yield_, - yieldRedemptionDate, - ) = fields - - self.wrapper.commissionReport( - CommissionReport( - execId, - float(commission or 0), - currency, - float(realizedPNL or 0), - float(yield_ or 0), - int(yieldRedemptionDate or 0), - ) - ) - - def position(self, fields): - c = Contract() - ( - _, - _, - account, - c.conId, - c.symbol, - c.secType, - c.lastTradeDateOrContractMonth, - c.strike, - c.right, - c.multiplier, - c.exchange, - c.currency, - c.localSymbol, - c.tradingClass, - position, - avgCost, - ) = fields - - self.parse(c) - self.wrapper.position(account, c, float(position or 0), float(avgCost or 0)) - - def positionMulti(self, fields): - c = Contract() - ( - _, - _, - reqId, - account, - c.conId, - c.symbol, - c.secType, - c.lastTradeDateOrContractMonth, - c.strike, - c.right, - c.multiplier, - c.exchange, - c.currency, - c.localSymbol, - c.tradingClass, - position, - avgCost, - modelCode, - ) = fields - - self.parse(c) - self.wrapper.positionMulti( - int(reqId), account, modelCode, c, float(position or 0), float(avgCost or 0) - ) - def securityDefinitionOptionParameter(self, fields): ( _, @@ -692,32 +586,6 @@ def familyCodes(self, fields): self.wrapper.familyCodes(familyCodes) - def symbolSamples(self, fields): - _, reqId, n, *fields = fields - - cds = [] - for _ in range(int(n)): - cd = ContractDescription() - cd.contract = c = Contract() - ( - c.conId, - c.symbol, - c.secType, - c.primaryExchange, - c.currency, - m, - *fields, - ) = fields - c.conId = int(c.conId) - m = int(m) - cd.derivativeSecTypes = fields[:m] - fields = fields[m:] - if self.serverVersion >= 176: - (cd.contract.description, cd.contract.issuerId, *fields) = fields - cds.append(cd) - - self.wrapper.symbolSamples(int(reqId), cds) - def smartComponents(self, fields): _, reqId, n, *fields = fields get = iter(fields).__next__ @@ -764,17 +632,6 @@ def histogramData(self, fields): self.wrapper.histogramData(int(reqId), histogram) - def marketRule(self, fields): - _, marketRuleId, n, *fields = fields - get = iter(fields).__next__ - - increments = [ - PriceIncrement(lowEdge=float(get()), increment=float(get())) - for _ in range(int(n)) - ] - - self.wrapper.marketRule(int(marketRuleId), increments) - def historicalTicks(self, fields): _, reqId, n, *fields = fields get = iter(fields).__next__ @@ -879,485 +736,6 @@ def tickByTick(self, fields): self.wrapper.tickByTickMidPoint(reqId, time, float(midPoint)) - def openOrder(self, fields): - o = Order() - c = Contract() - st = OrderState() - ( - _, - o.orderId, - c.conId, - c.symbol, - c.secType, - c.lastTradeDateOrContractMonth, - c.strike, - c.right, - c.multiplier, - c.exchange, - c.currency, - c.localSymbol, - c.tradingClass, - o.action, - o.totalQuantity, - o.orderType, - o.lmtPrice, - o.auxPrice, - o.tif, - o.ocaGroup, - o.account, - o.openClose, - o.origin, - o.orderRef, - o.clientId, - o.permId, - o.outsideRth, - o.hidden, - o.discretionaryAmt, - o.goodAfterTime, - _, - o.faGroup, - o.faMethod, - o.faPercentage, - *fields, - ) = fields - - if self.serverVersion < 177: - o.faProfile, *fields = fields - - ( - o.modelCode, - o.goodTillDate, - o.rule80A, - o.percentOffset, - o.settlingFirm, - o.shortSaleSlot, - o.designatedLocation, - o.exemptCode, - o.auctionStrategy, - o.startingPrice, - o.stockRefPrice, - o.delta, - o.stockRangeLower, - o.stockRangeUpper, - o.displaySize, - o.blockOrder, - o.sweepToFill, - o.allOrNone, - o.minQty, - o.ocaType, - o.eTradeOnly, - o.firmQuoteOnly, - o.nbboPriceCap, - o.parentId, - o.triggerMethod, - o.volatility, - o.volatilityType, - o.deltaNeutralOrderType, - o.deltaNeutralAuxPrice, - *fields, - ) = fields - - if o.deltaNeutralOrderType: - ( - o.deltaNeutralConId, - o.deltaNeutralSettlingFirm, - o.deltaNeutralClearingAccount, - o.deltaNeutralClearingIntent, - o.deltaNeutralOpenClose, - o.deltaNeutralShortSale, - o.deltaNeutralShortSaleSlot, - o.deltaNeutralDesignatedLocation, - *fields, - ) = fields - - ( - o.continuousUpdate, - o.referencePriceType, - o.trailStopPrice, - o.trailingPercent, - o.basisPoints, - o.basisPointsType, - c.comboLegsDescrip, - *fields, - ) = fields - - numLegs = int(fields.pop(0)) - c.comboLegs = [] - for _ in range(numLegs): - leg: Any = ComboLeg() - ( - leg.conId, - leg.ratio, - leg.action, - leg.exchange, - leg.openClose, - leg.shortSaleSlot, - leg.designatedLocation, - leg.exemptCode, - *fields, - ) = fields - self.parse(leg) - c.comboLegs.append(leg) - - numOrderLegs = int(fields.pop(0)) - o.orderComboLegs = [] - for _ in range(numOrderLegs): - leg = OrderComboLeg() - leg.price = fields.pop(0) - self.parse(leg) - o.orderComboLegs.append(leg) - - numParams = int(fields.pop(0)) - if numParams > 0: - o.smartComboRoutingParams = [] - for _ in range(numParams): - tag, value, *fields = fields - o.smartComboRoutingParams.append(TagValue(tag, value)) - - (o.scaleInitLevelSize, o.scaleSubsLevelSize, increment, *fields) = fields - - o.scalePriceIncrement = float(increment or UNSET_DOUBLE) - if 0 < o.scalePriceIncrement < UNSET_DOUBLE: - ( - o.scalePriceAdjustValue, - o.scalePriceAdjustInterval, - o.scaleProfitOffset, - o.scaleAutoReset, - o.scaleInitPosition, - o.scaleInitFillQty, - o.scaleRandomPercent, - *fields, - ) = fields - - o.hedgeType = fields.pop(0) - if o.hedgeType: - o.hedgeParam = fields.pop(0) - - ( - o.optOutSmartRouting, - o.clearingAccount, - o.clearingIntent, - o.notHeld, - dncPresent, - *fields, - ) = fields - - if int(dncPresent): - conId, delta, price, *fields = fields - c.deltaNeutralContract = DeltaNeutralContract( - int(conId or 0), float(delta or 0), float(price or 0) - ) - - o.algoStrategy = fields.pop(0) - if o.algoStrategy: - numParams = int(fields.pop(0)) - if numParams > 0: - o.algoParams = [] - for _ in range(numParams): - tag, value, *fields = fields - o.algoParams.append(TagValue(tag, value)) - - ( - o.solicited, - o.whatIf, - st.status, - st.initMarginBefore, - st.maintMarginBefore, - st.equityWithLoanBefore, - st.initMarginChange, - st.maintMarginChange, - st.equityWithLoanChange, - st.initMarginAfter, - st.maintMarginAfter, - st.equityWithLoanAfter, - st.commission, - st.minCommission, - st.maxCommission, - st.commissionCurrency, - st.warningText, - o.randomizeSize, - o.randomizePrice, - *fields, - ) = fields - - if o.orderType in {"PEG BENCH", "PEGBENCH"}: - ( - o.referenceContractId, - o.isPeggedChangeAmountDecrease, - o.peggedChangeAmount, - o.referenceChangeAmount, - o.referenceExchangeId, - *fields, - ) = fields - - numConditions = int(fields.pop(0)) - if numConditions > 0: - for _ in range(numConditions): - condType = int(fields.pop(0)) - condCls = OrderCondition.createClass(condType) - n = len(dataclasses.fields(condCls)) - 1 - cond = condCls(condType, *fields[:n]) - self.parse(cond) - o.conditions.append(cond) - fields = fields[n:] - (o.conditionsIgnoreRth, o.conditionsCancelOrder, *fields) = fields - - ( - o.adjustedOrderType, - o.triggerPrice, - o.trailStopPrice, - o.lmtPriceOffset, - o.adjustedStopPrice, - o.adjustedStopLimitPrice, - o.adjustedTrailingAmount, - o.adjustableTrailingUnit, - o.softDollarTier.name, - o.softDollarTier.val, - o.softDollarTier.displayName, - o.cashQty, - o.dontUseAutoPriceForHedge, - o.isOmsContainer, - o.discretionaryUpToLimitPrice, - o.usePriceMgmtAlgo, - *fields, - ) = fields - - if self.serverVersion >= 159: - o.duration = fields.pop(0) - - if self.serverVersion >= 160: - o.postToAts = fields.pop(0) - - if self.serverVersion >= 162: - o.autoCancelParent = fields.pop(0) - - if self.serverVersion >= 170: - ( - o.minTradeQty, - o.minCompeteSize, - o.competeAgainstBestOffset, - o.midOffsetAtWhole, - o.midOffsetAtHalf, - *fields, - ) = fields - - self.parse(c) - self.parse(o) - self.parse(st) - self.wrapper.openOrder(o.orderId, c, o, st) - - def completedOrder(self, fields): - o = Order() - c = Contract() - st = OrderState() - - ( - _, - c.conId, - c.symbol, - c.secType, - c.lastTradeDateOrContractMonth, - c.strike, - c.right, - c.multiplier, - c.exchange, - c.currency, - c.localSymbol, - c.tradingClass, - o.action, - o.totalQuantity, - o.orderType, - o.lmtPrice, - o.auxPrice, - o.tif, - o.ocaGroup, - o.account, - o.openClose, - o.origin, - o.orderRef, - o.permId, - o.outsideRth, - o.hidden, - o.discretionaryAmt, - o.goodAfterTime, - o.faGroup, - o.faMethod, - o.faPercentage, - *fields, - ) = fields - if self.serverVersion < 177: - o.faProfile, *fields = fields - - ( - o.modelCode, - o.goodTillDate, - o.rule80A, - o.percentOffset, - o.settlingFirm, - o.shortSaleSlot, - o.designatedLocation, - o.exemptCode, - o.startingPrice, - o.stockRefPrice, - o.delta, - o.stockRangeLower, - o.stockRangeUpper, - o.displaySize, - o.sweepToFill, - o.allOrNone, - o.minQty, - o.ocaType, - o.triggerMethod, - o.volatility, - o.volatilityType, - o.deltaNeutralOrderType, - o.deltaNeutralAuxPrice, - *fields, - ) = fields - - if o.deltaNeutralOrderType: - ( - o.deltaNeutralConId, - o.deltaNeutralShortSale, - o.deltaNeutralShortSaleSlot, - o.deltaNeutralDesignatedLocation, - *fields, - ) = fields - - ( - o.continuousUpdate, - o.referencePriceType, - o.trailStopPrice, - o.trailingPercent, - c.comboLegsDescrip, - *fields, - ) = fields - - numLegs = int(fields.pop(0)) - c.comboLegs = [] - for _ in range(numLegs): - leg: Any = ComboLeg() - ( - leg.conId, - leg.ratio, - leg.action, - leg.exchange, - leg.openClose, - leg.shortSaleSlot, - leg.designatedLocation, - leg.exemptCode, - *fields, - ) = fields - self.parse(leg) - c.comboLegs.append(leg) - - numOrderLegs = int(fields.pop(0)) - o.orderComboLegs = [] - for _ in range(numOrderLegs): - leg = OrderComboLeg() - leg.price = fields.pop(0) - self.parse(leg) - o.orderComboLegs.append(leg) - - numParams = int(fields.pop(0)) - if numParams > 0: - o.smartComboRoutingParams = [] - for _ in range(numParams): - tag, value, *fields = fields - o.smartComboRoutingParams.append(TagValue(tag, value)) - (o.scaleInitLevelSize, o.scaleSubsLevelSize, increment, *fields) = fields - - o.scalePriceIncrement = float(increment or UNSET_DOUBLE) - if 0 < o.scalePriceIncrement < UNSET_DOUBLE: - ( - o.scalePriceAdjustValue, - o.scalePriceAdjustInterval, - o.scaleProfitOffset, - o.scaleAutoReset, - o.scaleInitPosition, - o.scaleInitFillQty, - o.scaleRandomPercent, - *fields, - ) = fields - - o.hedgeType = fields.pop(0) - if o.hedgeType: - o.hedgeParam = fields.pop(0) - - (o.clearingAccount, o.clearingIntent, o.notHeld, dncPresent, *fields) = fields - - if int(dncPresent): - conId, delta, price, *fields = fields - c.deltaNeutralContract = DeltaNeutralContract( - int(conId or 0), float(delta or 0), float(price or 0) - ) - - o.algoStrategy = fields.pop(0) - if o.algoStrategy: - numParams = int(fields.pop(0)) - if numParams > 0: - o.algoParams = [] - for _ in range(numParams): - tag, value, *fields = fields - o.algoParams.append(TagValue(tag, value)) - (o.solicited, st.status, o.randomizeSize, o.randomizePrice, *fields) = fields - - if o.orderType in {"PEG BENCH", "PEGBENCH"}: - ( - o.referenceContractId, - o.isPeggedChangeAmountDecrease, - o.peggedChangeAmount, - o.referenceChangeAmount, - o.referenceExchangeId, - *fields, - ) = fields - - numConditions = int(fields.pop(0)) - if numConditions > 0: - for _ in range(numConditions): - condType = int(fields.pop(0)) - condCls = OrderCondition.createClass(condType) - n = len(dataclasses.fields(condCls)) - 1 - cond = condCls(condType, *fields[:n]) - self.parse(cond) - o.conditions.append(cond) - fields = fields[n:] - (o.conditionsIgnoreRth, o.conditionsCancelOrder, *fields) = fields - - ( - o.trailStopPrice, - o.lmtPriceOffset, - o.cashQty, - o.dontUseAutoPriceForHedge, - o.isOmsContainer, - o.autoCancelDate, - o.filledQuantity, - o.refFuturesConId, - o.autoCancelParent, - o.shareholder, - o.imbalanceOnly, - o.routeMarketableToBbo, - o.parentPermId, - st.completedTime, - st.completedStatus, - *fields, - ) = fields - - if self.serverVersion >= 170: - ( - o.minTradeQty, - o.minCompeteSize, - o.competeAgainstBestOffset, - o.midOffsetAtWhole, - o.midOffsetAtHalf, - *fields, - ) = fields - - self.parse(c) - self.parse(o) - self.parse(st) - self.wrapper.completedOrder(c, o, st) - def historicalSchedule(self, fields): (_, reqId, startDateTime, endDateTime, timeZone, count, *fields) = fields get = iter(fields).__next__ diff --git a/ib_async/ib.py b/ib_async/ib.py index 4f73160d..42046b8e 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -1,18 +1,37 @@ """High-level interface to Interactive Brokers.""" -import asyncio +from asyncio import TimeoutError import copy import datetime +import asyncio import logging import time from enum import auto, Flag -from typing import Any, Awaitable, Iterator, List, Optional, Union +from typing import ( + Any, + Awaitable, + Callable, + cast, + Iterator, + List, + Optional, + TypeVar, + Union, + AsyncIterator, +) + +_T = TypeVar("_T") from eventkit import Event import ib_async.util as util +from ib_async.wrapper import Wrapper, RequestError from ib_async.client import Client from ib_async.contract import Contract, ContractDescription, ContractDetails +from ib_async.protobuf.ContractDataRequest_pb2 import ( + ContractDataRequest as ContractDataRequestProto, +) + from ib_async.objects import ( AccountValue, BarDataList, @@ -424,6 +443,25 @@ def _onError(self, reqId, errorCode, errorString, contract): timeRangeAsync = staticmethod(util.timeRangeAsync) waitUntil = staticmethod(util.waitUntil) + @staticmethod + def _raise_if_error(data: Any) -> Any: + """ + Raise error in reactive stream. + """ + if isinstance(data, Exception): + raise data + return data + + @staticmethod + async def _raise_if_error_async(ait: AsyncIterator[_T]) -> AsyncIterator[_T]: + """ + Raise error in asyn iterator. + """ + async for item in ait: + if isinstance(item, RequestError): + raise item + yield item + def _run(self, *awaitables: Awaitable): return util.run(*awaitables, timeout=self.RequestTimeout) @@ -870,6 +908,14 @@ def reqCurrentTime(self) -> datetime.datetime: """ return self._run(self.reqCurrentTimeAsync()) + def reqCurrentTimeMili(self) -> datetime.datetime: + """ + Request TWS current time in milliseconds. + + This method is blocking. + """ + return self._run(self.reqCurrentTimeMiliAsync()) + def reqAccountUpdates(self, account: str = ""): """ This is called at startup - no need to call again. @@ -1103,7 +1149,7 @@ def reqMatchingSymbols(self, pattern: str) -> list[ContractDescription]: """ return self._run(self.reqMatchingSymbolsAsync(pattern)) - def reqMarketRule(self, marketRuleId: int) -> PriceIncrement: + def reqMarketRule(self, marketRuleId: int) -> list[PriceIncrement]: """ Request price increments rule. @@ -2035,6 +2081,7 @@ async def connectAsync( raiseSyncErrors: bool = False, fetchFields: StartupFetch = StartupFetchALL, ): + self.wrapper._isReady = False clientId = int(clientId) self.wrapper.clientId = clientId timeout = timeout or None @@ -2042,6 +2089,12 @@ async def connectAsync( # establish API connection await self.client.connectAsync(host, port, clientId, timeout) + if not self.client.useProtoBuf(): + raise ConnectionError( + "Protobuf not supported by server. " + "Please upgrade TWS/Gateway to a version that supports protobuf." + ) + # autobind manual orders if clientId == 0: self.reqAutoOpenOrders(True) @@ -2100,6 +2153,7 @@ async def connectAsync( raise ConnectionError("Socket connection broken while connecting") self._logger.info("Synchronization complete") + self.wrapper._isReady = True self.connectedEvent.emit() except BaseException: self.disconnect() @@ -2212,22 +2266,50 @@ def whatIfOrderAsync( return future def reqCurrentTimeAsync(self) -> Awaitable[datetime.datetime]: - future = self.wrapper.startReq("currentTime") self.client.reqCurrentTime() - return future + return ( + self.wrapper.response_bus.filter(lambda key, _: key == "currentTime") + .pluck(1) + .take(1) + ) - def reqAccountUpdatesAsync(self, account: str) -> Awaitable[None]: - future = self.wrapper.startReq("accountValues") - self.client.reqAccountUpdates(True, account) - return future + def reqCurrentTimeMiliAsync(self) -> Awaitable[datetime.datetime]: + self.client.reqCurrentTimeMili() + return ( + self.wrapper.response_bus.filter(lambda key, _: key == "currentTimeMili") + .pluck(1) + .take(1) + ) + + def reqAccountUpdatesAsync(self, account: str = "") -> Awaitable[None]: + """ + Request account and portfolio values of the account + and keep updated. Returns when both account values and portfolio + are filled. + + This is a coroutine. + + Args: + account: If specified, filter for this account name. + """ + acctCode = account or self.wrapper.accounts[0] + self.client.reqAccountUpdates(True, acctCode) + return self.wrapper.response_bus.filter( + lambda key, _: key == "accountValues" + ).take(1) def reqAccountUpdatesMultiAsync( self, account: str, modelCode: str = "" - ) -> Awaitable[None]: + ) -> Awaitable[list[AccountValue]]: reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId) self.client.reqAccountUpdatesMulti(reqId, account, modelCode, False) - return future + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .takewhile(lambda rId, data: data is not None) + .pluck(1) + .map(self._raise_if_error) + .list() + ) async def accountSummaryAsync(self, account: str = "") -> list[AccountValue]: if not self.wrapper.acctSummary: @@ -2241,86 +2323,158 @@ async def accountSummaryAsync(self, account: str = "") -> list[AccountValue]: return list(self.wrapper.acctSummary.values()) - def reqAccountSummaryAsync(self) -> Awaitable[None]: + def reqAccountSummaryAsync(self, group: str = "All", tags: str = ""): + """ + Request account values for all accounts and keep them updated. + Returns when account summary is filled. + + This is a coroutine. + """ + if not tags: + tags = ( + "AccountType,NetLiquidation,TotalCashValue,SettledCash," + "AccruedCash,BuyingPower,EquityWithLoanValue," + "PreviousEquityWithLoanValue,GrossPositionValue,ReqTEquity," + "ReqTMargin,SMA,InitMarginReq,MaintMarginReq,AvailableFunds," + "ExcessLiquidity,Cushion,FullInitMarginReq,FullMaintMarginReq," + "FullAvailableFunds,FullExcessLiquidity,LookAheadNextChange," + "LookAheadInitMarginReq,LookAheadMaintMarginReq," + "LookAheadAvailableFunds,LookAheadExcessLiquidity," + "HighestSeverity,DayTradesRemaining,Leverage" + ) reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId) - tags = ( - "AccountType,NetLiquidation,TotalCashValue,SettledCash," - "AccruedCash,BuyingPower,EquityWithLoanValue," - "PreviousDayEquityWithLoanValue,GrossPositionValue,RegTEquity," - "RegTMargin,SMA,InitMarginReq,MaintMarginReq,AvailableFunds," - "ExcessLiquidity,Cushion,FullInitMarginReq,FullMaintMarginReq," - "FullAvailableFunds,FullExcessLiquidity,LookAheadNextChange," - "LookAheadInitMarginReq,LookAheadMaintMarginReq," - "LookAheadAvailableFunds,LookAheadExcessLiquidity," - "HighestSeverity,DayTradesRemaining,DayTradesRemainingT+1," - "DayTradesRemainingT+2,DayTradesRemainingT+3," - "DayTradesRemainingT+4,Leverage,$LEDGER:ALL" + self.client.reqAccountSummary(reqId, group, tags) + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) ) - self.client.reqAccountSummary(reqId, "All", tags) - return future def reqOpenOrdersAsync(self) -> Awaitable[list[Trade]]: - future = self.wrapper.startReq("openOrders") self.client.reqOpenOrders() - return future + return ( + self.wrapper.response_bus.filter(lambda key, _: key == "openOrders") + .takewhile(lambda key, data: data is not None) + .pluck(1) + .list() + ) def reqAllOpenOrdersAsync(self) -> Awaitable[list[Trade]]: - future = self.wrapper.startReq("openOrders") self.client.reqAllOpenOrders() - return future + return ( + self.wrapper.response_bus.filter(lambda key, _: key == "openOrders") + .takewhile(lambda key, data: data is not None) + .pluck(1) + .list() + ) def reqCompletedOrdersAsync(self, apiOnly: bool) -> Awaitable[list[Trade]]: - future = self.wrapper.startReq("completedOrders") self.client.reqCompletedOrders(apiOnly) - return future + return ( + self.wrapper.response_bus.filter(lambda key, _: key == "completedOrders") + .takewhile(lambda key, data: data is not None) + .pluck(1) + .list() + ) def reqExecutionsAsync( self, execFilter: Optional[ExecutionFilter] = None ) -> Awaitable[list[Fill]]: - execFilter = execFilter or ExecutionFilter() + """Request a list of fills.""" reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId) - self.client.reqExecutions(reqId, execFilter) - return future + self.client.reqExecutions(reqId, execFilter or ExecutionFilter()) + fills = ( + self.wrapper.response_bus.filter( + lambda rId, e: rId == reqId and e is not None + ) + .takewhile(lambda rId, data: data is not None) + .pluck(1) + .map(self._raise_if_error) + .list() + ) + return fills def reqPositionsAsync(self) -> Awaitable[list[Position]]: - future = self.wrapper.startReq("positions") + """Request a list of positions.""" self.client.reqPositions() - return future + return ( + self.wrapper.response_bus.filter(lambda name, _: name == "position") + .takewhile(lambda name, data: data is not None) + .pluck(1) + ) def reqContractDetailsAsync( self, contract: Contract - ) -> Awaitable[list[ContractDetails]]: + ) -> "Awaitable[list[ContractDetails]]": + """ + Asynchronous version of :meth:`.reqContractDetails`. + + Returns: + An awaitable that yields the matching contract details. + """ reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId, contract) self.client.reqContractDetails(reqId, contract) - return future + # This is a streaming response; contract details are sent one by one, + # followed by a final "end" event. The .list() operator gathers all + # individual results into a single list. + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .takewhile(lambda rId, data: data is not None) + .pluck(1) + .map(self._raise_if_error) + .list() + ) - async def reqMatchingSymbolsAsync( + def reqMatchingSymbolsAsync( self, pattern: str - ) -> Optional[list[ContractDescription]]: + ) -> Awaitable[list[ContractDescription]]: + """ + Request contract descriptions of contracts that match a pattern. + + This method is blocking. + + https://interactivebrokers.github.io/tws-api/matching_symbols.html + + Args: + pattern: The first few letters of the ticker symbol, or for + longer strings a character sequence matching a word in + the security name. + """ reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId) self.client.reqMatchingSymbols(reqId, pattern) - try: - await asyncio.wait_for(future, 4) - return future.result() - except asyncio.TimeoutError: - self._logger.error("reqMatchingSymbolsAsync: Timeout") - return None + # This is a single-shot response; the API sends the entire list + # of results in one event. .take(1).pluck(1) is used to grab the + # payload (which is the list) from that single event. + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) - async def reqMarketRuleAsync( - self, marketRuleId: int - ) -> Optional[list[PriceIncrement]]: - future = self.wrapper.startReq(f"marketRule-{marketRuleId}") - try: - self.client.reqMarketRule(marketRuleId) - await asyncio.wait_for(future, 1) - return future.result() - except asyncio.TimeoutError: - self._logger.error("reqMarketRuleAsync: Timeout") - return None + def reqMarketRuleAsync(self, marketRuleId: int) -> Awaitable[list[PriceIncrement]]: + """ + Request price increments rule. + + https://interactivebrokers.github.io/tws-api/minimum_increment.html + + Args: + marketRuleId: ID of market rule. + The market rule IDs for a contract can be obtained + via :meth:`.reqContractDetails` from + :class:`.ContractDetails`.marketRuleIds, + which contains a comma separated string of market rule IDs. + """ + self.client.reqMarketRule(marketRuleId) + return ( + self.wrapper.response_bus.filter( + lambda rId, _: rId == f"marketRule-{marketRuleId}" + ) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) async def reqHistoricalDataAsync( self, @@ -2336,20 +2490,8 @@ async def reqHistoricalDataAsync( timeout: float = 60, ) -> BarDataList: reqId = self.client.getReqId() - bars = BarDataList() - bars.reqId = reqId - bars.contract = contract - bars.endDateTime = endDateTime - bars.durationStr = durationStr - bars.barSizeSetting = barSizeSetting - bars.whatToShow = whatToShow - bars.useRTH = useRTH - bars.formatDate = formatDate - bars.keepUpToDate = keepUpToDate - bars.chartOptions = chartOptions or [] - future = self.wrapper.startReq(reqId, contract, container=bars) - if keepUpToDate: - self.wrapper.startSubscription(reqId, bars, contract) + # if keepUpToDate: + # self.wrapper.startSubscription(reqId, bars, contract) end = util.formatIBDatetime(endDateTime) self.client.reqHistoricalData( reqId, @@ -2363,14 +2505,31 @@ async def reqHistoricalDataAsync( keepUpToDate, chartOptions, ) - task = asyncio.wait_for(future, timeout) if timeout else future + awaitable = ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .takewhile(lambda rId, data: data is not None) + .pluck(1) + .map(self._raise_if_error) + ) + task = asyncio.wait_for(awaitable, timeout) if timeout else awaitable try: - await task + result = await task except asyncio.TimeoutError: self.client.cancelHistoricalData(reqId) self._logger.warning(f"reqHistoricalData: Timeout for {contract}") - bars.clear() + bars = BarDataList() + bars.reqId = reqId + bars.contract = contract + bars.endDateTime = endDateTime + bars.durationStr = durationStr + bars.barSizeSetting = barSizeSetting + bars.whatToShow = whatToShow + bars.useRTH = useRTH + bars.formatDate = formatDate + bars.keepUpToDate = keepUpToDate + bars.chartOptions = chartOptions or [] + bars.extend(result) return bars def reqHistoricalScheduleAsync( @@ -2427,16 +2586,21 @@ def reqHistoricalTicksAsync( return future async def reqHeadTimeStampAsync( - self, contract: Contract, whatToShow: str, useRTH: bool, formatDate: int + self, contract: Contract, whatToShow: str, useRTH: bool, formatDate: int = 1 ) -> datetime.datetime: reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId, contract) self.client.reqHeadTimeStamp(reqId, contract, whatToShow, useRTH, formatDate) - await future + result = ( + await self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) self.client.cancelHeadTimeStamp(reqId) - return future.result() + + return result def reqSmartComponentsAsync(self, bboExchange): reqId = self.client.getReqId() @@ -2546,12 +2710,43 @@ def reqSecDefOptParamsAsync( underlyingConId: int, ) -> Awaitable[list[OptionChain]]: reqId = self.client.getReqId() - - future = self.wrapper.startReq(reqId) self.client.reqSecDefOptParams( reqId, underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId ) - return future + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .takewhile(lambda rId, data: data is not None) + .pluck(1) + .list() + ) + + def reqSecDefOptParams( + self, + underlyingSymbol: str, + futFopExchange: str, + underlyingSecType: str, + underlyingConId: int, + ) -> list[OptionChain]: + """ + Get the option chain. + + This method is blocking. + + https://interactivebrokers.github.io/tws-api/options.html + + Args: + underlyingSymbol: Symbol of underlier contract. + futFopExchange: Exchange (only for ``FuturesOption``, otherwise + leave blank). + underlyingSecType: The type of the underlying security, like + 'STK' or 'FUT'. + underlyingConId: conId of the underlying contract. + """ + return self._run( + self.reqSecDefOptParamsAsync( + underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId + ) + ) def reqNewsProvidersAsync(self) -> Awaitable[list[NewsProvider]]: future = self.wrapper.startReq("newsProviders") diff --git a/ib_async/message.py b/ib_async/message.py new file mode 100644 index 00000000..9c586745 --- /dev/null +++ b/ib_async/message.py @@ -0,0 +1,203 @@ +class MessageId: + """ + Encapsulates TWS API message ID constants and protocol-related logic. + """ + + from dataclasses import dataclass + + + PROTOBUF_MSG_ID = 200 + """Protobuf message ID offset""" + + @dataclass(frozen=True,slots=True) + class IN: + """Incoming Message IDs (TWS -> Client)""" + + TICK_PRICE = 1 + TICK_SIZE = 2 + ORDER_STATUS = 3 + ERR_MSG = 4 + OPEN_ORDER = 5 + ACCT_VALUE = 6 + PORTFOLIO_VALUE = 7 + ACCT_UPDATE_TIME = 8 + NEXT_VALID_ID = 9 + CONTRACT_DATA = 10 + EXECUTION_DATA = 11 + MARKET_DEPTH = 12 + MARKET_DEPTH_L2 = 13 + NEWS_BULLETINS = 14 + MANAGED_ACCTS = 15 + RECEIVE_FA = 16 + HISTORICAL_DATA = 17 + BOND_CONTRACT_DATA = 18 + SCANNER_PARAMETERS = 19 + SCANNER_DATA = 20 + TICK_OPTION_COMPUTATION = 21 + TICK_GENERIC = 45 + TICK_STRING = 46 + TICK_EFP = 47 + CURRENT_TIME = 49 + REAL_TIME_BARS = 50 + FUNDAMENTAL_DATA = 51 + CONTRACT_DATA_END = 52 + OPEN_ORDER_END = 53 + ACCT_DOWNLOAD_END = 54 + EXECUTION_DATA_END = 55 + DELTA_NEUTRAL_VALIDATION = 56 + TICK_SNAPSHOT_END = 57 + MARKET_DATA_TYPE = 58 + COMMISSION_AND_FEES_REPORT = 59 + POSITION_DATA = 61 + POSITION_END = 62 + ACCOUNT_SUMMARY = 63 + ACCOUNT_SUMMARY_END = 64 + VERIFY_MESSAGE_API = 65 + VERIFY_COMPLETED = 66 + DISPLAY_GROUP_LIST = 67 + DISPLAY_GROUP_UPDATED = 68 + VERIFY_AND_AUTH_MESSAGE_API = 69 + VERIFY_AND_AUTH_COMPLETED = 70 + POSITION_MULTI = 71 + POSITION_MULTI_END = 72 + ACCOUNT_UPDATE_MULTI = 73 + ACCOUNT_UPDATE_MULTI_END = 74 + SECURITY_DEFINITION_OPTION_PARAMETER = 75 + SECURITY_DEFINITION_OPTION_PARAMETER_END = 76 + SOFT_DOLLAR_TIERS = 77 + FAMILY_CODES = 78 + SYMBOL_SAMPLES = 79 + MKT_DEPTH_EXCHANGES = 80 + TICK_REQ_PARAMS = 81 + SMART_COMPONENTS = 82 + NEWS_ARTICLE = 83 + TICK_NEWS = 84 + NEWS_PROVIDERS = 85 + HISTORICAL_NEWS = 86 + HISTORICAL_NEWS_END = 87 + HEAD_TIMESTAMP = 88 + HISTOGRAM_DATA = 89 + HISTORICAL_DATA_UPDATE = 90 + REROUTE_MKT_DATA_REQ = 91 + REROUTE_MKT_DEPTH_REQ = 92 + MARKET_RULE = 93 + PNL = 94 + PNL_SINGLE = 95 + HISTORICAL_TICKS = 96 + HISTORICAL_TICKS_BID_ASK = 97 + HISTORICAL_TICKS_LAST = 98 + TICK_BY_TICK = 99 + ORDER_BOUND = 100 + COMPLETED_ORDER = 101 + COMPLETED_ORDERS_END = 102 + REPLACE_FA_END = 103 + WSH_META_DATA = 104 + WSH_EVENT_DATA = 105 + HISTORICAL_SCHEDULE = 106 + USER_INFO = 107 + HISTORICAL_DATA_END = 108 + CURRENT_TIME_IN_MILLIS = 109 + + @dataclass(frozen=True,slots=True) + class OUT: + """Outgoing Message IDs (Client -> TWS)""" + + REQ_MKT_DATA = 1 + CANCEL_MKT_DATA = 2 + PLACE_ORDER = 3 + CANCEL_ORDER = 4 + REQ_OPEN_ORDERS = 5 + REQ_ACCT_DATA = 6 + REQ_EXECUTIONS = 7 + REQ_IDS = 8 + REQ_CONTRACT_DATA = 9 + REQ_MKT_DEPTH = 10 + CANCEL_MKT_DEPTH = 11 + REQ_NEWS_BULLETINS = 12 + CANCEL_NEWS_BULLETINS = 13 + SET_SERVER_LOGLEVEL = 14 + REQ_AUTO_OPEN_ORDERS = 15 + REQ_ALL_OPEN_ORDERS = 16 + REQ_MANAGED_ACCTS = 17 + REQ_FA = 18 + REPLACE_FA = 19 + REQ_HISTORICAL_DATA = 20 + EXERCISE_OPTIONS = 21 + REQ_SCANNER_SUBSCRIPTION = 22 + CANCEL_SCANNER_SUBSCRIPTION = 23 + REQ_SCANNER_PARAMETERS = 24 + CANCEL_HISTORICAL_DATA = 25 + REQ_CURRENT_TIME = 49 + REQ_REAL_TIME_BARS = 50 + CANCEL_REAL_TIME_BARS = 51 + REQ_FUNDAMENTAL_DATA = 52 + CANCEL_FUNDAMENTAL_DATA = 53 + REQ_CALC_IMPLIED_VOLAT = 54 + REQ_CALC_OPTION_PRICE = 55 + CANCEL_CALC_IMPLIED_VOLAT = 56 + CANCEL_CALC_OPTION_PRICE = 57 + REQ_GLOBAL_CANCEL = 58 + REQ_MARKET_DATA_TYPE = 59 + REQ_POSITIONS = 61 + REQ_ACCOUNT_SUMMARY = 62 + CANCEL_ACCOUNT_SUMMARY = 63 + CANCEL_POSITIONS = 64 + VERIFY_REQUEST = 65 + VERIFY_MESSAGE = 66 + QUERY_DISPLAY_GROUPS = 67 + SUBSCRIBE_TO_GROUP_EVENTS = 68 + UPDATE_DISPLAY_GROUP = 69 + UNSUBSCRIBE_FROM_GROUP_EVENTS = 70 + START_API = 71 + VERIFY_AND_AUTH_REQUEST = 72 + VERIFY_AND_AUTH_MESSAGE = 73 + REQ_POSITIONS_MULTI = 74 + CANCEL_POSITIONS_MULTI = 75 + REQ_ACCOUNT_UPDATES_MULTI = 76 + CANCEL_ACCOUNT_UPDATES_MULTI = 77 + REQ_SEC_DEF_OPT_PARAMS = 78 + REQ_SOFT_DOLLAR_TIERS = 79 + REQ_FAMILY_CODES = 80 + REQ_MATCHING_SYMBOLS = 81 + REQ_MKT_DEPTH_EXCHANGES = 82 + REQ_SMART_COMPONENTS = 83 + REQ_NEWS_ARTICLE = 84 + REQ_NEWS_PROVIDERS = 85 + REQ_HISTORICAL_NEWS = 86 + REQ_HEAD_TIMESTAMP = 87 + REQ_HISTOGRAM_DATA = 88 + CANCEL_HISTOGRAM_DATA = 89 + CANCEL_HEAD_TIMESTAMP = 90 + REQ_MARKET_RULE = 91 + REQ_PNL = 92 + CANCEL_PNL = 93 + REQ_PNL_SINGLE = 94 + CANCEL_PNL_SINGLE = 95 + REQ_HISTORICAL_TICKS = 96 + REQ_TICK_BY_TICK_DATA = 97 + CANCEL_TICK_BY_TICK_DATA = 98 + REQ_COMPLETED_ORDERS = 99 + REQ_WSH_META_DATA = 100 + CANCEL_WSH_META_DATA = 101 + REQ_WSH_EVENT_DATA = 102 + CANCEL_WSH_EVENT_DATA = 103 + REQ_USER_INFO = 104 + REQ_CURRENT_TIME_IN_MILLIS = 105 + CANCEL_CONTRACT_DATA = 106 + CANCEL_HISTORICAL_TICKS = 107 + + @staticmethod + def to_protobuf(legacy_id: int) -> int: + """ + Converts a legacy outgoing ID to its Protobuf equivalent by adding the + Protobuf message ID offset. + """ + return legacy_id + MessageId.PROTOBUF_MSG_ID + + @staticmethod + def from_protobuf(proto_id: int) -> int: + """ + Converts an incoming Protobuf ID back to its legacy equivalent by + subtracting the Protobuf message ID offset. + """ + return proto_id - MessageId.PROTOBUF_MSG_ID diff --git a/ib_async/objects.py b/ib_async/objects.py index 980f7a30..e8cbfcb9 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -4,6 +4,7 @@ from dataclasses import dataclass, field from datetime import date as date_, datetime, timezone, tzinfo +from enum import Enum from typing import Any, List, NamedTuple, Optional, Union from eventkit import Event @@ -13,6 +14,16 @@ nan = float("nan") +class OptionExerciseType(Enum): + NoneItem = (-1, "None") + Exercise = (1, "Exercise") + Lapse = (2, "Lapse") + DoNothing = (3, "DoNothing") + Assigned = (100, "Assigned ") + AutoexerciseClearing = (101, "AutoexerciseClearing") + Expired = (102, "Expired") + Netting = (103, "Netting") + AutoexerciseTrading = (200, "AutoexerciseTrading") @dataclass class ScannerSubscription: @@ -70,6 +81,8 @@ class Execution: modelCode: str = "" lastLiquidity: int = 0 pendingPriceRevision: bool = False + submitter:str = "" + optExerciseOrLapseType:OptionExerciseType = OptionExerciseType.NoneItem @dataclass @@ -91,6 +104,8 @@ class ExecutionFilter: secType: str = "" exchange: str = "" side: str = "" + lastNDays:int = UNSET_INTEGER + specificDates:list[int] = field(default_factory=list) @dataclass @@ -560,3 +575,9 @@ class IBDefaults: # optionally change the timezone used for log history events in objects (no impact on orders or data processing) timezone: tzinfo = timezone.utc + + +@dataclass +class IneligibilityReason: + id_: str = field(default_factory=str) + description: str = field(default_factory=str) diff --git a/ib_async/order.py b/ib_async/order.py index 33b866a5..ed11426e 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -12,7 +12,7 @@ from .contract import Contract, TagValue from .objects import Fill, SoftDollarTier, TradeLogEntry -from .util import dataclassNonDefaults, UNSET_DOUBLE, UNSET_INTEGER +from .util import UNSET_DECIMAL, dataclassNonDefaults, UNSET_DOUBLE, UNSET_INTEGER @dataclass @@ -162,6 +162,13 @@ class Order: competeAgainstBestOffset: float | Decimal = UNSET_DOUBLE midOffsetAtWhole: float | Decimal = UNSET_DOUBLE midOffsetAtHalf: float | Decimal = UNSET_DOUBLE + customerAccount:str = "" + professionalCustomer:bool = False + bondAccruedInterest:str = "" + includeOvernight:bool = False + manualOrderIndicator:int = UNSET_INTEGER + submitter:str = "" + def __repr__(self): attrs = dataclassNonDefaults(self) @@ -472,6 +479,7 @@ def remaining(self) -> float: return float(self.order.totalQuantity) - self.filled() + class BracketOrder(NamedTuple): parent: Order takeProfit: Order @@ -555,3 +563,13 @@ class PercentChangeCondition(OrderCondition): changePercent: float = 0.0 conId: int = 0 exch: str = "" + +@dataclass +class OrderAllocation: + account = "" + position:Decimal = UNSET_DECIMAL + positionDesired:Decimal = UNSET_DECIMAL + positionAfter:Decimal = UNSET_DECIMAL + desiredAllocQty:Decimal = UNSET_DECIMAL + allowedAllocQty:Decimal = UNSET_DECIMAL + isMonetary = False diff --git a/ib_async/protobuf/AccountDataEnd_pb2.py b/ib_async/protobuf/AccountDataEnd_pb2.py new file mode 100644 index 00000000..eb7e8fb4 --- /dev/null +++ b/ib_async/protobuf/AccountDataEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AccountDataEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AccountDataEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x41\x63\x63ountDataEnd.proto\x12\x08protobuf\":\n\x0e\x41\x63\x63ountDataEnd\x12\x18\n\x0b\x61\x63\x63ountName\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_accountNameB>\n\x16\x63om.ib.client.protobufB\x13\x41\x63\x63ountDataEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AccountDataEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023AccountDataEndProto\252\002\016IBApi.protobuf' + _globals['_ACCOUNTDATAEND']._serialized_start=34 + _globals['_ACCOUNTDATAEND']._serialized_end=92 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AccountDataEnd_pb2.pyi b/ib_async/protobuf/AccountDataEnd_pb2.pyi new file mode 100644 index 00000000..7932a14f --- /dev/null +++ b/ib_async/protobuf/AccountDataEnd_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AccountDataEnd(_message.Message): + __slots__ = ("accountName",) + ACCOUNTNAME_FIELD_NUMBER: _ClassVar[int] + accountName: str + def __init__(self, accountName: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/AccountDataRequest_pb2.py b/ib_async/protobuf/AccountDataRequest_pb2.py new file mode 100644 index 00000000..85c86dce --- /dev/null +++ b/ib_async/protobuf/AccountDataRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AccountDataRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AccountDataRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x41\x63\x63ountDataRequest.proto\x12\x08protobuf\"^\n\x12\x41\x63\x63ountDataRequest\x12\x16\n\tsubscribe\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x15\n\x08\x61\x63\x63tCode\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x0c\n\n_subscribeB\x0b\n\t_acctCodeBB\n\x16\x63om.ib.client.protobufB\x17\x41\x63\x63ountDataRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AccountDataRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027AccountDataRequestProto\252\002\016IBApi.protobuf' + _globals['_ACCOUNTDATAREQUEST']._serialized_start=38 + _globals['_ACCOUNTDATAREQUEST']._serialized_end=132 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AccountDataRequest_pb2.pyi b/ib_async/protobuf/AccountDataRequest_pb2.pyi new file mode 100644 index 00000000..371710b8 --- /dev/null +++ b/ib_async/protobuf/AccountDataRequest_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AccountDataRequest(_message.Message): + __slots__ = ("subscribe", "acctCode") + SUBSCRIBE_FIELD_NUMBER: _ClassVar[int] + ACCTCODE_FIELD_NUMBER: _ClassVar[int] + subscribe: bool + acctCode: str + def __init__(self, subscribe: bool = ..., acctCode: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/AccountSummaryEnd_pb2.py b/ib_async/protobuf/AccountSummaryEnd_pb2.py new file mode 100644 index 00000000..e68cb037 --- /dev/null +++ b/ib_async/protobuf/AccountSummaryEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AccountSummaryEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AccountSummaryEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x41\x63\x63ountSummaryEnd.proto\x12\x08protobuf\"1\n\x11\x41\x63\x63ountSummaryEnd\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBA\n\x16\x63om.ib.client.protobufB\x16\x41\x63\x63ountSummaryEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AccountSummaryEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026AccountSummaryEndProto\252\002\016IBApi.protobuf' + _globals['_ACCOUNTSUMMARYEND']._serialized_start=37 + _globals['_ACCOUNTSUMMARYEND']._serialized_end=86 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AccountSummaryEnd_pb2.pyi b/ib_async/protobuf/AccountSummaryEnd_pb2.pyi new file mode 100644 index 00000000..513a691d --- /dev/null +++ b/ib_async/protobuf/AccountSummaryEnd_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AccountSummaryEnd(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/AccountSummaryRequest_pb2.py b/ib_async/protobuf/AccountSummaryRequest_pb2.py new file mode 100644 index 00000000..e375b84d --- /dev/null +++ b/ib_async/protobuf/AccountSummaryRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AccountSummaryRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AccountSummaryRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x41\x63\x63ountSummaryRequest.proto\x12\x08protobuf\"o\n\x15\x41\x63\x63ountSummaryRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x12\n\x05group\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04tags\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\x08\n\x06_groupB\x07\n\x05_tagsBE\n\x16\x63om.ib.client.protobufB\x1a\x41\x63\x63ountSummaryRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AccountSummaryRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032AccountSummaryRequestProto\252\002\016IBApi.protobuf' + _globals['_ACCOUNTSUMMARYREQUEST']._serialized_start=41 + _globals['_ACCOUNTSUMMARYREQUEST']._serialized_end=152 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AccountSummaryRequest_pb2.pyi b/ib_async/protobuf/AccountSummaryRequest_pb2.pyi new file mode 100644 index 00000000..a8c4ec6e --- /dev/null +++ b/ib_async/protobuf/AccountSummaryRequest_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AccountSummaryRequest(_message.Message): + __slots__ = ("reqId", "group", "tags") + REQID_FIELD_NUMBER: _ClassVar[int] + GROUP_FIELD_NUMBER: _ClassVar[int] + TAGS_FIELD_NUMBER: _ClassVar[int] + reqId: int + group: str + tags: str + def __init__(self, reqId: _Optional[int] = ..., group: _Optional[str] = ..., tags: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/AccountSummary_pb2.py b/ib_async/protobuf/AccountSummary_pb2.py new file mode 100644 index 00000000..1ec9f80f --- /dev/null +++ b/ib_async/protobuf/AccountSummary_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AccountSummary.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AccountSummary.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x41\x63\x63ountSummary.proto\x12\x08protobuf\"\xac\x01\n\x0e\x41\x63\x63ountSummary\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07\x61\x63\x63ount\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x10\n\x03tag\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x12\n\x05value\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x15\n\x08\x63urrency\x18\x05 \x01(\tH\x04\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_accountB\x06\n\x04_tagB\x08\n\x06_valueB\x0b\n\t_currencyB>\n\x16\x63om.ib.client.protobufB\x13\x41\x63\x63ountSummaryProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AccountSummary_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023AccountSummaryProto\252\002\016IBApi.protobuf' + _globals['_ACCOUNTSUMMARY']._serialized_start=35 + _globals['_ACCOUNTSUMMARY']._serialized_end=207 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AccountSummary_pb2.pyi b/ib_async/protobuf/AccountSummary_pb2.pyi new file mode 100644 index 00000000..759ecdb8 --- /dev/null +++ b/ib_async/protobuf/AccountSummary_pb2.pyi @@ -0,0 +1,19 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AccountSummary(_message.Message): + __slots__ = ("reqId", "account", "tag", "value", "currency") + REQID_FIELD_NUMBER: _ClassVar[int] + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + TAG_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + CURRENCY_FIELD_NUMBER: _ClassVar[int] + reqId: int + account: str + tag: str + value: str + currency: str + def __init__(self, reqId: _Optional[int] = ..., account: _Optional[str] = ..., tag: _Optional[str] = ..., value: _Optional[str] = ..., currency: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/AccountUpdateMultiEnd_pb2.py b/ib_async/protobuf/AccountUpdateMultiEnd_pb2.py new file mode 100644 index 00000000..d43c265b --- /dev/null +++ b/ib_async/protobuf/AccountUpdateMultiEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AccountUpdateMultiEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AccountUpdateMultiEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x41\x63\x63ountUpdateMultiEnd.proto\x12\x08protobuf\"5\n\x15\x41\x63\x63ountUpdateMultiEnd\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBE\n\x16\x63om.ib.client.protobufB\x1a\x41\x63\x63ountUpdateMultiEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AccountUpdateMultiEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032AccountUpdateMultiEndProto\252\002\016IBApi.protobuf' + _globals['_ACCOUNTUPDATEMULTIEND']._serialized_start=41 + _globals['_ACCOUNTUPDATEMULTIEND']._serialized_end=94 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AccountUpdateMultiEnd_pb2.pyi b/ib_async/protobuf/AccountUpdateMultiEnd_pb2.pyi new file mode 100644 index 00000000..3877717f --- /dev/null +++ b/ib_async/protobuf/AccountUpdateMultiEnd_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AccountUpdateMultiEnd(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/AccountUpdateMulti_pb2.py b/ib_async/protobuf/AccountUpdateMulti_pb2.py new file mode 100644 index 00000000..dda95c8a --- /dev/null +++ b/ib_async/protobuf/AccountUpdateMulti_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AccountUpdateMulti.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AccountUpdateMulti.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x41\x63\x63ountUpdateMulti.proto\x12\x08protobuf\"\xd6\x01\n\x12\x41\x63\x63ountUpdateMulti\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07\x61\x63\x63ount\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x16\n\tmodelCode\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03key\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x12\n\x05value\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x15\n\x08\x63urrency\x18\x06 \x01(\tH\x05\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_accountB\x0c\n\n_modelCodeB\x06\n\x04_keyB\x08\n\x06_valueB\x0b\n\t_currencyBB\n\x16\x63om.ib.client.protobufB\x17\x41\x63\x63ountUpdateMultiProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AccountUpdateMulti_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027AccountUpdateMultiProto\252\002\016IBApi.protobuf' + _globals['_ACCOUNTUPDATEMULTI']._serialized_start=39 + _globals['_ACCOUNTUPDATEMULTI']._serialized_end=253 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AccountUpdateMulti_pb2.pyi b/ib_async/protobuf/AccountUpdateMulti_pb2.pyi new file mode 100644 index 00000000..71209153 --- /dev/null +++ b/ib_async/protobuf/AccountUpdateMulti_pb2.pyi @@ -0,0 +1,21 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AccountUpdateMulti(_message.Message): + __slots__ = ("reqId", "account", "modelCode", "key", "value", "currency") + REQID_FIELD_NUMBER: _ClassVar[int] + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + MODELCODE_FIELD_NUMBER: _ClassVar[int] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + CURRENCY_FIELD_NUMBER: _ClassVar[int] + reqId: int + account: str + modelCode: str + key: str + value: str + currency: str + def __init__(self, reqId: _Optional[int] = ..., account: _Optional[str] = ..., modelCode: _Optional[str] = ..., key: _Optional[str] = ..., value: _Optional[str] = ..., currency: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/AccountUpdateTime_pb2.py b/ib_async/protobuf/AccountUpdateTime_pb2.py new file mode 100644 index 00000000..87382477 --- /dev/null +++ b/ib_async/protobuf/AccountUpdateTime_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AccountUpdateTime.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AccountUpdateTime.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x41\x63\x63ountUpdateTime.proto\x12\x08protobuf\"9\n\x11\x41\x63\x63ountUpdateTime\x12\x16\n\ttimeStamp\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_timeStampBA\n\x16\x63om.ib.client.protobufB\x16\x41\x63\x63ountUpdateTimeProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AccountUpdateTime_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026AccountUpdateTimeProto\252\002\016IBApi.protobuf' + _globals['_ACCOUNTUPDATETIME']._serialized_start=37 + _globals['_ACCOUNTUPDATETIME']._serialized_end=94 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AccountUpdateTime_pb2.pyi b/ib_async/protobuf/AccountUpdateTime_pb2.pyi new file mode 100644 index 00000000..73671885 --- /dev/null +++ b/ib_async/protobuf/AccountUpdateTime_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AccountUpdateTime(_message.Message): + __slots__ = ("timeStamp",) + TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + timeStamp: str + def __init__(self, timeStamp: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/AccountUpdatesMultiRequest_pb2.py b/ib_async/protobuf/AccountUpdatesMultiRequest_pb2.py new file mode 100644 index 00000000..6e7b4e8b --- /dev/null +++ b/ib_async/protobuf/AccountUpdatesMultiRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AccountUpdatesMultiRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AccountUpdatesMultiRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n AccountUpdatesMultiRequest.proto\x12\x08protobuf\"\xae\x01\n\x1a\x41\x63\x63ountUpdatesMultiRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07\x61\x63\x63ount\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x16\n\tmodelCode\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x19\n\x0cledgerAndNLV\x18\x04 \x01(\x08H\x03\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_accountB\x0c\n\n_modelCodeB\x0f\n\r_ledgerAndNLVBJ\n\x16\x63om.ib.client.protobufB\x1f\x41\x63\x63ountUpdatesMultiRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AccountUpdatesMultiRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\037AccountUpdatesMultiRequestProto\252\002\016IBApi.protobuf' + _globals['_ACCOUNTUPDATESMULTIREQUEST']._serialized_start=47 + _globals['_ACCOUNTUPDATESMULTIREQUEST']._serialized_end=221 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AccountUpdatesMultiRequest_pb2.pyi b/ib_async/protobuf/AccountUpdatesMultiRequest_pb2.pyi new file mode 100644 index 00000000..9105d3a3 --- /dev/null +++ b/ib_async/protobuf/AccountUpdatesMultiRequest_pb2.pyi @@ -0,0 +1,17 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AccountUpdatesMultiRequest(_message.Message): + __slots__ = ("reqId", "account", "modelCode", "ledgerAndNLV") + REQID_FIELD_NUMBER: _ClassVar[int] + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + MODELCODE_FIELD_NUMBER: _ClassVar[int] + LEDGERANDNLV_FIELD_NUMBER: _ClassVar[int] + reqId: int + account: str + modelCode: str + ledgerAndNLV: bool + def __init__(self, reqId: _Optional[int] = ..., account: _Optional[str] = ..., modelCode: _Optional[str] = ..., ledgerAndNLV: bool = ...) -> None: ... diff --git a/ib_async/protobuf/AccountValue_pb2.py b/ib_async/protobuf/AccountValue_pb2.py new file mode 100644 index 00000000..d902eab3 --- /dev/null +++ b/ib_async/protobuf/AccountValue_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AccountValue.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AccountValue.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x41\x63\x63ountValue.proto\x12\x08protobuf\"\x94\x01\n\x0c\x41\x63\x63ountValue\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05value\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x15\n\x08\x63urrency\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x61\x63\x63ountName\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x06\n\x04_keyB\x08\n\x06_valueB\x0b\n\t_currencyB\x0e\n\x0c_accountNameB<\n\x16\x63om.ib.client.protobufB\x11\x41\x63\x63ountValueProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AccountValue_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\021AccountValueProto\252\002\016IBApi.protobuf' + _globals['_ACCOUNTVALUE']._serialized_start=33 + _globals['_ACCOUNTVALUE']._serialized_end=181 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AccountValue_pb2.pyi b/ib_async/protobuf/AccountValue_pb2.pyi new file mode 100644 index 00000000..8867140a --- /dev/null +++ b/ib_async/protobuf/AccountValue_pb2.pyi @@ -0,0 +1,17 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AccountValue(_message.Message): + __slots__ = ("key", "value", "currency", "accountName") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + CURRENCY_FIELD_NUMBER: _ClassVar[int] + ACCOUNTNAME_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + currency: str + accountName: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ..., currency: _Optional[str] = ..., accountName: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/AllOpenOrdersRequest_pb2.py b/ib_async/protobuf/AllOpenOrdersRequest_pb2.py new file mode 100644 index 00000000..80f66e7e --- /dev/null +++ b/ib_async/protobuf/AllOpenOrdersRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AllOpenOrdersRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AllOpenOrdersRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1a\x41llOpenOrdersRequest.proto\x12\x08protobuf\"\x16\n\x14\x41llOpenOrdersRequestBD\n\x16\x63om.ib.client.protobufB\x19\x41llOpenOrdersRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AllOpenOrdersRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031AllOpenOrdersRequestProto\252\002\016IBApi.protobuf' + _globals['_ALLOPENORDERSREQUEST']._serialized_start=40 + _globals['_ALLOPENORDERSREQUEST']._serialized_end=62 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AllOpenOrdersRequest_pb2.pyi b/ib_async/protobuf/AllOpenOrdersRequest_pb2.pyi new file mode 100644 index 00000000..7f37e1bd --- /dev/null +++ b/ib_async/protobuf/AllOpenOrdersRequest_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class AllOpenOrdersRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/AutoOpenOrdersRequest_pb2.py b/ib_async/protobuf/AutoOpenOrdersRequest_pb2.py new file mode 100644 index 00000000..9e711801 --- /dev/null +++ b/ib_async/protobuf/AutoOpenOrdersRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: AutoOpenOrdersRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'AutoOpenOrdersRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x41utoOpenOrdersRequest.proto\x12\x08protobuf\";\n\x15\x41utoOpenOrdersRequest\x12\x15\n\x08\x61utoBind\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x0b\n\t_autoBindBE\n\x16\x63om.ib.client.protobufB\x1a\x41utoOpenOrdersRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'AutoOpenOrdersRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032AutoOpenOrdersRequestProto\252\002\016IBApi.protobuf' + _globals['_AUTOOPENORDERSREQUEST']._serialized_start=41 + _globals['_AUTOOPENORDERSREQUEST']._serialized_end=100 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/AutoOpenOrdersRequest_pb2.pyi b/ib_async/protobuf/AutoOpenOrdersRequest_pb2.pyi new file mode 100644 index 00000000..68e734f4 --- /dev/null +++ b/ib_async/protobuf/AutoOpenOrdersRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class AutoOpenOrdersRequest(_message.Message): + __slots__ = ("autoBind",) + AUTOBIND_FIELD_NUMBER: _ClassVar[int] + autoBind: bool + def __init__(self, autoBind: bool = ...) -> None: ... diff --git a/ib_async/protobuf/CalculateImpliedVolatilityRequest_pb2.py b/ib_async/protobuf/CalculateImpliedVolatilityRequest_pb2.py new file mode 100644 index 00000000..13ccd04b --- /dev/null +++ b/ib_async/protobuf/CalculateImpliedVolatilityRequest_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CalculateImpliedVolatilityRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CalculateImpliedVolatilityRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'CalculateImpliedVolatilityRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xf9\x02\n!CalculateImpliedVolatilityRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x18\n\x0boptionPrice\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x17\n\nunderPrice\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12k\n\x18impliedVolatilityOptions\x18\x05 \x03(\x0b\x32I.protobuf.CalculateImpliedVolatilityRequest.ImpliedVolatilityOptionsEntry\x1a?\n\x1dImpliedVolatilityOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\x0e\n\x0c_optionPriceB\r\n\x0b_underPriceBQ\n\x16\x63om.ib.client.protobufB&CalculateImpliedVolatilityRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CalculateImpliedVolatilityRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB&CalculateImpliedVolatilityRequestProto\252\002\016IBApi.protobuf' + _globals['_CALCULATEIMPLIEDVOLATILITYREQUEST_IMPLIEDVOLATILITYOPTIONSENTRY']._loaded_options = None + _globals['_CALCULATEIMPLIEDVOLATILITYREQUEST_IMPLIEDVOLATILITYOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_CALCULATEIMPLIEDVOLATILITYREQUEST']._serialized_start=70 + _globals['_CALCULATEIMPLIEDVOLATILITYREQUEST']._serialized_end=447 + _globals['_CALCULATEIMPLIEDVOLATILITYREQUEST_IMPLIEDVOLATILITYOPTIONSENTRY']._serialized_start=330 + _globals['_CALCULATEIMPLIEDVOLATILITYREQUEST_IMPLIEDVOLATILITYOPTIONSENTRY']._serialized_end=393 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CalculateImpliedVolatilityRequest_pb2.pyi b/ib_async/protobuf/CalculateImpliedVolatilityRequest_pb2.pyi new file mode 100644 index 00000000..973a9d34 --- /dev/null +++ b/ib_async/protobuf/CalculateImpliedVolatilityRequest_pb2.pyi @@ -0,0 +1,29 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class CalculateImpliedVolatilityRequest(_message.Message): + __slots__ = ("reqId", "contract", "optionPrice", "underPrice", "impliedVolatilityOptions") + class ImpliedVolatilityOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + OPTIONPRICE_FIELD_NUMBER: _ClassVar[int] + UNDERPRICE_FIELD_NUMBER: _ClassVar[int] + IMPLIEDVOLATILITYOPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + optionPrice: float + underPrice: float + impliedVolatilityOptions: _containers.ScalarMap[str, str] + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., optionPrice: _Optional[float] = ..., underPrice: _Optional[float] = ..., impliedVolatilityOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/CalculateOptionPriceRequest_pb2.py b/ib_async/protobuf/CalculateOptionPriceRequest_pb2.py new file mode 100644 index 00000000..9b5d0244 --- /dev/null +++ b/ib_async/protobuf/CalculateOptionPriceRequest_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CalculateOptionPriceRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CalculateOptionPriceRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!CalculateOptionPriceRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xd9\x02\n\x1b\x43\x61lculateOptionPriceRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x17\n\nvolatility\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x17\n\nunderPrice\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12Y\n\x12optionPriceOptions\x18\x05 \x03(\x0b\x32=.protobuf.CalculateOptionPriceRequest.OptionPriceOptionsEntry\x1a\x39\n\x17OptionPriceOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\r\n\x0b_volatilityB\r\n\x0b_underPriceBK\n\x16\x63om.ib.client.protobufB CalculateOptionPriceRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CalculateOptionPriceRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB CalculateOptionPriceRequestProto\252\002\016IBApi.protobuf' + _globals['_CALCULATEOPTIONPRICEREQUEST_OPTIONPRICEOPTIONSENTRY']._loaded_options = None + _globals['_CALCULATEOPTIONPRICEREQUEST_OPTIONPRICEOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_CALCULATEOPTIONPRICEREQUEST']._serialized_start=64 + _globals['_CALCULATEOPTIONPRICEREQUEST']._serialized_end=409 + _globals['_CALCULATEOPTIONPRICEREQUEST_OPTIONPRICEOPTIONSENTRY']._serialized_start=299 + _globals['_CALCULATEOPTIONPRICEREQUEST_OPTIONPRICEOPTIONSENTRY']._serialized_end=356 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CalculateOptionPriceRequest_pb2.pyi b/ib_async/protobuf/CalculateOptionPriceRequest_pb2.pyi new file mode 100644 index 00000000..cb6dbcbd --- /dev/null +++ b/ib_async/protobuf/CalculateOptionPriceRequest_pb2.pyi @@ -0,0 +1,29 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class CalculateOptionPriceRequest(_message.Message): + __slots__ = ("reqId", "contract", "volatility", "underPrice", "optionPriceOptions") + class OptionPriceOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + VOLATILITY_FIELD_NUMBER: _ClassVar[int] + UNDERPRICE_FIELD_NUMBER: _ClassVar[int] + OPTIONPRICEOPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + volatility: float + underPrice: float + optionPriceOptions: _containers.ScalarMap[str, str] + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., volatility: _Optional[float] = ..., underPrice: _Optional[float] = ..., optionPriceOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelAccountSummary_pb2.py b/ib_async/protobuf/CancelAccountSummary_pb2.py new file mode 100644 index 00000000..2dfea5f9 --- /dev/null +++ b/ib_async/protobuf/CancelAccountSummary_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelAccountSummary.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelAccountSummary.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1a\x43\x61ncelAccountSummary.proto\x12\x08protobuf\"4\n\x14\x43\x61ncelAccountSummary\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBD\n\x16\x63om.ib.client.protobufB\x19\x43\x61ncelAccountSummaryProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelAccountSummary_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031CancelAccountSummaryProto\252\002\016IBApi.protobuf' + _globals['_CANCELACCOUNTSUMMARY']._serialized_start=40 + _globals['_CANCELACCOUNTSUMMARY']._serialized_end=92 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelAccountSummary_pb2.pyi b/ib_async/protobuf/CancelAccountSummary_pb2.pyi new file mode 100644 index 00000000..866df24c --- /dev/null +++ b/ib_async/protobuf/CancelAccountSummary_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelAccountSummary(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelAccountUpdatesMulti_pb2.py b/ib_async/protobuf/CancelAccountUpdatesMulti_pb2.py new file mode 100644 index 00000000..5fe8f65a --- /dev/null +++ b/ib_async/protobuf/CancelAccountUpdatesMulti_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelAccountUpdatesMulti.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelAccountUpdatesMulti.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x43\x61ncelAccountUpdatesMulti.proto\x12\x08protobuf\"9\n\x19\x43\x61ncelAccountUpdatesMulti\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBI\n\x16\x63om.ib.client.protobufB\x1e\x43\x61ncelAccountUpdatesMultiProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelAccountUpdatesMulti_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\036CancelAccountUpdatesMultiProto\252\002\016IBApi.protobuf' + _globals['_CANCELACCOUNTUPDATESMULTI']._serialized_start=45 + _globals['_CANCELACCOUNTUPDATESMULTI']._serialized_end=102 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelAccountUpdatesMulti_pb2.pyi b/ib_async/protobuf/CancelAccountUpdatesMulti_pb2.pyi new file mode 100644 index 00000000..2468e76d --- /dev/null +++ b/ib_async/protobuf/CancelAccountUpdatesMulti_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelAccountUpdatesMulti(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelCalculateImpliedVolatility_pb2.py b/ib_async/protobuf/CancelCalculateImpliedVolatility_pb2.py new file mode 100644 index 00000000..db1ff868 --- /dev/null +++ b/ib_async/protobuf/CancelCalculateImpliedVolatility_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelCalculateImpliedVolatility.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelCalculateImpliedVolatility.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&CancelCalculateImpliedVolatility.proto\x12\x08protobuf\"@\n CancelCalculateImpliedVolatility\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBP\n\x16\x63om.ib.client.protobufB%CancelCalculateImpliedVolatilityProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelCalculateImpliedVolatility_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB%CancelCalculateImpliedVolatilityProto\252\002\016IBApi.protobuf' + _globals['_CANCELCALCULATEIMPLIEDVOLATILITY']._serialized_start=52 + _globals['_CANCELCALCULATEIMPLIEDVOLATILITY']._serialized_end=116 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelCalculateImpliedVolatility_pb2.pyi b/ib_async/protobuf/CancelCalculateImpliedVolatility_pb2.pyi new file mode 100644 index 00000000..c9e13ec6 --- /dev/null +++ b/ib_async/protobuf/CancelCalculateImpliedVolatility_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelCalculateImpliedVolatility(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelCalculateOptionPrice_pb2.py b/ib_async/protobuf/CancelCalculateOptionPrice_pb2.py new file mode 100644 index 00000000..7671b75b --- /dev/null +++ b/ib_async/protobuf/CancelCalculateOptionPrice_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelCalculateOptionPrice.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelCalculateOptionPrice.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n CancelCalculateOptionPrice.proto\x12\x08protobuf\":\n\x1a\x43\x61ncelCalculateOptionPrice\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBJ\n\x16\x63om.ib.client.protobufB\x1f\x43\x61ncelCalculateOptionPriceProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelCalculateOptionPrice_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\037CancelCalculateOptionPriceProto\252\002\016IBApi.protobuf' + _globals['_CANCELCALCULATEOPTIONPRICE']._serialized_start=46 + _globals['_CANCELCALCULATEOPTIONPRICE']._serialized_end=104 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelCalculateOptionPrice_pb2.pyi b/ib_async/protobuf/CancelCalculateOptionPrice_pb2.pyi new file mode 100644 index 00000000..aec06de9 --- /dev/null +++ b/ib_async/protobuf/CancelCalculateOptionPrice_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelCalculateOptionPrice(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelContractData_pb2.py b/ib_async/protobuf/CancelContractData_pb2.py new file mode 100644 index 00000000..f883bc3a --- /dev/null +++ b/ib_async/protobuf/CancelContractData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelContractData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelContractData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x43\x61ncelContractData.proto\x12\x08protobuf\"2\n\x12\x43\x61ncelContractData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBB\n\x16\x63om.ib.client.protobufB\x17\x43\x61ncelContractDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelContractData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027CancelContractDataProto\252\002\016IBApi.protobuf' + _globals['_CANCELCONTRACTDATA']._serialized_start=38 + _globals['_CANCELCONTRACTDATA']._serialized_end=88 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelContractData_pb2.pyi b/ib_async/protobuf/CancelContractData_pb2.pyi new file mode 100644 index 00000000..faed6b51 --- /dev/null +++ b/ib_async/protobuf/CancelContractData_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelContractData(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelFundamentalsData_pb2.py b/ib_async/protobuf/CancelFundamentalsData_pb2.py new file mode 100644 index 00000000..090e0c15 --- /dev/null +++ b/ib_async/protobuf/CancelFundamentalsData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelFundamentalsData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelFundamentalsData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x43\x61ncelFundamentalsData.proto\x12\x08protobuf\"6\n\x16\x43\x61ncelFundamentalsData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBF\n\x16\x63om.ib.client.protobufB\x1b\x43\x61ncelFundamentalsDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelFundamentalsData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\033CancelFundamentalsDataProto\252\002\016IBApi.protobuf' + _globals['_CANCELFUNDAMENTALSDATA']._serialized_start=42 + _globals['_CANCELFUNDAMENTALSDATA']._serialized_end=96 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelFundamentalsData_pb2.pyi b/ib_async/protobuf/CancelFundamentalsData_pb2.pyi new file mode 100644 index 00000000..10884f39 --- /dev/null +++ b/ib_async/protobuf/CancelFundamentalsData_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelFundamentalsData(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelHeadTimestamp_pb2.py b/ib_async/protobuf/CancelHeadTimestamp_pb2.py new file mode 100644 index 00000000..56893acf --- /dev/null +++ b/ib_async/protobuf/CancelHeadTimestamp_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelHeadTimestamp.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelHeadTimestamp.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x43\x61ncelHeadTimestamp.proto\x12\x08protobuf\"3\n\x13\x43\x61ncelHeadTimestamp\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBC\n\x16\x63om.ib.client.protobufB\x18\x43\x61ncelHeadTimestampProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelHeadTimestamp_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030CancelHeadTimestampProto\252\002\016IBApi.protobuf' + _globals['_CANCELHEADTIMESTAMP']._serialized_start=39 + _globals['_CANCELHEADTIMESTAMP']._serialized_end=90 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelHeadTimestamp_pb2.pyi b/ib_async/protobuf/CancelHeadTimestamp_pb2.pyi new file mode 100644 index 00000000..f1f26fbc --- /dev/null +++ b/ib_async/protobuf/CancelHeadTimestamp_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelHeadTimestamp(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelHistogramData_pb2.py b/ib_async/protobuf/CancelHistogramData_pb2.py new file mode 100644 index 00000000..f27758c7 --- /dev/null +++ b/ib_async/protobuf/CancelHistogramData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelHistogramData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelHistogramData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x43\x61ncelHistogramData.proto\x12\x08protobuf\"3\n\x13\x43\x61ncelHistogramData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBC\n\x16\x63om.ib.client.protobufB\x18\x43\x61ncelHistogramDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelHistogramData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030CancelHistogramDataProto\252\002\016IBApi.protobuf' + _globals['_CANCELHISTOGRAMDATA']._serialized_start=39 + _globals['_CANCELHISTOGRAMDATA']._serialized_end=90 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelHistogramData_pb2.pyi b/ib_async/protobuf/CancelHistogramData_pb2.pyi new file mode 100644 index 00000000..6c441f05 --- /dev/null +++ b/ib_async/protobuf/CancelHistogramData_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelHistogramData(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelHistoricalData_pb2.py b/ib_async/protobuf/CancelHistoricalData_pb2.py new file mode 100644 index 00000000..25bbce56 --- /dev/null +++ b/ib_async/protobuf/CancelHistoricalData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelHistoricalData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelHistoricalData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1a\x43\x61ncelHistoricalData.proto\x12\x08protobuf\"4\n\x14\x43\x61ncelHistoricalData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBD\n\x16\x63om.ib.client.protobufB\x19\x43\x61ncelHistoricalDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelHistoricalData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031CancelHistoricalDataProto\252\002\016IBApi.protobuf' + _globals['_CANCELHISTORICALDATA']._serialized_start=40 + _globals['_CANCELHISTORICALDATA']._serialized_end=92 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelHistoricalData_pb2.pyi b/ib_async/protobuf/CancelHistoricalData_pb2.pyi new file mode 100644 index 00000000..1de16409 --- /dev/null +++ b/ib_async/protobuf/CancelHistoricalData_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelHistoricalData(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelHistoricalTicks_pb2.py b/ib_async/protobuf/CancelHistoricalTicks_pb2.py new file mode 100644 index 00000000..7c206d0e --- /dev/null +++ b/ib_async/protobuf/CancelHistoricalTicks_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelHistoricalTicks.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelHistoricalTicks.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x43\x61ncelHistoricalTicks.proto\x12\x08protobuf\"5\n\x15\x43\x61ncelHistoricalTicks\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBE\n\x16\x63om.ib.client.protobufB\x1a\x43\x61ncelHistoricalTicksProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelHistoricalTicks_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032CancelHistoricalTicksProto\252\002\016IBApi.protobuf' + _globals['_CANCELHISTORICALTICKS']._serialized_start=41 + _globals['_CANCELHISTORICALTICKS']._serialized_end=94 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelHistoricalTicks_pb2.pyi b/ib_async/protobuf/CancelHistoricalTicks_pb2.pyi new file mode 100644 index 00000000..fd971b4d --- /dev/null +++ b/ib_async/protobuf/CancelHistoricalTicks_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelHistoricalTicks(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelMarketData_pb2.py b/ib_async/protobuf/CancelMarketData_pb2.py new file mode 100644 index 00000000..e4344b64 --- /dev/null +++ b/ib_async/protobuf/CancelMarketData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelMarketData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelMarketData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x43\x61ncelMarketData.proto\x12\x08protobuf\"0\n\x10\x43\x61ncelMarketData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdB@\n\x16\x63om.ib.client.protobufB\x15\x43\x61ncelMarketDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelMarketData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025CancelMarketDataProto\252\002\016IBApi.protobuf' + _globals['_CANCELMARKETDATA']._serialized_start=36 + _globals['_CANCELMARKETDATA']._serialized_end=84 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelMarketData_pb2.pyi b/ib_async/protobuf/CancelMarketData_pb2.pyi new file mode 100644 index 00000000..6769a4f4 --- /dev/null +++ b/ib_async/protobuf/CancelMarketData_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelMarketData(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelMarketDepth_pb2.py b/ib_async/protobuf/CancelMarketDepth_pb2.py new file mode 100644 index 00000000..2973a872 --- /dev/null +++ b/ib_async/protobuf/CancelMarketDepth_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelMarketDepth.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelMarketDepth.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x43\x61ncelMarketDepth.proto\x12\x08protobuf\"]\n\x11\x43\x61ncelMarketDepth\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x19\n\x0cisSmartDepth\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x0f\n\r_isSmartDepthBA\n\x16\x63om.ib.client.protobufB\x16\x43\x61ncelMarketDepthProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelMarketDepth_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026CancelMarketDepthProto\252\002\016IBApi.protobuf' + _globals['_CANCELMARKETDEPTH']._serialized_start=37 + _globals['_CANCELMARKETDEPTH']._serialized_end=130 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelMarketDepth_pb2.pyi b/ib_async/protobuf/CancelMarketDepth_pb2.pyi new file mode 100644 index 00000000..796dc07e --- /dev/null +++ b/ib_async/protobuf/CancelMarketDepth_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelMarketDepth(_message.Message): + __slots__ = ("reqId", "isSmartDepth") + REQID_FIELD_NUMBER: _ClassVar[int] + ISSMARTDEPTH_FIELD_NUMBER: _ClassVar[int] + reqId: int + isSmartDepth: bool + def __init__(self, reqId: _Optional[int] = ..., isSmartDepth: bool = ...) -> None: ... diff --git a/ib_async/protobuf/CancelNewsBulletins_pb2.py b/ib_async/protobuf/CancelNewsBulletins_pb2.py new file mode 100644 index 00000000..c08ff30c --- /dev/null +++ b/ib_async/protobuf/CancelNewsBulletins_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelNewsBulletins.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelNewsBulletins.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x43\x61ncelNewsBulletins.proto\x12\x08protobuf\"\x15\n\x13\x43\x61ncelNewsBulletinsBC\n\x16\x63om.ib.client.protobufB\x18\x43\x61ncelNewsBulletinsProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelNewsBulletins_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030CancelNewsBulletinsProto\252\002\016IBApi.protobuf' + _globals['_CANCELNEWSBULLETINS']._serialized_start=39 + _globals['_CANCELNEWSBULLETINS']._serialized_end=60 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelNewsBulletins_pb2.pyi b/ib_async/protobuf/CancelNewsBulletins_pb2.pyi new file mode 100644 index 00000000..dc3056a3 --- /dev/null +++ b/ib_async/protobuf/CancelNewsBulletins_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelNewsBulletins(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/CancelOrderRequest_pb2.py b/ib_async/protobuf/CancelOrderRequest_pb2.py new file mode 100644 index 00000000..c17553d2 --- /dev/null +++ b/ib_async/protobuf/CancelOrderRequest_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelOrderRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelOrderRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import OrderCancel_pb2 as OrderCancel__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x43\x61ncelOrderRequest.proto\x12\x08protobuf\x1a\x11OrderCancel.proto\"w\n\x12\x43\x61ncelOrderRequest\x12\x14\n\x07orderId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12/\n\x0borderCancel\x18\x02 \x01(\x0b\x32\x15.protobuf.OrderCancelH\x01\x88\x01\x01\x42\n\n\x08_orderIdB\x0e\n\x0c_orderCancelBB\n\x16\x63om.ib.client.protobufB\x17\x43\x61ncelOrderRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelOrderRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027CancelOrderRequestProto\252\002\016IBApi.protobuf' + _globals['_CANCELORDERREQUEST']._serialized_start=57 + _globals['_CANCELORDERREQUEST']._serialized_end=176 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelOrderRequest_pb2.pyi b/ib_async/protobuf/CancelOrderRequest_pb2.pyi new file mode 100644 index 00000000..8ec06d20 --- /dev/null +++ b/ib_async/protobuf/CancelOrderRequest_pb2.pyi @@ -0,0 +1,15 @@ +import OrderCancel_pb2 as _OrderCancel_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelOrderRequest(_message.Message): + __slots__ = ("orderId", "orderCancel") + ORDERID_FIELD_NUMBER: _ClassVar[int] + ORDERCANCEL_FIELD_NUMBER: _ClassVar[int] + orderId: int + orderCancel: _OrderCancel_pb2.OrderCancel + def __init__(self, orderId: _Optional[int] = ..., orderCancel: _Optional[_Union[_OrderCancel_pb2.OrderCancel, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelPnLSingle_pb2.py b/ib_async/protobuf/CancelPnLSingle_pb2.py new file mode 100644 index 00000000..af55c580 --- /dev/null +++ b/ib_async/protobuf/CancelPnLSingle_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelPnLSingle.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelPnLSingle.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x43\x61ncelPnLSingle.proto\x12\x08protobuf\"/\n\x0f\x43\x61ncelPnLSingle\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdB?\n\x16\x63om.ib.client.protobufB\x14\x43\x61ncelPnLSingleProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelPnLSingle_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024CancelPnLSingleProto\252\002\016IBApi.protobuf' + _globals['_CANCELPNLSINGLE']._serialized_start=35 + _globals['_CANCELPNLSINGLE']._serialized_end=82 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelPnLSingle_pb2.pyi b/ib_async/protobuf/CancelPnLSingle_pb2.pyi new file mode 100644 index 00000000..01b73859 --- /dev/null +++ b/ib_async/protobuf/CancelPnLSingle_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelPnLSingle(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelPnL_pb2.py b/ib_async/protobuf/CancelPnL_pb2.py new file mode 100644 index 00000000..d93259a0 --- /dev/null +++ b/ib_async/protobuf/CancelPnL_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelPnL.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelPnL.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0f\x43\x61ncelPnL.proto\x12\x08protobuf\")\n\tCancelPnL\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdB9\n\x16\x63om.ib.client.protobufB\x0e\x43\x61ncelPnLProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelPnL_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\016CancelPnLProto\252\002\016IBApi.protobuf' + _globals['_CANCELPNL']._serialized_start=29 + _globals['_CANCELPNL']._serialized_end=70 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelPnL_pb2.pyi b/ib_async/protobuf/CancelPnL_pb2.pyi new file mode 100644 index 00000000..63617376 --- /dev/null +++ b/ib_async/protobuf/CancelPnL_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelPnL(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelPositionsMulti_pb2.py b/ib_async/protobuf/CancelPositionsMulti_pb2.py new file mode 100644 index 00000000..41143538 --- /dev/null +++ b/ib_async/protobuf/CancelPositionsMulti_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelPositionsMulti.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelPositionsMulti.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1a\x43\x61ncelPositionsMulti.proto\x12\x08protobuf\"4\n\x14\x43\x61ncelPositionsMulti\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBD\n\x16\x63om.ib.client.protobufB\x19\x43\x61ncelPositionsMultiProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelPositionsMulti_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031CancelPositionsMultiProto\252\002\016IBApi.protobuf' + _globals['_CANCELPOSITIONSMULTI']._serialized_start=40 + _globals['_CANCELPOSITIONSMULTI']._serialized_end=92 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelPositionsMulti_pb2.pyi b/ib_async/protobuf/CancelPositionsMulti_pb2.pyi new file mode 100644 index 00000000..b8e1387c --- /dev/null +++ b/ib_async/protobuf/CancelPositionsMulti_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelPositionsMulti(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelPositions_pb2.py b/ib_async/protobuf/CancelPositions_pb2.py new file mode 100644 index 00000000..fc5df4c8 --- /dev/null +++ b/ib_async/protobuf/CancelPositions_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelPositions.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelPositions.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x43\x61ncelPositions.proto\x12\x08protobuf\"\x11\n\x0f\x43\x61ncelPositionsB?\n\x16\x63om.ib.client.protobufB\x14\x43\x61ncelPositionsProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelPositions_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024CancelPositionsProto\252\002\016IBApi.protobuf' + _globals['_CANCELPOSITIONS']._serialized_start=35 + _globals['_CANCELPOSITIONS']._serialized_end=52 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelPositions_pb2.pyi b/ib_async/protobuf/CancelPositions_pb2.pyi new file mode 100644 index 00000000..211c0dc8 --- /dev/null +++ b/ib_async/protobuf/CancelPositions_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelPositions(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/CancelRealTimeBars_pb2.py b/ib_async/protobuf/CancelRealTimeBars_pb2.py new file mode 100644 index 00000000..9f42ed6c --- /dev/null +++ b/ib_async/protobuf/CancelRealTimeBars_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelRealTimeBars.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelRealTimeBars.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x43\x61ncelRealTimeBars.proto\x12\x08protobuf\"2\n\x12\x43\x61ncelRealTimeBars\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBB\n\x16\x63om.ib.client.protobufB\x17\x43\x61ncelRealTimeBarsProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelRealTimeBars_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027CancelRealTimeBarsProto\252\002\016IBApi.protobuf' + _globals['_CANCELREALTIMEBARS']._serialized_start=38 + _globals['_CANCELREALTIMEBARS']._serialized_end=88 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelRealTimeBars_pb2.pyi b/ib_async/protobuf/CancelRealTimeBars_pb2.pyi new file mode 100644 index 00000000..f01eb7c8 --- /dev/null +++ b/ib_async/protobuf/CancelRealTimeBars_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelRealTimeBars(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelScannerSubscription_pb2.py b/ib_async/protobuf/CancelScannerSubscription_pb2.py new file mode 100644 index 00000000..11c8dec6 --- /dev/null +++ b/ib_async/protobuf/CancelScannerSubscription_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelScannerSubscription.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelScannerSubscription.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x43\x61ncelScannerSubscription.proto\x12\x08protobuf\"9\n\x19\x43\x61ncelScannerSubscription\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBI\n\x16\x63om.ib.client.protobufB\x1e\x43\x61ncelScannerSubscriptionProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelScannerSubscription_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\036CancelScannerSubscriptionProto\252\002\016IBApi.protobuf' + _globals['_CANCELSCANNERSUBSCRIPTION']._serialized_start=45 + _globals['_CANCELSCANNERSUBSCRIPTION']._serialized_end=102 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelScannerSubscription_pb2.pyi b/ib_async/protobuf/CancelScannerSubscription_pb2.pyi new file mode 100644 index 00000000..d6b07ce7 --- /dev/null +++ b/ib_async/protobuf/CancelScannerSubscription_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelScannerSubscription(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelTickByTick_pb2.py b/ib_async/protobuf/CancelTickByTick_pb2.py new file mode 100644 index 00000000..77822e00 --- /dev/null +++ b/ib_async/protobuf/CancelTickByTick_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelTickByTick.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelTickByTick.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x43\x61ncelTickByTick.proto\x12\x08protobuf\"0\n\x10\x43\x61ncelTickByTick\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdB@\n\x16\x63om.ib.client.protobufB\x15\x43\x61ncelTickByTickProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelTickByTick_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025CancelTickByTickProto\252\002\016IBApi.protobuf' + _globals['_CANCELTICKBYTICK']._serialized_start=36 + _globals['_CANCELTICKBYTICK']._serialized_end=84 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelTickByTick_pb2.pyi b/ib_async/protobuf/CancelTickByTick_pb2.pyi new file mode 100644 index 00000000..3060dc29 --- /dev/null +++ b/ib_async/protobuf/CancelTickByTick_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelTickByTick(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelWshEventData_pb2.py b/ib_async/protobuf/CancelWshEventData_pb2.py new file mode 100644 index 00000000..c34048f5 --- /dev/null +++ b/ib_async/protobuf/CancelWshEventData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelWshEventData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelWshEventData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x43\x61ncelWshEventData.proto\x12\x08protobuf\"2\n\x12\x43\x61ncelWshEventData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBB\n\x16\x63om.ib.client.protobufB\x17\x43\x61ncelWshEventDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelWshEventData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027CancelWshEventDataProto\252\002\016IBApi.protobuf' + _globals['_CANCELWSHEVENTDATA']._serialized_start=38 + _globals['_CANCELWSHEVENTDATA']._serialized_end=88 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelWshEventData_pb2.pyi b/ib_async/protobuf/CancelWshEventData_pb2.pyi new file mode 100644 index 00000000..3c7c8194 --- /dev/null +++ b/ib_async/protobuf/CancelWshEventData_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelWshEventData(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CancelWshMetaData_pb2.py b/ib_async/protobuf/CancelWshMetaData_pb2.py new file mode 100644 index 00000000..e3f38c06 --- /dev/null +++ b/ib_async/protobuf/CancelWshMetaData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CancelWshMetaData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CancelWshMetaData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x43\x61ncelWshMetaData.proto\x12\x08protobuf\"1\n\x11\x43\x61ncelWshMetaData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBA\n\x16\x63om.ib.client.protobufB\x16\x43\x61ncelWshMetaDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CancelWshMetaData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026CancelWshMetaDataProto\252\002\016IBApi.protobuf' + _globals['_CANCELWSHMETADATA']._serialized_start=37 + _globals['_CANCELWSHMETADATA']._serialized_end=86 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CancelWshMetaData_pb2.pyi b/ib_async/protobuf/CancelWshMetaData_pb2.pyi new file mode 100644 index 00000000..d43dcd15 --- /dev/null +++ b/ib_async/protobuf/CancelWshMetaData_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CancelWshMetaData(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/ComboLeg_pb2.py b/ib_async/protobuf/ComboLeg_pb2.py new file mode 100644 index 00000000..9bf4534d --- /dev/null +++ b/ib_async/protobuf/ComboLeg_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ComboLeg.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ComboLeg.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x43omboLeg.proto\x12\x08protobuf\"\xea\x02\n\x08\x43omboLeg\x12\x12\n\x05\x63onId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x12\n\x05ratio\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x13\n\x06\x61\x63tion\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x15\n\x08\x65xchange\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x16\n\topenClose\x18\x05 \x01(\x05H\x04\x88\x01\x01\x12\x1b\n\x0eshortSalesSlot\x18\x06 \x01(\x05H\x05\x88\x01\x01\x12\x1f\n\x12\x64\x65signatedLocation\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x17\n\nexemptCode\x18\x08 \x01(\x05H\x07\x88\x01\x01\x12\x18\n\x0bperLegPrice\x18\t \x01(\x01H\x08\x88\x01\x01\x42\x08\n\x06_conIdB\x08\n\x06_ratioB\t\n\x07_actionB\x0b\n\t_exchangeB\x0c\n\n_openCloseB\x11\n\x0f_shortSalesSlotB\x15\n\x13_designatedLocationB\r\n\x0b_exemptCodeB\x0e\n\x0c_perLegPriceB8\n\x16\x63om.ib.client.protobufB\rComboLegProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ComboLeg_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\rComboLegProto\252\002\016IBApi.protobuf' + _globals['_COMBOLEG']._serialized_start=29 + _globals['_COMBOLEG']._serialized_end=391 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ComboLeg_pb2.pyi b/ib_async/protobuf/ComboLeg_pb2.pyi new file mode 100644 index 00000000..a6f55cc1 --- /dev/null +++ b/ib_async/protobuf/ComboLeg_pb2.pyi @@ -0,0 +1,27 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ComboLeg(_message.Message): + __slots__ = ("conId", "ratio", "action", "exchange", "openClose", "shortSalesSlot", "designatedLocation", "exemptCode", "perLegPrice") + CONID_FIELD_NUMBER: _ClassVar[int] + RATIO_FIELD_NUMBER: _ClassVar[int] + ACTION_FIELD_NUMBER: _ClassVar[int] + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + OPENCLOSE_FIELD_NUMBER: _ClassVar[int] + SHORTSALESSLOT_FIELD_NUMBER: _ClassVar[int] + DESIGNATEDLOCATION_FIELD_NUMBER: _ClassVar[int] + EXEMPTCODE_FIELD_NUMBER: _ClassVar[int] + PERLEGPRICE_FIELD_NUMBER: _ClassVar[int] + conId: int + ratio: int + action: str + exchange: str + openClose: int + shortSalesSlot: int + designatedLocation: str + exemptCode: int + perLegPrice: float + def __init__(self, conId: _Optional[int] = ..., ratio: _Optional[int] = ..., action: _Optional[str] = ..., exchange: _Optional[str] = ..., openClose: _Optional[int] = ..., shortSalesSlot: _Optional[int] = ..., designatedLocation: _Optional[str] = ..., exemptCode: _Optional[int] = ..., perLegPrice: _Optional[float] = ...) -> None: ... diff --git a/ib_async/protobuf/CommissionAndFeesReport_pb2.py b/ib_async/protobuf/CommissionAndFeesReport_pb2.py new file mode 100644 index 00000000..c729f628 --- /dev/null +++ b/ib_async/protobuf/CommissionAndFeesReport_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CommissionAndFeesReport.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CommissionAndFeesReport.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x43ommissionAndFeesReport.proto\x12\x08protobuf\"\x9d\x02\n\x17\x43ommissionAndFeesReport\x12\x13\n\x06\x65xecId\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x1e\n\x11\x63ommissionAndFees\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x15\n\x08\x63urrency\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0brealizedPNL\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tbondYield\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12 \n\x13yieldRedemptionDate\x18\x06 \x01(\tH\x05\x88\x01\x01\x42\t\n\x07_execIdB\x14\n\x12_commissionAndFeesB\x0b\n\t_currencyB\x0e\n\x0c_realizedPNLB\x0c\n\n_bondYieldB\x16\n\x14_yieldRedemptionDateBG\n\x16\x63om.ib.client.protobufB\x1c\x43ommissionAndFeesReportProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CommissionAndFeesReport_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\034CommissionAndFeesReportProto\252\002\016IBApi.protobuf' + _globals['_COMMISSIONANDFEESREPORT']._serialized_start=44 + _globals['_COMMISSIONANDFEESREPORT']._serialized_end=329 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CommissionAndFeesReport_pb2.pyi b/ib_async/protobuf/CommissionAndFeesReport_pb2.pyi new file mode 100644 index 00000000..13b8f770 --- /dev/null +++ b/ib_async/protobuf/CommissionAndFeesReport_pb2.pyi @@ -0,0 +1,21 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CommissionAndFeesReport(_message.Message): + __slots__ = ("execId", "commissionAndFees", "currency", "realizedPNL", "bondYield", "yieldRedemptionDate") + EXECID_FIELD_NUMBER: _ClassVar[int] + COMMISSIONANDFEES_FIELD_NUMBER: _ClassVar[int] + CURRENCY_FIELD_NUMBER: _ClassVar[int] + REALIZEDPNL_FIELD_NUMBER: _ClassVar[int] + BONDYIELD_FIELD_NUMBER: _ClassVar[int] + YIELDREDEMPTIONDATE_FIELD_NUMBER: _ClassVar[int] + execId: str + commissionAndFees: float + currency: str + realizedPNL: float + bondYield: float + yieldRedemptionDate: str + def __init__(self, execId: _Optional[str] = ..., commissionAndFees: _Optional[float] = ..., currency: _Optional[str] = ..., realizedPNL: _Optional[float] = ..., bondYield: _Optional[float] = ..., yieldRedemptionDate: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/CompletedOrder_pb2.py b/ib_async/protobuf/CompletedOrder_pb2.py new file mode 100644 index 00000000..4004f578 --- /dev/null +++ b/ib_async/protobuf/CompletedOrder_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CompletedOrder.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CompletedOrder.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 +from . import Order_pb2 as Order__pb2 +from . import OrderState_pb2 as OrderState__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x43ompletedOrder.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\x1a\x0bOrder.proto\x1a\x10OrderState.proto\"\xb5\x01\n\x0e\x43ompletedOrder\x12)\n\x08\x63ontract\x18\x01 \x01(\x0b\x32\x12.protobuf.ContractH\x00\x88\x01\x01\x12#\n\x05order\x18\x02 \x01(\x0b\x32\x0f.protobuf.OrderH\x01\x88\x01\x01\x12-\n\norderState\x18\x03 \x01(\x0b\x32\x14.protobuf.OrderStateH\x02\x88\x01\x01\x42\x0b\n\t_contractB\x08\n\x06_orderB\r\n\x0b_orderStateB>\n\x16\x63om.ib.client.protobufB\x13\x43ompletedOrderProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CompletedOrder_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023CompletedOrderProto\252\002\016IBApi.protobuf' + _globals['_COMPLETEDORDER']._serialized_start=82 + _globals['_COMPLETEDORDER']._serialized_end=263 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CompletedOrder_pb2.pyi b/ib_async/protobuf/CompletedOrder_pb2.pyi new file mode 100644 index 00000000..7b98e6a1 --- /dev/null +++ b/ib_async/protobuf/CompletedOrder_pb2.pyi @@ -0,0 +1,19 @@ +import Contract_pb2 as _Contract_pb2 +import Order_pb2 as _Order_pb2 +import OrderState_pb2 as _OrderState_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class CompletedOrder(_message.Message): + __slots__ = ("contract", "order", "orderState") + CONTRACT_FIELD_NUMBER: _ClassVar[int] + ORDER_FIELD_NUMBER: _ClassVar[int] + ORDERSTATE_FIELD_NUMBER: _ClassVar[int] + contract: _Contract_pb2.Contract + order: _Order_pb2.Order + orderState: _OrderState_pb2.OrderState + def __init__(self, contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., order: _Optional[_Union[_Order_pb2.Order, _Mapping]] = ..., orderState: _Optional[_Union[_OrderState_pb2.OrderState, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/CompletedOrdersEnd_pb2.py b/ib_async/protobuf/CompletedOrdersEnd_pb2.py new file mode 100644 index 00000000..4ab1b26d --- /dev/null +++ b/ib_async/protobuf/CompletedOrdersEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CompletedOrdersEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CompletedOrdersEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x43ompletedOrdersEnd.proto\x12\x08protobuf\"\x14\n\x12\x43ompletedOrdersEndBB\n\x16\x63om.ib.client.protobufB\x17\x43ompletedOrdersEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CompletedOrdersEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027CompletedOrdersEndProto\252\002\016IBApi.protobuf' + _globals['_COMPLETEDORDERSEND']._serialized_start=38 + _globals['_COMPLETEDORDERSEND']._serialized_end=58 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CompletedOrdersEnd_pb2.pyi b/ib_async/protobuf/CompletedOrdersEnd_pb2.pyi new file mode 100644 index 00000000..12610460 --- /dev/null +++ b/ib_async/protobuf/CompletedOrdersEnd_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class CompletedOrdersEnd(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/CompletedOrdersRequest_pb2.py b/ib_async/protobuf/CompletedOrdersRequest_pb2.py new file mode 100644 index 00000000..200daa89 --- /dev/null +++ b/ib_async/protobuf/CompletedOrdersRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CompletedOrdersRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CompletedOrdersRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x43ompletedOrdersRequest.proto\x12\x08protobuf\":\n\x16\x43ompletedOrdersRequest\x12\x14\n\x07\x61piOnly\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_apiOnlyBF\n\x16\x63om.ib.client.protobufB\x1b\x43ompletedOrdersRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CompletedOrdersRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\033CompletedOrdersRequestProto\252\002\016IBApi.protobuf' + _globals['_COMPLETEDORDERSREQUEST']._serialized_start=42 + _globals['_COMPLETEDORDERSREQUEST']._serialized_end=100 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CompletedOrdersRequest_pb2.pyi b/ib_async/protobuf/CompletedOrdersRequest_pb2.pyi new file mode 100644 index 00000000..d36fdf80 --- /dev/null +++ b/ib_async/protobuf/CompletedOrdersRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CompletedOrdersRequest(_message.Message): + __slots__ = ("apiOnly",) + APIONLY_FIELD_NUMBER: _ClassVar[int] + apiOnly: bool + def __init__(self, apiOnly: bool = ...) -> None: ... diff --git a/ib_async/protobuf/ContractDataEnd_pb2.py b/ib_async/protobuf/ContractDataEnd_pb2.py new file mode 100644 index 00000000..11f21ec5 --- /dev/null +++ b/ib_async/protobuf/ContractDataEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ContractDataEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ContractDataEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x43ontractDataEnd.proto\x12\x08protobuf\"/\n\x0f\x43ontractDataEnd\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdB?\n\x16\x63om.ib.client.protobufB\x14\x43ontractDataEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ContractDataEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024ContractDataEndProto\252\002\016IBApi.protobuf' + _globals['_CONTRACTDATAEND']._serialized_start=35 + _globals['_CONTRACTDATAEND']._serialized_end=82 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ContractDataEnd_pb2.pyi b/ib_async/protobuf/ContractDataEnd_pb2.pyi new file mode 100644 index 00000000..756aa34a --- /dev/null +++ b/ib_async/protobuf/ContractDataEnd_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ContractDataEnd(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/ContractDataRequest_pb2.py b/ib_async/protobuf/ContractDataRequest_pb2.py new file mode 100644 index 00000000..03745823 --- /dev/null +++ b/ib_async/protobuf/ContractDataRequest_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ContractDataRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ContractDataRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x43ontractDataRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"k\n\x13\x43ontractDataRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractBC\n\x16\x63om.ib.client.protobufB\x18\x43ontractDataRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ContractDataRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030ContractDataRequestProto\252\002\016IBApi.protobuf' + _globals['_CONTRACTDATAREQUEST']._serialized_start=55 + _globals['_CONTRACTDATAREQUEST']._serialized_end=162 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ContractDataRequest_pb2.pyi b/ib_async/protobuf/ContractDataRequest_pb2.pyi new file mode 100644 index 00000000..78c1fa80 --- /dev/null +++ b/ib_async/protobuf/ContractDataRequest_pb2.pyi @@ -0,0 +1,15 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ContractDataRequest(_message.Message): + __slots__ = ("reqId", "contract") + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/ContractData_pb2.py b/ib_async/protobuf/ContractData_pb2.py new file mode 100644 index 00000000..e0ad6527 --- /dev/null +++ b/ib_async/protobuf/ContractData_pb2.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ContractData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ContractData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 +from . import ContractDetails_pb2 as ContractDetails__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x43ontractData.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\x1a\x15\x43ontractDetails.proto\"\xb1\x01\n\x0c\x43ontractData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x37\n\x0f\x63ontractDetails\x18\x03 \x01(\x0b\x32\x19.protobuf.ContractDetailsH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\x12\n\x10_contractDetailsB<\n\x16\x63om.ib.client.protobufB\x11\x43ontractDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ContractData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\021ContractDataProto\252\002\016IBApi.protobuf' + _globals['_CONTRACTDATA']._serialized_start=72 + _globals['_CONTRACTDATA']._serialized_end=249 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ContractData_pb2.pyi b/ib_async/protobuf/ContractData_pb2.pyi new file mode 100644 index 00000000..132b2222 --- /dev/null +++ b/ib_async/protobuf/ContractData_pb2.pyi @@ -0,0 +1,18 @@ +import Contract_pb2 as _Contract_pb2 +import ContractDetails_pb2 as _ContractDetails_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ContractData(_message.Message): + __slots__ = ("reqId", "contract", "contractDetails") + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + CONTRACTDETAILS_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + contractDetails: _ContractDetails_pb2.ContractDetails + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., contractDetails: _Optional[_Union[_ContractDetails_pb2.ContractDetails, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/ContractDescription_pb2.py b/ib_async/protobuf/ContractDescription_pb2.py new file mode 100644 index 00000000..4bd527a2 --- /dev/null +++ b/ib_async/protobuf/ContractDescription_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ContractDescription.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ContractDescription.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x43ontractDescription.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"i\n\x13\x43ontractDescription\x12)\n\x08\x63ontract\x18\x01 \x01(\x0b\x32\x12.protobuf.ContractH\x00\x88\x01\x01\x12\x1a\n\x12\x64\x65rivativeSecTypes\x18\x02 \x03(\tB\x0b\n\t_contractBC\n\x16\x63om.ib.client.protobufB\x18\x43ontractDescriptionProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ContractDescription_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030ContractDescriptionProto\252\002\016IBApi.protobuf' + _globals['_CONTRACTDESCRIPTION']._serialized_start=55 + _globals['_CONTRACTDESCRIPTION']._serialized_end=160 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ContractDescription_pb2.pyi b/ib_async/protobuf/ContractDescription_pb2.pyi new file mode 100644 index 00000000..f0abf288 --- /dev/null +++ b/ib_async/protobuf/ContractDescription_pb2.pyi @@ -0,0 +1,16 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ContractDescription(_message.Message): + __slots__ = ("contract", "derivativeSecTypes") + CONTRACT_FIELD_NUMBER: _ClassVar[int] + DERIVATIVESECTYPES_FIELD_NUMBER: _ClassVar[int] + contract: _Contract_pb2.Contract + derivativeSecTypes: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., derivativeSecTypes: _Optional[_Iterable[str]] = ...) -> None: ... diff --git a/ib_async/protobuf/ContractDetails_pb2.py b/ib_async/protobuf/ContractDetails_pb2.py new file mode 100644 index 00000000..37ab0597 --- /dev/null +++ b/ib_async/protobuf/ContractDetails_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ContractDetails.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ContractDetails.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import IneligibilityReason_pb2 as IneligibilityReason__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x43ontractDetails.proto\x12\x08protobuf\x1a\x19IneligibilityReason.proto\"\xf2\x16\n\x0f\x43ontractDetails\x12\x17\n\nmarketName\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07minTick\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x17\n\norderTypes\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0evalidExchanges\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0epriceMagnifier\x18\x05 \x01(\x05H\x04\x88\x01\x01\x12\x17\n\nunderConId\x18\x06 \x01(\x05H\x05\x88\x01\x01\x12\x15\n\x08longName\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x1a\n\rcontractMonth\x18\x08 \x01(\tH\x07\x88\x01\x01\x12\x15\n\x08industry\x18\t \x01(\tH\x08\x88\x01\x01\x12\x15\n\x08\x63\x61tegory\x18\n \x01(\tH\t\x88\x01\x01\x12\x18\n\x0bsubcategory\x18\x0b \x01(\tH\n\x88\x01\x01\x12\x17\n\ntimeZoneId\x18\x0c \x01(\tH\x0b\x88\x01\x01\x12\x19\n\x0ctradingHours\x18\r \x01(\tH\x0c\x88\x01\x01\x12\x18\n\x0bliquidHours\x18\x0e \x01(\tH\r\x88\x01\x01\x12\x13\n\x06\x65vRule\x18\x0f \x01(\tH\x0e\x88\x01\x01\x12\x19\n\x0c\x65vMultiplier\x18\x10 \x01(\x01H\x0f\x88\x01\x01\x12;\n\tsecIdList\x18\x11 \x03(\x0b\x32(.protobuf.ContractDetails.SecIdListEntry\x12\x15\n\x08\x61ggGroup\x18\x12 \x01(\x05H\x10\x88\x01\x01\x12\x18\n\x0bunderSymbol\x18\x13 \x01(\tH\x11\x88\x01\x01\x12\x19\n\x0cunderSecType\x18\x14 \x01(\tH\x12\x88\x01\x01\x12\x1a\n\rmarketRuleIds\x18\x15 \x01(\tH\x13\x88\x01\x01\x12\x1f\n\x12realExpirationDate\x18\x16 \x01(\tH\x14\x88\x01\x01\x12\x16\n\tstockType\x18\x17 \x01(\tH\x15\x88\x01\x01\x12\x14\n\x07minSize\x18\x18 \x01(\tH\x16\x88\x01\x01\x12\x1a\n\rsizeIncrement\x18\x19 \x01(\tH\x17\x88\x01\x01\x12#\n\x16suggestedSizeIncrement\x18\x1a \x01(\tH\x18\x88\x01\x01\x12\x15\n\x08\x66undName\x18\x1b \x01(\tH\x19\x88\x01\x01\x12\x17\n\nfundFamily\x18\x1c \x01(\tH\x1a\x88\x01\x01\x12\x15\n\x08\x66undType\x18\x1d \x01(\tH\x1b\x88\x01\x01\x12\x1a\n\rfundFrontLoad\x18\x1e \x01(\tH\x1c\x88\x01\x01\x12\x19\n\x0c\x66undBackLoad\x18\x1f \x01(\tH\x1d\x88\x01\x01\x12%\n\x18\x66undBackLoadTimeInterval\x18 \x01(\tH\x1e\x88\x01\x01\x12\x1e\n\x11\x66undManagementFee\x18! \x01(\tH\x1f\x88\x01\x01\x12\x17\n\nfundClosed\x18\" \x01(\x08H \x88\x01\x01\x12&\n\x19\x66undClosedForNewInvestors\x18# \x01(\x08H!\x88\x01\x01\x12\"\n\x15\x66undClosedForNewMoney\x18$ \x01(\x08H\"\x88\x01\x01\x12\x1d\n\x10\x66undNotifyAmount\x18% \x01(\tH#\x88\x01\x01\x12\'\n\x1a\x66undMinimumInitialPurchase\x18& \x01(\tH$\x88\x01\x01\x12*\n\x1d\x66undMinimumSubsequentPurchase\x18\' \x01(\tH%\x88\x01\x01\x12\x1e\n\x11\x66undBlueSkyStates\x18( \x01(\tH&\x88\x01\x01\x12#\n\x16\x66undBlueSkyTerritories\x18) \x01(\tH\'\x88\x01\x01\x12,\n\x1f\x66undDistributionPolicyIndicator\x18* \x01(\tH(\x88\x01\x01\x12\x1a\n\rfundAssetType\x18+ \x01(\tH)\x88\x01\x01\x12\x12\n\x05\x63usip\x18, \x01(\tH*\x88\x01\x01\x12\x16\n\tissueDate\x18- \x01(\tH+\x88\x01\x01\x12\x14\n\x07ratings\x18. \x01(\tH,\x88\x01\x01\x12\x15\n\x08\x62ondType\x18/ \x01(\tH-\x88\x01\x01\x12\x13\n\x06\x63oupon\x18\x30 \x01(\x01H.\x88\x01\x01\x12\x17\n\ncouponType\x18\x31 \x01(\tH/\x88\x01\x01\x12\x18\n\x0b\x63onvertible\x18\x32 \x01(\x08H0\x88\x01\x01\x12\x15\n\x08\x63\x61llable\x18\x33 \x01(\x08H1\x88\x01\x01\x12\x15\n\x08puttable\x18\x34 \x01(\x08H2\x88\x01\x01\x12\x17\n\ndescAppend\x18\x35 \x01(\tH3\x88\x01\x01\x12\x1b\n\x0enextOptionDate\x18\x36 \x01(\tH4\x88\x01\x01\x12\x1b\n\x0enextOptionType\x18\x37 \x01(\tH5\x88\x01\x01\x12\x1e\n\x11nextOptionPartial\x18\x38 \x01(\x08H6\x88\x01\x01\x12\x16\n\tbondNotes\x18\x39 \x01(\tH7\x88\x01\x01\x12>\n\x17ineligibilityReasonList\x18: \x03(\x0b\x32\x1d.protobuf.IneligibilityReason\x12\x1b\n\x0e\x65ventContract1\x18; \x01(\tH8\x88\x01\x01\x12&\n\x19\x65ventContractDescription1\x18< \x01(\tH9\x88\x01\x01\x12&\n\x19\x65ventContractDescription2\x18= \x01(\tH:\x88\x01\x01\x1a\x30\n\x0eSecIdListEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\r\n\x0b_marketNameB\n\n\x08_minTickB\r\n\x0b_orderTypesB\x11\n\x0f_validExchangesB\x11\n\x0f_priceMagnifierB\r\n\x0b_underConIdB\x0b\n\t_longNameB\x10\n\x0e_contractMonthB\x0b\n\t_industryB\x0b\n\t_categoryB\x0e\n\x0c_subcategoryB\r\n\x0b_timeZoneIdB\x0f\n\r_tradingHoursB\x0e\n\x0c_liquidHoursB\t\n\x07_evRuleB\x0f\n\r_evMultiplierB\x0b\n\t_aggGroupB\x0e\n\x0c_underSymbolB\x0f\n\r_underSecTypeB\x10\n\x0e_marketRuleIdsB\x15\n\x13_realExpirationDateB\x0c\n\n_stockTypeB\n\n\x08_minSizeB\x10\n\x0e_sizeIncrementB\x19\n\x17_suggestedSizeIncrementB\x0b\n\t_fundNameB\r\n\x0b_fundFamilyB\x0b\n\t_fundTypeB\x10\n\x0e_fundFrontLoadB\x0f\n\r_fundBackLoadB\x1b\n\x19_fundBackLoadTimeIntervalB\x14\n\x12_fundManagementFeeB\r\n\x0b_fundClosedB\x1c\n\x1a_fundClosedForNewInvestorsB\x18\n\x16_fundClosedForNewMoneyB\x13\n\x11_fundNotifyAmountB\x1d\n\x1b_fundMinimumInitialPurchaseB \n\x1e_fundMinimumSubsequentPurchaseB\x14\n\x12_fundBlueSkyStatesB\x19\n\x17_fundBlueSkyTerritoriesB\"\n _fundDistributionPolicyIndicatorB\x10\n\x0e_fundAssetTypeB\x08\n\x06_cusipB\x0c\n\n_issueDateB\n\n\x08_ratingsB\x0b\n\t_bondTypeB\t\n\x07_couponB\r\n\x0b_couponTypeB\x0e\n\x0c_convertibleB\x0b\n\t_callableB\x0b\n\t_puttableB\r\n\x0b_descAppendB\x11\n\x0f_nextOptionDateB\x11\n\x0f_nextOptionTypeB\x14\n\x12_nextOptionPartialB\x0c\n\n_bondNotesB\x11\n\x0f_eventContract1B\x1c\n\x1a_eventContractDescription1B\x1c\n\x1a_eventContractDescription2B?\n\x16\x63om.ib.client.protobufB\x14\x43ontractDetailsProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ContractDetails_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024ContractDetailsProto\252\002\016IBApi.protobuf' + _globals['_CONTRACTDETAILS_SECIDLISTENTRY']._loaded_options = None + _globals['_CONTRACTDETAILS_SECIDLISTENTRY']._serialized_options = b'8\001' + _globals['_CONTRACTDETAILS']._serialized_start=63 + _globals['_CONTRACTDETAILS']._serialized_end=2993 + _globals['_CONTRACTDETAILS_SECIDLISTENTRY']._serialized_start=1871 + _globals['_CONTRACTDETAILS_SECIDLISTENTRY']._serialized_end=1919 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ContractDetails_pb2.pyi b/ib_async/protobuf/ContractDetails_pb2.pyi new file mode 100644 index 00000000..d7f9b506 --- /dev/null +++ b/ib_async/protobuf/ContractDetails_pb2.pyi @@ -0,0 +1,141 @@ +import IneligibilityReason_pb2 as _IneligibilityReason_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ContractDetails(_message.Message): + __slots__ = ("marketName", "minTick", "orderTypes", "validExchanges", "priceMagnifier", "underConId", "longName", "contractMonth", "industry", "category", "subcategory", "timeZoneId", "tradingHours", "liquidHours", "evRule", "evMultiplier", "secIdList", "aggGroup", "underSymbol", "underSecType", "marketRuleIds", "realExpirationDate", "stockType", "minSize", "sizeIncrement", "suggestedSizeIncrement", "fundName", "fundFamily", "fundType", "fundFrontLoad", "fundBackLoad", "fundBackLoadTimeInterval", "fundManagementFee", "fundClosed", "fundClosedForNewInvestors", "fundClosedForNewMoney", "fundNotifyAmount", "fundMinimumInitialPurchase", "fundMinimumSubsequentPurchase", "fundBlueSkyStates", "fundBlueSkyTerritories", "fundDistributionPolicyIndicator", "fundAssetType", "cusip", "issueDate", "ratings", "bondType", "coupon", "couponType", "convertible", "callable", "puttable", "descAppend", "nextOptionDate", "nextOptionType", "nextOptionPartial", "bondNotes", "ineligibilityReasonList", "eventContract1", "eventContractDescription1", "eventContractDescription2") + class SecIdListEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + MARKETNAME_FIELD_NUMBER: _ClassVar[int] + MINTICK_FIELD_NUMBER: _ClassVar[int] + ORDERTYPES_FIELD_NUMBER: _ClassVar[int] + VALIDEXCHANGES_FIELD_NUMBER: _ClassVar[int] + PRICEMAGNIFIER_FIELD_NUMBER: _ClassVar[int] + UNDERCONID_FIELD_NUMBER: _ClassVar[int] + LONGNAME_FIELD_NUMBER: _ClassVar[int] + CONTRACTMONTH_FIELD_NUMBER: _ClassVar[int] + INDUSTRY_FIELD_NUMBER: _ClassVar[int] + CATEGORY_FIELD_NUMBER: _ClassVar[int] + SUBCATEGORY_FIELD_NUMBER: _ClassVar[int] + TIMEZONEID_FIELD_NUMBER: _ClassVar[int] + TRADINGHOURS_FIELD_NUMBER: _ClassVar[int] + LIQUIDHOURS_FIELD_NUMBER: _ClassVar[int] + EVRULE_FIELD_NUMBER: _ClassVar[int] + EVMULTIPLIER_FIELD_NUMBER: _ClassVar[int] + SECIDLIST_FIELD_NUMBER: _ClassVar[int] + AGGGROUP_FIELD_NUMBER: _ClassVar[int] + UNDERSYMBOL_FIELD_NUMBER: _ClassVar[int] + UNDERSECTYPE_FIELD_NUMBER: _ClassVar[int] + MARKETRULEIDS_FIELD_NUMBER: _ClassVar[int] + REALEXPIRATIONDATE_FIELD_NUMBER: _ClassVar[int] + STOCKTYPE_FIELD_NUMBER: _ClassVar[int] + MINSIZE_FIELD_NUMBER: _ClassVar[int] + SIZEINCREMENT_FIELD_NUMBER: _ClassVar[int] + SUGGESTEDSIZEINCREMENT_FIELD_NUMBER: _ClassVar[int] + FUNDNAME_FIELD_NUMBER: _ClassVar[int] + FUNDFAMILY_FIELD_NUMBER: _ClassVar[int] + FUNDTYPE_FIELD_NUMBER: _ClassVar[int] + FUNDFRONTLOAD_FIELD_NUMBER: _ClassVar[int] + FUNDBACKLOAD_FIELD_NUMBER: _ClassVar[int] + FUNDBACKLOADTIMEINTERVAL_FIELD_NUMBER: _ClassVar[int] + FUNDMANAGEMENTFEE_FIELD_NUMBER: _ClassVar[int] + FUNDCLOSED_FIELD_NUMBER: _ClassVar[int] + FUNDCLOSEDFORNEWINVESTORS_FIELD_NUMBER: _ClassVar[int] + FUNDCLOSEDFORNEWMONEY_FIELD_NUMBER: _ClassVar[int] + FUNDNOTIFYAMOUNT_FIELD_NUMBER: _ClassVar[int] + FUNDMINIMUMINITIALPURCHASE_FIELD_NUMBER: _ClassVar[int] + FUNDMINIMUMSUBSEQUENTPURCHASE_FIELD_NUMBER: _ClassVar[int] + FUNDBLUESKYSTATES_FIELD_NUMBER: _ClassVar[int] + FUNDBLUESKYTERRITORIES_FIELD_NUMBER: _ClassVar[int] + FUNDDISTRIBUTIONPOLICYINDICATOR_FIELD_NUMBER: _ClassVar[int] + FUNDASSETTYPE_FIELD_NUMBER: _ClassVar[int] + CUSIP_FIELD_NUMBER: _ClassVar[int] + ISSUEDATE_FIELD_NUMBER: _ClassVar[int] + RATINGS_FIELD_NUMBER: _ClassVar[int] + BONDTYPE_FIELD_NUMBER: _ClassVar[int] + COUPON_FIELD_NUMBER: _ClassVar[int] + COUPONTYPE_FIELD_NUMBER: _ClassVar[int] + CONVERTIBLE_FIELD_NUMBER: _ClassVar[int] + CALLABLE_FIELD_NUMBER: _ClassVar[int] + PUTTABLE_FIELD_NUMBER: _ClassVar[int] + DESCAPPEND_FIELD_NUMBER: _ClassVar[int] + NEXTOPTIONDATE_FIELD_NUMBER: _ClassVar[int] + NEXTOPTIONTYPE_FIELD_NUMBER: _ClassVar[int] + NEXTOPTIONPARTIAL_FIELD_NUMBER: _ClassVar[int] + BONDNOTES_FIELD_NUMBER: _ClassVar[int] + INELIGIBILITYREASONLIST_FIELD_NUMBER: _ClassVar[int] + EVENTCONTRACT1_FIELD_NUMBER: _ClassVar[int] + EVENTCONTRACTDESCRIPTION1_FIELD_NUMBER: _ClassVar[int] + EVENTCONTRACTDESCRIPTION2_FIELD_NUMBER: _ClassVar[int] + marketName: str + minTick: str + orderTypes: str + validExchanges: str + priceMagnifier: int + underConId: int + longName: str + contractMonth: str + industry: str + category: str + subcategory: str + timeZoneId: str + tradingHours: str + liquidHours: str + evRule: str + evMultiplier: float + secIdList: _containers.ScalarMap[str, str] + aggGroup: int + underSymbol: str + underSecType: str + marketRuleIds: str + realExpirationDate: str + stockType: str + minSize: str + sizeIncrement: str + suggestedSizeIncrement: str + fundName: str + fundFamily: str + fundType: str + fundFrontLoad: str + fundBackLoad: str + fundBackLoadTimeInterval: str + fundManagementFee: str + fundClosed: bool + fundClosedForNewInvestors: bool + fundClosedForNewMoney: bool + fundNotifyAmount: str + fundMinimumInitialPurchase: str + fundMinimumSubsequentPurchase: str + fundBlueSkyStates: str + fundBlueSkyTerritories: str + fundDistributionPolicyIndicator: str + fundAssetType: str + cusip: str + issueDate: str + ratings: str + bondType: str + coupon: float + couponType: str + convertible: bool + callable: bool + puttable: bool + descAppend: str + nextOptionDate: str + nextOptionType: str + nextOptionPartial: bool + bondNotes: str + ineligibilityReasonList: _containers.RepeatedCompositeFieldContainer[_IneligibilityReason_pb2.IneligibilityReason] + eventContract1: str + eventContractDescription1: str + eventContractDescription2: str + def __init__(self, marketName: _Optional[str] = ..., minTick: _Optional[str] = ..., orderTypes: _Optional[str] = ..., validExchanges: _Optional[str] = ..., priceMagnifier: _Optional[int] = ..., underConId: _Optional[int] = ..., longName: _Optional[str] = ..., contractMonth: _Optional[str] = ..., industry: _Optional[str] = ..., category: _Optional[str] = ..., subcategory: _Optional[str] = ..., timeZoneId: _Optional[str] = ..., tradingHours: _Optional[str] = ..., liquidHours: _Optional[str] = ..., evRule: _Optional[str] = ..., evMultiplier: _Optional[float] = ..., secIdList: _Optional[_Mapping[str, str]] = ..., aggGroup: _Optional[int] = ..., underSymbol: _Optional[str] = ..., underSecType: _Optional[str] = ..., marketRuleIds: _Optional[str] = ..., realExpirationDate: _Optional[str] = ..., stockType: _Optional[str] = ..., minSize: _Optional[str] = ..., sizeIncrement: _Optional[str] = ..., suggestedSizeIncrement: _Optional[str] = ..., fundName: _Optional[str] = ..., fundFamily: _Optional[str] = ..., fundType: _Optional[str] = ..., fundFrontLoad: _Optional[str] = ..., fundBackLoad: _Optional[str] = ..., fundBackLoadTimeInterval: _Optional[str] = ..., fundManagementFee: _Optional[str] = ..., fundClosed: bool = ..., fundClosedForNewInvestors: bool = ..., fundClosedForNewMoney: bool = ..., fundNotifyAmount: _Optional[str] = ..., fundMinimumInitialPurchase: _Optional[str] = ..., fundMinimumSubsequentPurchase: _Optional[str] = ..., fundBlueSkyStates: _Optional[str] = ..., fundBlueSkyTerritories: _Optional[str] = ..., fundDistributionPolicyIndicator: _Optional[str] = ..., fundAssetType: _Optional[str] = ..., cusip: _Optional[str] = ..., issueDate: _Optional[str] = ..., ratings: _Optional[str] = ..., bondType: _Optional[str] = ..., coupon: _Optional[float] = ..., couponType: _Optional[str] = ..., convertible: bool = ..., callable: bool = ..., puttable: bool = ..., descAppend: _Optional[str] = ..., nextOptionDate: _Optional[str] = ..., nextOptionType: _Optional[str] = ..., nextOptionPartial: bool = ..., bondNotes: _Optional[str] = ..., ineligibilityReasonList: _Optional[_Iterable[_Union[_IneligibilityReason_pb2.IneligibilityReason, _Mapping]]] = ..., eventContract1: _Optional[str] = ..., eventContractDescription1: _Optional[str] = ..., eventContractDescription2: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/Contract_pb2.py b/ib_async/protobuf/Contract_pb2.py new file mode 100644 index 00000000..62e316a9 --- /dev/null +++ b/ib_async/protobuf/Contract_pb2.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: Contract.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'Contract.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import ComboLeg_pb2 as ComboLeg__pb2 +from . import DeltaNeutralContract_pb2 as DeltaNeutralContract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x43ontract.proto\x12\x08protobuf\x1a\x0e\x43omboLeg.proto\x1a\x1a\x44\x65ltaNeutralContract.proto\"\x8b\x07\n\x08\x43ontract\x12\x12\n\x05\x63onId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x13\n\x06symbol\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07secType\x18\x03 \x01(\tH\x02\x88\x01\x01\x12)\n\x1clastTradeDateOrContractMonth\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06strike\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x12\n\x05right\x18\x06 \x01(\tH\x05\x88\x01\x01\x12\x17\n\nmultiplier\x18\x07 \x01(\x01H\x06\x88\x01\x01\x12\x15\n\x08\x65xchange\x18\x08 \x01(\tH\x07\x88\x01\x01\x12\x18\n\x0bprimaryExch\x18\t \x01(\tH\x08\x88\x01\x01\x12\x15\n\x08\x63urrency\x18\n \x01(\tH\t\x88\x01\x01\x12\x18\n\x0blocalSymbol\x18\x0b \x01(\tH\n\x88\x01\x01\x12\x19\n\x0ctradingClass\x18\x0c \x01(\tH\x0b\x88\x01\x01\x12\x16\n\tsecIdType\x18\r \x01(\tH\x0c\x88\x01\x01\x12\x12\n\x05secId\x18\x0e \x01(\tH\r\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0f \x01(\tH\x0e\x88\x01\x01\x12\x15\n\x08issuerId\x18\x10 \x01(\tH\x0f\x88\x01\x01\x12\x41\n\x14\x64\x65ltaNeutralContract\x18\x11 \x01(\x0b\x32\x1e.protobuf.DeltaNeutralContractH\x10\x88\x01\x01\x12\x1b\n\x0eincludeExpired\x18\x12 \x01(\x08H\x11\x88\x01\x01\x12\x1d\n\x10\x63omboLegsDescrip\x18\x13 \x01(\tH\x12\x88\x01\x01\x12%\n\tcomboLegs\x18\x14 \x03(\x0b\x32\x12.protobuf.ComboLeg\x12\x1a\n\rlastTradeDate\x18\x15 \x01(\tH\x13\x88\x01\x01\x42\x08\n\x06_conIdB\t\n\x07_symbolB\n\n\x08_secTypeB\x1f\n\x1d_lastTradeDateOrContractMonthB\t\n\x07_strikeB\x08\n\x06_rightB\r\n\x0b_multiplierB\x0b\n\t_exchangeB\x0e\n\x0c_primaryExchB\x0b\n\t_currencyB\x0e\n\x0c_localSymbolB\x0f\n\r_tradingClassB\x0c\n\n_secIdTypeB\x08\n\x06_secIdB\x0e\n\x0c_descriptionB\x0b\n\t_issuerIdB\x17\n\x15_deltaNeutralContractB\x11\n\x0f_includeExpiredB\x13\n\x11_comboLegsDescripB\x10\n\x0e_lastTradeDateB8\n\x16\x63om.ib.client.protobufB\rContractProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Contract_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\rContractProto\252\002\016IBApi.protobuf' + _globals['_CONTRACT']._serialized_start=73 + _globals['_CONTRACT']._serialized_end=980 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/Contract_pb2.pyi b/ib_async/protobuf/Contract_pb2.pyi new file mode 100644 index 00000000..12242b2f --- /dev/null +++ b/ib_async/protobuf/Contract_pb2.pyi @@ -0,0 +1,55 @@ +import ComboLeg_pb2 as _ComboLeg_pb2 +import DeltaNeutralContract_pb2 as _DeltaNeutralContract_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class Contract(_message.Message): + __slots__ = ("conId", "symbol", "secType", "lastTradeDateOrContractMonth", "strike", "right", "multiplier", "exchange", "primaryExch", "currency", "localSymbol", "tradingClass", "secIdType", "secId", "description", "issuerId", "deltaNeutralContract", "includeExpired", "comboLegsDescrip", "comboLegs", "lastTradeDate") + CONID_FIELD_NUMBER: _ClassVar[int] + SYMBOL_FIELD_NUMBER: _ClassVar[int] + SECTYPE_FIELD_NUMBER: _ClassVar[int] + LASTTRADEDATEORCONTRACTMONTH_FIELD_NUMBER: _ClassVar[int] + STRIKE_FIELD_NUMBER: _ClassVar[int] + RIGHT_FIELD_NUMBER: _ClassVar[int] + MULTIPLIER_FIELD_NUMBER: _ClassVar[int] + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + PRIMARYEXCH_FIELD_NUMBER: _ClassVar[int] + CURRENCY_FIELD_NUMBER: _ClassVar[int] + LOCALSYMBOL_FIELD_NUMBER: _ClassVar[int] + TRADINGCLASS_FIELD_NUMBER: _ClassVar[int] + SECIDTYPE_FIELD_NUMBER: _ClassVar[int] + SECID_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + ISSUERID_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALCONTRACT_FIELD_NUMBER: _ClassVar[int] + INCLUDEEXPIRED_FIELD_NUMBER: _ClassVar[int] + COMBOLEGSDESCRIP_FIELD_NUMBER: _ClassVar[int] + COMBOLEGS_FIELD_NUMBER: _ClassVar[int] + LASTTRADEDATE_FIELD_NUMBER: _ClassVar[int] + conId: int + symbol: str + secType: str + lastTradeDateOrContractMonth: str + strike: float + right: str + multiplier: float + exchange: str + primaryExch: str + currency: str + localSymbol: str + tradingClass: str + secIdType: str + secId: str + description: str + issuerId: str + deltaNeutralContract: _DeltaNeutralContract_pb2.DeltaNeutralContract + includeExpired: bool + comboLegsDescrip: str + comboLegs: _containers.RepeatedCompositeFieldContainer[_ComboLeg_pb2.ComboLeg] + lastTradeDate: str + def __init__(self, conId: _Optional[int] = ..., symbol: _Optional[str] = ..., secType: _Optional[str] = ..., lastTradeDateOrContractMonth: _Optional[str] = ..., strike: _Optional[float] = ..., right: _Optional[str] = ..., multiplier: _Optional[float] = ..., exchange: _Optional[str] = ..., primaryExch: _Optional[str] = ..., currency: _Optional[str] = ..., localSymbol: _Optional[str] = ..., tradingClass: _Optional[str] = ..., secIdType: _Optional[str] = ..., secId: _Optional[str] = ..., description: _Optional[str] = ..., issuerId: _Optional[str] = ..., deltaNeutralContract: _Optional[_Union[_DeltaNeutralContract_pb2.DeltaNeutralContract, _Mapping]] = ..., includeExpired: bool = ..., comboLegsDescrip: _Optional[str] = ..., comboLegs: _Optional[_Iterable[_Union[_ComboLeg_pb2.ComboLeg, _Mapping]]] = ..., lastTradeDate: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/CurrentTimeInMillisRequest_pb2.py b/ib_async/protobuf/CurrentTimeInMillisRequest_pb2.py new file mode 100644 index 00000000..893d05ac --- /dev/null +++ b/ib_async/protobuf/CurrentTimeInMillisRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CurrentTimeInMillisRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CurrentTimeInMillisRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n CurrentTimeInMillisRequest.proto\x12\x08protobuf\"\x1c\n\x1a\x43urrentTimeInMillisRequestBJ\n\x16\x63om.ib.client.protobufB\x1f\x43urrentTimeInMillisRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CurrentTimeInMillisRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\037CurrentTimeInMillisRequestProto\252\002\016IBApi.protobuf' + _globals['_CURRENTTIMEINMILLISREQUEST']._serialized_start=46 + _globals['_CURRENTTIMEINMILLISREQUEST']._serialized_end=74 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CurrentTimeInMillisRequest_pb2.pyi b/ib_async/protobuf/CurrentTimeInMillisRequest_pb2.pyi new file mode 100644 index 00000000..f4fce35d --- /dev/null +++ b/ib_async/protobuf/CurrentTimeInMillisRequest_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class CurrentTimeInMillisRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/CurrentTimeInMillis_pb2.py b/ib_async/protobuf/CurrentTimeInMillis_pb2.py new file mode 100644 index 00000000..0690e671 --- /dev/null +++ b/ib_async/protobuf/CurrentTimeInMillis_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CurrentTimeInMillis.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CurrentTimeInMillis.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x43urrentTimeInMillis.proto\x12\x08protobuf\"O\n\x13\x43urrentTimeInMillis\x12 \n\x13\x63urrentTimeInMillis\x18\x01 \x01(\x03H\x00\x88\x01\x01\x42\x16\n\x14_currentTimeInMillisBC\n\x16\x63om.ib.client.protobufB\x18\x43urrentTimeInMillisProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CurrentTimeInMillis_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030CurrentTimeInMillisProto\252\002\016IBApi.protobuf' + _globals['_CURRENTTIMEINMILLIS']._serialized_start=39 + _globals['_CURRENTTIMEINMILLIS']._serialized_end=118 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CurrentTimeInMillis_pb2.pyi b/ib_async/protobuf/CurrentTimeInMillis_pb2.pyi new file mode 100644 index 00000000..8c60aaea --- /dev/null +++ b/ib_async/protobuf/CurrentTimeInMillis_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CurrentTimeInMillis(_message.Message): + __slots__ = ("currentTimeInMillis",) + CURRENTTIMEINMILLIS_FIELD_NUMBER: _ClassVar[int] + currentTimeInMillis: int + def __init__(self, currentTimeInMillis: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/CurrentTimeRequest_pb2.py b/ib_async/protobuf/CurrentTimeRequest_pb2.py new file mode 100644 index 00000000..76a18cd1 --- /dev/null +++ b/ib_async/protobuf/CurrentTimeRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CurrentTimeRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CurrentTimeRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x43urrentTimeRequest.proto\x12\x08protobuf\"\x14\n\x12\x43urrentTimeRequestBB\n\x16\x63om.ib.client.protobufB\x17\x43urrentTimeRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CurrentTimeRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027CurrentTimeRequestProto\252\002\016IBApi.protobuf' + _globals['_CURRENTTIMEREQUEST']._serialized_start=38 + _globals['_CURRENTTIMEREQUEST']._serialized_end=58 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CurrentTimeRequest_pb2.pyi b/ib_async/protobuf/CurrentTimeRequest_pb2.pyi new file mode 100644 index 00000000..fe8d7493 --- /dev/null +++ b/ib_async/protobuf/CurrentTimeRequest_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class CurrentTimeRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/CurrentTime_pb2.py b/ib_async/protobuf/CurrentTime_pb2.py new file mode 100644 index 00000000..b02e6d75 --- /dev/null +++ b/ib_async/protobuf/CurrentTime_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: CurrentTime.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'CurrentTime.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x43urrentTime.proto\x12\x08protobuf\"7\n\x0b\x43urrentTime\x12\x18\n\x0b\x63urrentTime\x18\x01 \x01(\x03H\x00\x88\x01\x01\x42\x0e\n\x0c_currentTimeB;\n\x16\x63om.ib.client.protobufB\x10\x43urrentTimeProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'CurrentTime_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020CurrentTimeProto\252\002\016IBApi.protobuf' + _globals['_CURRENTTIME']._serialized_start=31 + _globals['_CURRENTTIME']._serialized_end=86 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/CurrentTime_pb2.pyi b/ib_async/protobuf/CurrentTime_pb2.pyi new file mode 100644 index 00000000..5e86be09 --- /dev/null +++ b/ib_async/protobuf/CurrentTime_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class CurrentTime(_message.Message): + __slots__ = ("currentTime",) + CURRENTTIME_FIELD_NUMBER: _ClassVar[int] + currentTime: int + def __init__(self, currentTime: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/DeltaNeutralContract_pb2.py b/ib_async/protobuf/DeltaNeutralContract_pb2.py new file mode 100644 index 00000000..7c357ade --- /dev/null +++ b/ib_async/protobuf/DeltaNeutralContract_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: DeltaNeutralContract.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'DeltaNeutralContract.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1a\x44\x65ltaNeutralContract.proto\x12\x08protobuf\"p\n\x14\x44\x65ltaNeutralContract\x12\x12\n\x05\x63onId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x12\n\x05\x64\x65lta\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x12\n\x05price\x18\x03 \x01(\x01H\x02\x88\x01\x01\x42\x08\n\x06_conIdB\x08\n\x06_deltaB\x08\n\x06_priceBD\n\x16\x63om.ib.client.protobufB\x19\x44\x65ltaNeutralContractProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DeltaNeutralContract_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031DeltaNeutralContractProto\252\002\016IBApi.protobuf' + _globals['_DELTANEUTRALCONTRACT']._serialized_start=40 + _globals['_DELTANEUTRALCONTRACT']._serialized_end=152 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/DeltaNeutralContract_pb2.pyi b/ib_async/protobuf/DeltaNeutralContract_pb2.pyi new file mode 100644 index 00000000..90de6c54 --- /dev/null +++ b/ib_async/protobuf/DeltaNeutralContract_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class DeltaNeutralContract(_message.Message): + __slots__ = ("conId", "delta", "price") + CONID_FIELD_NUMBER: _ClassVar[int] + DELTA_FIELD_NUMBER: _ClassVar[int] + PRICE_FIELD_NUMBER: _ClassVar[int] + conId: int + delta: float + price: float + def __init__(self, conId: _Optional[int] = ..., delta: _Optional[float] = ..., price: _Optional[float] = ...) -> None: ... diff --git a/ib_async/protobuf/DepthMarketDataDescription_pb2.py b/ib_async/protobuf/DepthMarketDataDescription_pb2.py new file mode 100644 index 00000000..413c454f --- /dev/null +++ b/ib_async/protobuf/DepthMarketDataDescription_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: DepthMarketDataDescription.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'DepthMarketDataDescription.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n DepthMarketDataDescription.proto\x12\x08protobuf\"\xe2\x01\n\x1a\x44\x65pthMarketDataDescription\x12\x15\n\x08\x65xchange\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07secType\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0blistingExch\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1c\n\x0fserviceDataType\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x15\n\x08\x61ggGroup\x18\x05 \x01(\x05H\x04\x88\x01\x01\x42\x0b\n\t_exchangeB\n\n\x08_secTypeB\x0e\n\x0c_listingExchB\x12\n\x10_serviceDataTypeB\x0b\n\t_aggGroupBJ\n\x16\x63om.ib.client.protobufB\x1f\x44\x65pthMarketDataDescriptionProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DepthMarketDataDescription_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\037DepthMarketDataDescriptionProto\252\002\016IBApi.protobuf' + _globals['_DEPTHMARKETDATADESCRIPTION']._serialized_start=47 + _globals['_DEPTHMARKETDATADESCRIPTION']._serialized_end=273 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/DepthMarketDataDescription_pb2.pyi b/ib_async/protobuf/DepthMarketDataDescription_pb2.pyi new file mode 100644 index 00000000..f272103f --- /dev/null +++ b/ib_async/protobuf/DepthMarketDataDescription_pb2.pyi @@ -0,0 +1,19 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class DepthMarketDataDescription(_message.Message): + __slots__ = ("exchange", "secType", "listingExch", "serviceDataType", "aggGroup") + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + SECTYPE_FIELD_NUMBER: _ClassVar[int] + LISTINGEXCH_FIELD_NUMBER: _ClassVar[int] + SERVICEDATATYPE_FIELD_NUMBER: _ClassVar[int] + AGGGROUP_FIELD_NUMBER: _ClassVar[int] + exchange: str + secType: str + listingExch: str + serviceDataType: str + aggGroup: int + def __init__(self, exchange: _Optional[str] = ..., secType: _Optional[str] = ..., listingExch: _Optional[str] = ..., serviceDataType: _Optional[str] = ..., aggGroup: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/DisplayGroupList_pb2.py b/ib_async/protobuf/DisplayGroupList_pb2.py new file mode 100644 index 00000000..1d54f5f9 --- /dev/null +++ b/ib_async/protobuf/DisplayGroupList_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: DisplayGroupList.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'DisplayGroupList.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x44isplayGroupList.proto\x12\x08protobuf\"P\n\x10\x44isplayGroupList\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x13\n\x06groups\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\t\n\x07_groupsB@\n\x16\x63om.ib.client.protobufB\x15\x44isplayGroupListProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DisplayGroupList_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025DisplayGroupListProto\252\002\016IBApi.protobuf' + _globals['_DISPLAYGROUPLIST']._serialized_start=36 + _globals['_DISPLAYGROUPLIST']._serialized_end=116 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/DisplayGroupList_pb2.pyi b/ib_async/protobuf/DisplayGroupList_pb2.pyi new file mode 100644 index 00000000..d6121aef --- /dev/null +++ b/ib_async/protobuf/DisplayGroupList_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class DisplayGroupList(_message.Message): + __slots__ = ("reqId", "groups") + REQID_FIELD_NUMBER: _ClassVar[int] + GROUPS_FIELD_NUMBER: _ClassVar[int] + reqId: int + groups: str + def __init__(self, reqId: _Optional[int] = ..., groups: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/DisplayGroupUpdated_pb2.py b/ib_async/protobuf/DisplayGroupUpdated_pb2.py new file mode 100644 index 00000000..219a21ee --- /dev/null +++ b/ib_async/protobuf/DisplayGroupUpdated_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: DisplayGroupUpdated.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'DisplayGroupUpdated.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x44isplayGroupUpdated.proto\x12\x08protobuf\"_\n\x13\x44isplayGroupUpdated\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x19\n\x0c\x63ontractInfo\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x0f\n\r_contractInfoBC\n\x16\x63om.ib.client.protobufB\x18\x44isplayGroupUpdatedProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DisplayGroupUpdated_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030DisplayGroupUpdatedProto\252\002\016IBApi.protobuf' + _globals['_DISPLAYGROUPUPDATED']._serialized_start=39 + _globals['_DISPLAYGROUPUPDATED']._serialized_end=134 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/DisplayGroupUpdated_pb2.pyi b/ib_async/protobuf/DisplayGroupUpdated_pb2.pyi new file mode 100644 index 00000000..4e214563 --- /dev/null +++ b/ib_async/protobuf/DisplayGroupUpdated_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class DisplayGroupUpdated(_message.Message): + __slots__ = ("reqId", "contractInfo") + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACTINFO_FIELD_NUMBER: _ClassVar[int] + reqId: int + contractInfo: str + def __init__(self, reqId: _Optional[int] = ..., contractInfo: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/ErrorMessage_pb2.py b/ib_async/protobuf/ErrorMessage_pb2.py new file mode 100644 index 00000000..1e823c65 --- /dev/null +++ b/ib_async/protobuf/ErrorMessage_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ErrorMessage.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ErrorMessage.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x45rrorMessage.proto\x12\x08protobuf\"\xd8\x01\n\x0c\x45rrorMessage\x12\x0f\n\x02id\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x16\n\terrorTime\x18\x02 \x01(\x03H\x01\x88\x01\x01\x12\x16\n\terrorCode\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12\x15\n\x08\x65rrorMsg\x18\x04 \x01(\tH\x03\x88\x01\x01\x12$\n\x17\x61\x64vancedOrderRejectJson\x18\x05 \x01(\tH\x04\x88\x01\x01\x42\x05\n\x03_idB\x0c\n\n_errorTimeB\x0c\n\n_errorCodeB\x0b\n\t_errorMsgB\x1a\n\x18_advancedOrderRejectJsonB<\n\x16\x63om.ib.client.protobufB\x11\x45rrorMessageProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ErrorMessage_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\021ErrorMessageProto\252\002\016IBApi.protobuf' + _globals['_ERRORMESSAGE']._serialized_start=33 + _globals['_ERRORMESSAGE']._serialized_end=249 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ErrorMessage_pb2.pyi b/ib_async/protobuf/ErrorMessage_pb2.pyi new file mode 100644 index 00000000..5f778cde --- /dev/null +++ b/ib_async/protobuf/ErrorMessage_pb2.pyi @@ -0,0 +1,19 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ErrorMessage(_message.Message): + __slots__ = ("id", "errorTime", "errorCode", "errorMsg", "advancedOrderRejectJson") + ID_FIELD_NUMBER: _ClassVar[int] + ERRORTIME_FIELD_NUMBER: _ClassVar[int] + ERRORCODE_FIELD_NUMBER: _ClassVar[int] + ERRORMSG_FIELD_NUMBER: _ClassVar[int] + ADVANCEDORDERREJECTJSON_FIELD_NUMBER: _ClassVar[int] + id: int + errorTime: int + errorCode: int + errorMsg: str + advancedOrderRejectJson: str + def __init__(self, id: _Optional[int] = ..., errorTime: _Optional[int] = ..., errorCode: _Optional[int] = ..., errorMsg: _Optional[str] = ..., advancedOrderRejectJson: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/ExecutionDetailsEnd_pb2.py b/ib_async/protobuf/ExecutionDetailsEnd_pb2.py new file mode 100644 index 00000000..9f60e30e --- /dev/null +++ b/ib_async/protobuf/ExecutionDetailsEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ExecutionDetailsEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ExecutionDetailsEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x45xecutionDetailsEnd.proto\x12\x08protobuf\"3\n\x13\x45xecutionDetailsEnd\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBC\n\x16\x63om.ib.client.protobufB\x18\x45xecutionDetailsEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ExecutionDetailsEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030ExecutionDetailsEndProto\252\002\016IBApi.protobuf' + _globals['_EXECUTIONDETAILSEND']._serialized_start=39 + _globals['_EXECUTIONDETAILSEND']._serialized_end=90 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ExecutionDetailsEnd_pb2.pyi b/ib_async/protobuf/ExecutionDetailsEnd_pb2.pyi new file mode 100644 index 00000000..29ff8d0c --- /dev/null +++ b/ib_async/protobuf/ExecutionDetailsEnd_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ExecutionDetailsEnd(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/ExecutionDetails_pb2.py b/ib_async/protobuf/ExecutionDetails_pb2.py new file mode 100644 index 00000000..fbe0b5ed --- /dev/null +++ b/ib_async/protobuf/ExecutionDetails_pb2.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ExecutionDetails.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ExecutionDetails.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 +from . import Execution_pb2 as Execution__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x45xecutionDetails.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\x1a\x0f\x45xecution.proto\"\xa3\x01\n\x10\x45xecutionDetails\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12+\n\texecution\x18\x03 \x01(\x0b\x32\x13.protobuf.ExecutionH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\x0c\n\n_executionB@\n\x16\x63om.ib.client.protobufB\x15\x45xecutionDetailsProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ExecutionDetails_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025ExecutionDetailsProto\252\002\016IBApi.protobuf' + _globals['_EXECUTIONDETAILS']._serialized_start=70 + _globals['_EXECUTIONDETAILS']._serialized_end=233 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ExecutionDetails_pb2.pyi b/ib_async/protobuf/ExecutionDetails_pb2.pyi new file mode 100644 index 00000000..2f5a0659 --- /dev/null +++ b/ib_async/protobuf/ExecutionDetails_pb2.pyi @@ -0,0 +1,18 @@ +import Contract_pb2 as _Contract_pb2 +import Execution_pb2 as _Execution_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ExecutionDetails(_message.Message): + __slots__ = ("reqId", "contract", "execution") + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + EXECUTION_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + execution: _Execution_pb2.Execution + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., execution: _Optional[_Union[_Execution_pb2.Execution, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/ExecutionFilter_pb2.py b/ib_async/protobuf/ExecutionFilter_pb2.py new file mode 100644 index 00000000..200f2ae0 --- /dev/null +++ b/ib_async/protobuf/ExecutionFilter_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ExecutionFilter.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ExecutionFilter.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x45xecutionFilter.proto\x12\x08protobuf\"\xb4\x02\n\x0f\x45xecutionFilter\x12\x15\n\x08\x63lientId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08\x61\x63\x63tCode\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06symbol\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07secType\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x15\n\x08\x65xchange\x18\x06 \x01(\tH\x05\x88\x01\x01\x12\x11\n\x04side\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x16\n\tlastNDays\x18\x08 \x01(\x05H\x07\x88\x01\x01\x12\x15\n\rspecificDates\x18\t \x03(\x05\x42\x0b\n\t_clientIdB\x0b\n\t_acctCodeB\x07\n\x05_timeB\t\n\x07_symbolB\n\n\x08_secTypeB\x0b\n\t_exchangeB\x07\n\x05_sideB\x0c\n\n_lastNDaysB?\n\x16\x63om.ib.client.protobufB\x14\x45xecutionFilterProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ExecutionFilter_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024ExecutionFilterProto\252\002\016IBApi.protobuf' + _globals['_EXECUTIONFILTER']._serialized_start=36 + _globals['_EXECUTIONFILTER']._serialized_end=344 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ExecutionFilter_pb2.pyi b/ib_async/protobuf/ExecutionFilter_pb2.pyi new file mode 100644 index 00000000..de42002c --- /dev/null +++ b/ib_async/protobuf/ExecutionFilter_pb2.pyi @@ -0,0 +1,29 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ExecutionFilter(_message.Message): + __slots__ = ("clientId", "acctCode", "time", "symbol", "secType", "exchange", "side", "lastNDays", "specificDates") + CLIENTID_FIELD_NUMBER: _ClassVar[int] + ACCTCODE_FIELD_NUMBER: _ClassVar[int] + TIME_FIELD_NUMBER: _ClassVar[int] + SYMBOL_FIELD_NUMBER: _ClassVar[int] + SECTYPE_FIELD_NUMBER: _ClassVar[int] + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + SIDE_FIELD_NUMBER: _ClassVar[int] + LASTNDAYS_FIELD_NUMBER: _ClassVar[int] + SPECIFICDATES_FIELD_NUMBER: _ClassVar[int] + clientId: int + acctCode: str + time: str + symbol: str + secType: str + exchange: str + side: str + lastNDays: int + specificDates: _containers.RepeatedScalarFieldContainer[int] + def __init__(self, clientId: _Optional[int] = ..., acctCode: _Optional[str] = ..., time: _Optional[str] = ..., symbol: _Optional[str] = ..., secType: _Optional[str] = ..., exchange: _Optional[str] = ..., side: _Optional[str] = ..., lastNDays: _Optional[int] = ..., specificDates: _Optional[_Iterable[int]] = ...) -> None: ... diff --git a/ib_async/protobuf/ExecutionRequest_pb2.py b/ib_async/protobuf/ExecutionRequest_pb2.py new file mode 100644 index 00000000..2d0f4cf9 --- /dev/null +++ b/ib_async/protobuf/ExecutionRequest_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ExecutionRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ExecutionRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import ExecutionFilter_pb2 as ExecutionFilter__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x45xecutionRequest.proto\x12\x08protobuf\x1a\x15\x45xecutionFilter.proto\"}\n\x10\x45xecutionRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x37\n\x0f\x65xecutionFilter\x18\x02 \x01(\x0b\x32\x19.protobuf.ExecutionFilterH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x12\n\x10_executionFilterB@\n\x16\x63om.ib.client.protobufB\x15\x45xecutionRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ExecutionRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025ExecutionRequestProto\252\002\016IBApi.protobuf' + _globals['_EXECUTIONREQUEST']._serialized_start=59 + _globals['_EXECUTIONREQUEST']._serialized_end=184 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ExecutionRequest_pb2.pyi b/ib_async/protobuf/ExecutionRequest_pb2.pyi new file mode 100644 index 00000000..304fda83 --- /dev/null +++ b/ib_async/protobuf/ExecutionRequest_pb2.pyi @@ -0,0 +1,15 @@ +import ExecutionFilter_pb2 as _ExecutionFilter_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ExecutionRequest(_message.Message): + __slots__ = ("reqId", "executionFilter") + REQID_FIELD_NUMBER: _ClassVar[int] + EXECUTIONFILTER_FIELD_NUMBER: _ClassVar[int] + reqId: int + executionFilter: _ExecutionFilter_pb2.ExecutionFilter + def __init__(self, reqId: _Optional[int] = ..., executionFilter: _Optional[_Union[_ExecutionFilter_pb2.ExecutionFilter, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/Execution_pb2.py b/ib_async/protobuf/Execution_pb2.py new file mode 100644 index 00000000..2a2b2e81 --- /dev/null +++ b/ib_async/protobuf/Execution_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: Execution.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'Execution.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0f\x45xecution.proto\x12\x08protobuf\"\xaf\x06\n\tExecution\x12\x14\n\x07orderId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x13\n\x06\x65xecId\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nacctNumber\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x15\n\x08\x65xchange\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x11\n\x04side\x18\x06 \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06shares\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x12\n\x05price\x18\x08 \x01(\x01H\x07\x88\x01\x01\x12\x13\n\x06permId\x18\t \x01(\x03H\x08\x88\x01\x01\x12\x15\n\x08\x63lientId\x18\n \x01(\x05H\t\x88\x01\x01\x12\x1a\n\risLiquidation\x18\x0b \x01(\x08H\n\x88\x01\x01\x12\x13\n\x06\x63umQty\x18\x0c \x01(\tH\x0b\x88\x01\x01\x12\x15\n\x08\x61vgPrice\x18\r \x01(\x01H\x0c\x88\x01\x01\x12\x15\n\x08orderRef\x18\x0e \x01(\tH\r\x88\x01\x01\x12\x13\n\x06\x65vRule\x18\x0f \x01(\tH\x0e\x88\x01\x01\x12\x19\n\x0c\x65vMultiplier\x18\x10 \x01(\x01H\x0f\x88\x01\x01\x12\x16\n\tmodelCode\x18\x11 \x01(\tH\x10\x88\x01\x01\x12\x1a\n\rlastLiquidity\x18\x12 \x01(\x05H\x11\x88\x01\x01\x12#\n\x16isPriceRevisionPending\x18\x13 \x01(\x08H\x12\x88\x01\x01\x12\x16\n\tsubmitter\x18\x14 \x01(\tH\x13\x88\x01\x01\x12#\n\x16optExerciseOrLapseType\x18\x15 \x01(\x05H\x14\x88\x01\x01\x42\n\n\x08_orderIdB\t\n\x07_execIdB\x07\n\x05_timeB\r\n\x0b_acctNumberB\x0b\n\t_exchangeB\x07\n\x05_sideB\t\n\x07_sharesB\x08\n\x06_priceB\t\n\x07_permIdB\x0b\n\t_clientIdB\x10\n\x0e_isLiquidationB\t\n\x07_cumQtyB\x0b\n\t_avgPriceB\x0b\n\t_orderRefB\t\n\x07_evRuleB\x0f\n\r_evMultiplierB\x0c\n\n_modelCodeB\x10\n\x0e_lastLiquidityB\x19\n\x17_isPriceRevisionPendingB\x0c\n\n_submitterB\x19\n\x17_optExerciseOrLapseTypeB9\n\x16\x63om.ib.client.protobufB\x0e\x45xecutionProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Execution_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\016ExecutionProto\252\002\016IBApi.protobuf' + _globals['_EXECUTION']._serialized_start=30 + _globals['_EXECUTION']._serialized_end=845 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/Execution_pb2.pyi b/ib_async/protobuf/Execution_pb2.pyi new file mode 100644 index 00000000..82bcd079 --- /dev/null +++ b/ib_async/protobuf/Execution_pb2.pyi @@ -0,0 +1,51 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class Execution(_message.Message): + __slots__ = ("orderId", "execId", "time", "acctNumber", "exchange", "side", "shares", "price", "permId", "clientId", "isLiquidation", "cumQty", "avgPrice", "orderRef", "evRule", "evMultiplier", "modelCode", "lastLiquidity", "isPriceRevisionPending", "submitter", "optExerciseOrLapseType") + ORDERID_FIELD_NUMBER: _ClassVar[int] + EXECID_FIELD_NUMBER: _ClassVar[int] + TIME_FIELD_NUMBER: _ClassVar[int] + ACCTNUMBER_FIELD_NUMBER: _ClassVar[int] + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + SIDE_FIELD_NUMBER: _ClassVar[int] + SHARES_FIELD_NUMBER: _ClassVar[int] + PRICE_FIELD_NUMBER: _ClassVar[int] + PERMID_FIELD_NUMBER: _ClassVar[int] + CLIENTID_FIELD_NUMBER: _ClassVar[int] + ISLIQUIDATION_FIELD_NUMBER: _ClassVar[int] + CUMQTY_FIELD_NUMBER: _ClassVar[int] + AVGPRICE_FIELD_NUMBER: _ClassVar[int] + ORDERREF_FIELD_NUMBER: _ClassVar[int] + EVRULE_FIELD_NUMBER: _ClassVar[int] + EVMULTIPLIER_FIELD_NUMBER: _ClassVar[int] + MODELCODE_FIELD_NUMBER: _ClassVar[int] + LASTLIQUIDITY_FIELD_NUMBER: _ClassVar[int] + ISPRICEREVISIONPENDING_FIELD_NUMBER: _ClassVar[int] + SUBMITTER_FIELD_NUMBER: _ClassVar[int] + OPTEXERCISEORLAPSETYPE_FIELD_NUMBER: _ClassVar[int] + orderId: int + execId: str + time: str + acctNumber: str + exchange: str + side: str + shares: str + price: float + permId: int + clientId: int + isLiquidation: bool + cumQty: str + avgPrice: float + orderRef: str + evRule: str + evMultiplier: float + modelCode: str + lastLiquidity: int + isPriceRevisionPending: bool + submitter: str + optExerciseOrLapseType: int + def __init__(self, orderId: _Optional[int] = ..., execId: _Optional[str] = ..., time: _Optional[str] = ..., acctNumber: _Optional[str] = ..., exchange: _Optional[str] = ..., side: _Optional[str] = ..., shares: _Optional[str] = ..., price: _Optional[float] = ..., permId: _Optional[int] = ..., clientId: _Optional[int] = ..., isLiquidation: bool = ..., cumQty: _Optional[str] = ..., avgPrice: _Optional[float] = ..., orderRef: _Optional[str] = ..., evRule: _Optional[str] = ..., evMultiplier: _Optional[float] = ..., modelCode: _Optional[str] = ..., lastLiquidity: _Optional[int] = ..., isPriceRevisionPending: bool = ..., submitter: _Optional[str] = ..., optExerciseOrLapseType: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/ExerciseOptionsRequest_pb2.py b/ib_async/protobuf/ExerciseOptionsRequest_pb2.py new file mode 100644 index 00000000..afa22da5 --- /dev/null +++ b/ib_async/protobuf/ExerciseOptionsRequest_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ExerciseOptionsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ExerciseOptionsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x45xerciseOptionsRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xbc\x03\n\x16\x45xerciseOptionsRequest\x12\x14\n\x07orderId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x1b\n\x0e\x65xerciseAction\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12\x1d\n\x10\x65xerciseQuantity\x18\x04 \x01(\x05H\x03\x88\x01\x01\x12\x14\n\x07\x61\x63\x63ount\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x15\n\x08override\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x1c\n\x0fmanualOrderTime\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x0f\x63ustomerAccount\x18\x08 \x01(\tH\x07\x88\x01\x01\x12!\n\x14professionalCustomer\x18\t \x01(\x08H\x08\x88\x01\x01\x42\n\n\x08_orderIdB\x0b\n\t_contractB\x11\n\x0f_exerciseActionB\x13\n\x11_exerciseQuantityB\n\n\x08_accountB\x0b\n\t_overrideB\x12\n\x10_manualOrderTimeB\x12\n\x10_customerAccountB\x17\n\x15_professionalCustomerBF\n\x16\x63om.ib.client.protobufB\x1b\x45xerciseOptionsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ExerciseOptionsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\033ExerciseOptionsRequestProto\252\002\016IBApi.protobuf' + _globals['_EXERCISEOPTIONSREQUEST']._serialized_start=59 + _globals['_EXERCISEOPTIONSREQUEST']._serialized_end=503 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ExerciseOptionsRequest_pb2.pyi b/ib_async/protobuf/ExerciseOptionsRequest_pb2.pyi new file mode 100644 index 00000000..72bea2f5 --- /dev/null +++ b/ib_async/protobuf/ExerciseOptionsRequest_pb2.pyi @@ -0,0 +1,29 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ExerciseOptionsRequest(_message.Message): + __slots__ = ("orderId", "contract", "exerciseAction", "exerciseQuantity", "account", "override", "manualOrderTime", "customerAccount", "professionalCustomer") + ORDERID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + EXERCISEACTION_FIELD_NUMBER: _ClassVar[int] + EXERCISEQUANTITY_FIELD_NUMBER: _ClassVar[int] + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + OVERRIDE_FIELD_NUMBER: _ClassVar[int] + MANUALORDERTIME_FIELD_NUMBER: _ClassVar[int] + CUSTOMERACCOUNT_FIELD_NUMBER: _ClassVar[int] + PROFESSIONALCUSTOMER_FIELD_NUMBER: _ClassVar[int] + orderId: int + contract: _Contract_pb2.Contract + exerciseAction: int + exerciseQuantity: int + account: str + override: bool + manualOrderTime: str + customerAccount: str + professionalCustomer: bool + def __init__(self, orderId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., exerciseAction: _Optional[int] = ..., exerciseQuantity: _Optional[int] = ..., account: _Optional[str] = ..., override: bool = ..., manualOrderTime: _Optional[str] = ..., customerAccount: _Optional[str] = ..., professionalCustomer: bool = ...) -> None: ... diff --git a/ib_async/protobuf/FAReplace_pb2.py b/ib_async/protobuf/FAReplace_pb2.py new file mode 100644 index 00000000..746be4d7 --- /dev/null +++ b/ib_async/protobuf/FAReplace_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: FAReplace.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'FAReplace.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0f\x46\x41Replace.proto\x12\x08protobuf\"k\n\tFAReplace\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x17\n\nfaDataType\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x10\n\x03xml\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\r\n\x0b_faDataTypeB\x06\n\x04_xmlB9\n\x16\x63om.ib.client.protobufB\x0e\x46\x41ReplaceProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'FAReplace_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\016FAReplaceProto\252\002\016IBApi.protobuf' + _globals['_FAREPLACE']._serialized_start=29 + _globals['_FAREPLACE']._serialized_end=136 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/FAReplace_pb2.pyi b/ib_async/protobuf/FAReplace_pb2.pyi new file mode 100644 index 00000000..ff848c42 --- /dev/null +++ b/ib_async/protobuf/FAReplace_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class FAReplace(_message.Message): + __slots__ = ("reqId", "faDataType", "xml") + REQID_FIELD_NUMBER: _ClassVar[int] + FADATATYPE_FIELD_NUMBER: _ClassVar[int] + XML_FIELD_NUMBER: _ClassVar[int] + reqId: int + faDataType: int + xml: str + def __init__(self, reqId: _Optional[int] = ..., faDataType: _Optional[int] = ..., xml: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/FARequest_pb2.py b/ib_async/protobuf/FARequest_pb2.py new file mode 100644 index 00000000..eaeaef98 --- /dev/null +++ b/ib_async/protobuf/FARequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: FARequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'FARequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0f\x46\x41Request.proto\x12\x08protobuf\"3\n\tFARequest\x12\x17\n\nfaDataType\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\r\n\x0b_faDataTypeB9\n\x16\x63om.ib.client.protobufB\x0e\x46\x41RequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'FARequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\016FARequestProto\252\002\016IBApi.protobuf' + _globals['_FAREQUEST']._serialized_start=29 + _globals['_FAREQUEST']._serialized_end=80 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/FARequest_pb2.pyi b/ib_async/protobuf/FARequest_pb2.pyi new file mode 100644 index 00000000..e2ecc736 --- /dev/null +++ b/ib_async/protobuf/FARequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class FARequest(_message.Message): + __slots__ = ("faDataType",) + FADATATYPE_FIELD_NUMBER: _ClassVar[int] + faDataType: int + def __init__(self, faDataType: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/FamilyCode_pb2.py b/ib_async/protobuf/FamilyCode_pb2.py new file mode 100644 index 00000000..437bec5e --- /dev/null +++ b/ib_async/protobuf/FamilyCode_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: FamilyCode.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'FamilyCode.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x46\x61milyCode.proto\x12\x08protobuf\"Z\n\nFamilyCode\x12\x16\n\taccountId\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nfamilyCode\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x0c\n\n_accountIdB\r\n\x0b_familyCodeB:\n\x16\x63om.ib.client.protobufB\x0f\x46\x61milyCodeProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'FamilyCode_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\017FamilyCodeProto\252\002\016IBApi.protobuf' + _globals['_FAMILYCODE']._serialized_start=30 + _globals['_FAMILYCODE']._serialized_end=120 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/FamilyCode_pb2.pyi b/ib_async/protobuf/FamilyCode_pb2.pyi new file mode 100644 index 00000000..86c0e8ec --- /dev/null +++ b/ib_async/protobuf/FamilyCode_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class FamilyCode(_message.Message): + __slots__ = ("accountId", "familyCode") + ACCOUNTID_FIELD_NUMBER: _ClassVar[int] + FAMILYCODE_FIELD_NUMBER: _ClassVar[int] + accountId: str + familyCode: str + def __init__(self, accountId: _Optional[str] = ..., familyCode: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/FamilyCodesRequest_pb2.py b/ib_async/protobuf/FamilyCodesRequest_pb2.py new file mode 100644 index 00000000..be8c5241 --- /dev/null +++ b/ib_async/protobuf/FamilyCodesRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: FamilyCodesRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'FamilyCodesRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x46\x61milyCodesRequest.proto\x12\x08protobuf\"\x14\n\x12\x46\x61milyCodesRequestBB\n\x16\x63om.ib.client.protobufB\x17\x46\x61milyCodesRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'FamilyCodesRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027FamilyCodesRequestProto\252\002\016IBApi.protobuf' + _globals['_FAMILYCODESREQUEST']._serialized_start=38 + _globals['_FAMILYCODESREQUEST']._serialized_end=58 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/FamilyCodesRequest_pb2.pyi b/ib_async/protobuf/FamilyCodesRequest_pb2.pyi new file mode 100644 index 00000000..d339c331 --- /dev/null +++ b/ib_async/protobuf/FamilyCodesRequest_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class FamilyCodesRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/FamilyCodes_pb2.py b/ib_async/protobuf/FamilyCodes_pb2.py new file mode 100644 index 00000000..e696e4c6 --- /dev/null +++ b/ib_async/protobuf/FamilyCodes_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: FamilyCodes.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'FamilyCodes.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import FamilyCode_pb2 as FamilyCode__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x46\x61milyCodes.proto\x12\x08protobuf\x1a\x10\x46\x61milyCode.proto\"8\n\x0b\x46\x61milyCodes\x12)\n\x0b\x66\x61milyCodes\x18\x01 \x03(\x0b\x32\x14.protobuf.FamilyCodeB;\n\x16\x63om.ib.client.protobufB\x10\x46\x61milyCodesProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'FamilyCodes_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020FamilyCodesProto\252\002\016IBApi.protobuf' + _globals['_FAMILYCODES']._serialized_start=49 + _globals['_FAMILYCODES']._serialized_end=105 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/FamilyCodes_pb2.pyi b/ib_async/protobuf/FamilyCodes_pb2.pyi new file mode 100644 index 00000000..f5124c48 --- /dev/null +++ b/ib_async/protobuf/FamilyCodes_pb2.pyi @@ -0,0 +1,14 @@ +import FamilyCode_pb2 as _FamilyCode_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class FamilyCodes(_message.Message): + __slots__ = ("familyCodes",) + FAMILYCODES_FIELD_NUMBER: _ClassVar[int] + familyCodes: _containers.RepeatedCompositeFieldContainer[_FamilyCode_pb2.FamilyCode] + def __init__(self, familyCodes: _Optional[_Iterable[_Union[_FamilyCode_pb2.FamilyCode, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/FundamentalsDataRequest_pb2.py b/ib_async/protobuf/FundamentalsDataRequest_pb2.py new file mode 100644 index 00000000..a9895677 --- /dev/null +++ b/ib_async/protobuf/FundamentalsDataRequest_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: FundamentalsDataRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'FundamentalsDataRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x46undamentalsDataRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xb8\x02\n\x17\x46undamentalsDataRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x17\n\nreportType\x18\x03 \x01(\tH\x02\x88\x01\x01\x12_\n\x17\x66undamentalsDataOptions\x18\x04 \x03(\x0b\x32>.protobuf.FundamentalsDataRequest.FundamentalsDataOptionsEntry\x1a>\n\x1c\x46undamentalsDataOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\r\n\x0b_reportTypeBG\n\x16\x63om.ib.client.protobufB\x1c\x46undamentalsDataRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'FundamentalsDataRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\034FundamentalsDataRequestProto\252\002\016IBApi.protobuf' + _globals['_FUNDAMENTALSDATAREQUEST_FUNDAMENTALSDATAOPTIONSENTRY']._loaded_options = None + _globals['_FUNDAMENTALSDATAREQUEST_FUNDAMENTALSDATAOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_FUNDAMENTALSDATAREQUEST']._serialized_start=60 + _globals['_FUNDAMENTALSDATAREQUEST']._serialized_end=372 + _globals['_FUNDAMENTALSDATAREQUEST_FUNDAMENTALSDATAOPTIONSENTRY']._serialized_start=272 + _globals['_FUNDAMENTALSDATAREQUEST_FUNDAMENTALSDATAOPTIONSENTRY']._serialized_end=334 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/FundamentalsDataRequest_pb2.pyi b/ib_async/protobuf/FundamentalsDataRequest_pb2.pyi new file mode 100644 index 00000000..f8116e54 --- /dev/null +++ b/ib_async/protobuf/FundamentalsDataRequest_pb2.pyi @@ -0,0 +1,27 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class FundamentalsDataRequest(_message.Message): + __slots__ = ("reqId", "contract", "reportType", "fundamentalsDataOptions") + class FundamentalsDataOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + REPORTTYPE_FIELD_NUMBER: _ClassVar[int] + FUNDAMENTALSDATAOPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + reportType: str + fundamentalsDataOptions: _containers.ScalarMap[str, str] + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., reportType: _Optional[str] = ..., fundamentalsDataOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/FundamentalsData_pb2.py b/ib_async/protobuf/FundamentalsData_pb2.py new file mode 100644 index 00000000..bd99f221 --- /dev/null +++ b/ib_async/protobuf/FundamentalsData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: FundamentalsData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'FundamentalsData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x46undamentalsData.proto\x12\x08protobuf\"L\n\x10\x46undamentalsData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x07\n\x05_dataB@\n\x16\x63om.ib.client.protobufB\x15\x46undamentalsDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'FundamentalsData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025FundamentalsDataProto\252\002\016IBApi.protobuf' + _globals['_FUNDAMENTALSDATA']._serialized_start=36 + _globals['_FUNDAMENTALSDATA']._serialized_end=112 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/FundamentalsData_pb2.pyi b/ib_async/protobuf/FundamentalsData_pb2.pyi new file mode 100644 index 00000000..24a0c09c --- /dev/null +++ b/ib_async/protobuf/FundamentalsData_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class FundamentalsData(_message.Message): + __slots__ = ("reqId", "data") + REQID_FIELD_NUMBER: _ClassVar[int] + DATA_FIELD_NUMBER: _ClassVar[int] + reqId: int + data: str + def __init__(self, reqId: _Optional[int] = ..., data: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/GlobalCancelRequest_pb2.py b/ib_async/protobuf/GlobalCancelRequest_pb2.py new file mode 100644 index 00000000..8e6993a4 --- /dev/null +++ b/ib_async/protobuf/GlobalCancelRequest_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: GlobalCancelRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'GlobalCancelRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import OrderCancel_pb2 as OrderCancel__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19GlobalCancelRequest.proto\x12\x08protobuf\x1a\x11OrderCancel.proto\"V\n\x13GlobalCancelRequest\x12/\n\x0borderCancel\x18\x01 \x01(\x0b\x32\x15.protobuf.OrderCancelH\x00\x88\x01\x01\x42\x0e\n\x0c_orderCancelBC\n\x16\x63om.ib.client.protobufB\x18GlobalCancelRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'GlobalCancelRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030GlobalCancelRequestProto\252\002\016IBApi.protobuf' + _globals['_GLOBALCANCELREQUEST']._serialized_start=58 + _globals['_GLOBALCANCELREQUEST']._serialized_end=144 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/GlobalCancelRequest_pb2.pyi b/ib_async/protobuf/GlobalCancelRequest_pb2.pyi new file mode 100644 index 00000000..fa6cad66 --- /dev/null +++ b/ib_async/protobuf/GlobalCancelRequest_pb2.pyi @@ -0,0 +1,13 @@ +import OrderCancel_pb2 as _OrderCancel_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class GlobalCancelRequest(_message.Message): + __slots__ = ("orderCancel",) + ORDERCANCEL_FIELD_NUMBER: _ClassVar[int] + orderCancel: _OrderCancel_pb2.OrderCancel + def __init__(self, orderCancel: _Optional[_Union[_OrderCancel_pb2.OrderCancel, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/HeadTimestampRequest_pb2.py b/ib_async/protobuf/HeadTimestampRequest_pb2.py new file mode 100644 index 00000000..d0321411 --- /dev/null +++ b/ib_async/protobuf/HeadTimestampRequest_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HeadTimestampRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HeadTimestampRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aHeadTimestampRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xdc\x01\n\x14HeadTimestampRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x13\n\x06useRTH\x18\x03 \x01(\x08H\x02\x88\x01\x01\x12\x17\n\nwhatToShow\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nformatDate\x18\x05 \x01(\x05H\x04\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\t\n\x07_useRTHB\r\n\x0b_whatToShowB\r\n\x0b_formatDateBD\n\x16\x63om.ib.client.protobufB\x19HeadTimestampRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HeadTimestampRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031HeadTimestampRequestProto\252\002\016IBApi.protobuf' + _globals['_HEADTIMESTAMPREQUEST']._serialized_start=57 + _globals['_HEADTIMESTAMPREQUEST']._serialized_end=277 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HeadTimestampRequest_pb2.pyi b/ib_async/protobuf/HeadTimestampRequest_pb2.pyi new file mode 100644 index 00000000..b5cdf0e2 --- /dev/null +++ b/ib_async/protobuf/HeadTimestampRequest_pb2.pyi @@ -0,0 +1,21 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HeadTimestampRequest(_message.Message): + __slots__ = ("reqId", "contract", "useRTH", "whatToShow", "formatDate") + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + USERTH_FIELD_NUMBER: _ClassVar[int] + WHATTOSHOW_FIELD_NUMBER: _ClassVar[int] + FORMATDATE_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + useRTH: bool + whatToShow: str + formatDate: int + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., useRTH: bool = ..., whatToShow: _Optional[str] = ..., formatDate: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/HeadTimestamp_pb2.py b/ib_async/protobuf/HeadTimestamp_pb2.py new file mode 100644 index 00000000..7febf087 --- /dev/null +++ b/ib_async/protobuf/HeadTimestamp_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HeadTimestamp.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HeadTimestamp.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13HeadTimestamp.proto\x12\x08protobuf\"[\n\rHeadTimestamp\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x1a\n\rheadTimestamp\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x10\n\x0e_headTimestampB=\n\x16\x63om.ib.client.protobufB\x12HeadTimestampProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HeadTimestamp_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\022HeadTimestampProto\252\002\016IBApi.protobuf' + _globals['_HEADTIMESTAMP']._serialized_start=33 + _globals['_HEADTIMESTAMP']._serialized_end=124 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HeadTimestamp_pb2.pyi b/ib_async/protobuf/HeadTimestamp_pb2.pyi new file mode 100644 index 00000000..565cc73b --- /dev/null +++ b/ib_async/protobuf/HeadTimestamp_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class HeadTimestamp(_message.Message): + __slots__ = ("reqId", "headTimestamp") + REQID_FIELD_NUMBER: _ClassVar[int] + HEADTIMESTAMP_FIELD_NUMBER: _ClassVar[int] + reqId: int + headTimestamp: str + def __init__(self, reqId: _Optional[int] = ..., headTimestamp: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/HistogramDataEntry_pb2.py b/ib_async/protobuf/HistogramDataEntry_pb2.py new file mode 100644 index 00000000..c8316918 --- /dev/null +++ b/ib_async/protobuf/HistogramDataEntry_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistogramDataEntry.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistogramDataEntry.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18HistogramDataEntry.proto\x12\x08protobuf\"N\n\x12HistogramDataEntry\x12\x12\n\x05price\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12\x11\n\x04size\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_priceB\x07\n\x05_sizeBB\n\x16\x63om.ib.client.protobufB\x17HistogramDataEntryProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistogramDataEntry_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027HistogramDataEntryProto\252\002\016IBApi.protobuf' + _globals['_HISTOGRAMDATAENTRY']._serialized_start=38 + _globals['_HISTOGRAMDATAENTRY']._serialized_end=116 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistogramDataEntry_pb2.pyi b/ib_async/protobuf/HistogramDataEntry_pb2.pyi new file mode 100644 index 00000000..aaf36d51 --- /dev/null +++ b/ib_async/protobuf/HistogramDataEntry_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistogramDataEntry(_message.Message): + __slots__ = ("price", "size") + PRICE_FIELD_NUMBER: _ClassVar[int] + SIZE_FIELD_NUMBER: _ClassVar[int] + price: float + size: str + def __init__(self, price: _Optional[float] = ..., size: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/HistogramDataRequest_pb2.py b/ib_async/protobuf/HistogramDataRequest_pb2.py new file mode 100644 index 00000000..f1d51378 --- /dev/null +++ b/ib_async/protobuf/HistogramDataRequest_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistogramDataRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistogramDataRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aHistogramDataRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xb4\x01\n\x14HistogramDataRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x13\n\x06useRTH\x18\x03 \x01(\x08H\x02\x88\x01\x01\x12\x17\n\ntimePeriod\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\t\n\x07_useRTHB\r\n\x0b_timePeriodBD\n\x16\x63om.ib.client.protobufB\x19HistogramDataRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistogramDataRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031HistogramDataRequestProto\252\002\016IBApi.protobuf' + _globals['_HISTOGRAMDATAREQUEST']._serialized_start=57 + _globals['_HISTOGRAMDATAREQUEST']._serialized_end=237 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistogramDataRequest_pb2.pyi b/ib_async/protobuf/HistogramDataRequest_pb2.pyi new file mode 100644 index 00000000..fdbf23d9 --- /dev/null +++ b/ib_async/protobuf/HistogramDataRequest_pb2.pyi @@ -0,0 +1,19 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistogramDataRequest(_message.Message): + __slots__ = ("reqId", "contract", "useRTH", "timePeriod") + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + USERTH_FIELD_NUMBER: _ClassVar[int] + TIMEPERIOD_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + useRTH: bool + timePeriod: str + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., useRTH: bool = ..., timePeriod: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/HistogramData_pb2.py b/ib_async/protobuf/HistogramData_pb2.py new file mode 100644 index 00000000..75fe7ced --- /dev/null +++ b/ib_async/protobuf/HistogramData_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistogramData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistogramData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import HistogramDataEntry_pb2 as HistogramDataEntry__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13HistogramData.proto\x12\x08protobuf\x1a\x18HistogramDataEntry.proto\"i\n\rHistogramData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12:\n\x14histogramDataEntries\x18\x02 \x03(\x0b\x32\x1c.protobuf.HistogramDataEntryB\x08\n\x06_reqIdB=\n\x16\x63om.ib.client.protobufB\x12HistogramDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistogramData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\022HistogramDataProto\252\002\016IBApi.protobuf' + _globals['_HISTOGRAMDATA']._serialized_start=59 + _globals['_HISTOGRAMDATA']._serialized_end=164 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistogramData_pb2.pyi b/ib_async/protobuf/HistogramData_pb2.pyi new file mode 100644 index 00000000..0de6a7ee --- /dev/null +++ b/ib_async/protobuf/HistogramData_pb2.pyi @@ -0,0 +1,16 @@ +import HistogramDataEntry_pb2 as _HistogramDataEntry_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistogramData(_message.Message): + __slots__ = ("reqId", "histogramDataEntries") + REQID_FIELD_NUMBER: _ClassVar[int] + HISTOGRAMDATAENTRIES_FIELD_NUMBER: _ClassVar[int] + reqId: int + histogramDataEntries: _containers.RepeatedCompositeFieldContainer[_HistogramDataEntry_pb2.HistogramDataEntry] + def __init__(self, reqId: _Optional[int] = ..., histogramDataEntries: _Optional[_Iterable[_Union[_HistogramDataEntry_pb2.HistogramDataEntry, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalDataBar_pb2.py b/ib_async/protobuf/HistoricalDataBar_pb2.py new file mode 100644 index 00000000..8add8653 --- /dev/null +++ b/ib_async/protobuf/HistoricalDataBar_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalDataBar.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalDataBar.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17HistoricalDataBar.proto\x12\x08protobuf\"\xfd\x01\n\x11HistoricalDataBar\x12\x11\n\x04\x64\x61te\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04open\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x11\n\x04high\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x10\n\x03low\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x12\n\x05\x63lose\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x13\n\x06volume\x18\x06 \x01(\tH\x05\x88\x01\x01\x12\x10\n\x03WAP\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x15\n\x08\x62\x61rCount\x18\x08 \x01(\x05H\x07\x88\x01\x01\x42\x07\n\x05_dateB\x07\n\x05_openB\x07\n\x05_highB\x06\n\x04_lowB\x08\n\x06_closeB\t\n\x07_volumeB\x06\n\x04_WAPB\x0b\n\t_barCountBA\n\x16\x63om.ib.client.protobufB\x16HistoricalDataBarProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalDataBar_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026HistoricalDataBarProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALDATABAR']._serialized_start=38 + _globals['_HISTORICALDATABAR']._serialized_end=291 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalDataBar_pb2.pyi b/ib_async/protobuf/HistoricalDataBar_pb2.pyi new file mode 100644 index 00000000..02783165 --- /dev/null +++ b/ib_async/protobuf/HistoricalDataBar_pb2.pyi @@ -0,0 +1,25 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalDataBar(_message.Message): + __slots__ = ("date", "open", "high", "low", "close", "volume", "WAP", "barCount") + DATE_FIELD_NUMBER: _ClassVar[int] + OPEN_FIELD_NUMBER: _ClassVar[int] + HIGH_FIELD_NUMBER: _ClassVar[int] + LOW_FIELD_NUMBER: _ClassVar[int] + CLOSE_FIELD_NUMBER: _ClassVar[int] + VOLUME_FIELD_NUMBER: _ClassVar[int] + WAP_FIELD_NUMBER: _ClassVar[int] + BARCOUNT_FIELD_NUMBER: _ClassVar[int] + date: str + open: float + high: float + low: float + close: float + volume: str + WAP: str + barCount: int + def __init__(self, date: _Optional[str] = ..., open: _Optional[float] = ..., high: _Optional[float] = ..., low: _Optional[float] = ..., close: _Optional[float] = ..., volume: _Optional[str] = ..., WAP: _Optional[str] = ..., barCount: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalDataEnd_pb2.py b/ib_async/protobuf/HistoricalDataEnd_pb2.py new file mode 100644 index 00000000..9ffae511 --- /dev/null +++ b/ib_async/protobuf/HistoricalDataEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalDataEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalDataEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17HistoricalDataEnd.proto\x12\x08protobuf\"\x85\x01\n\x11HistoricalDataEnd\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x19\n\x0cstartDateStr\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nendDateStr\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\x0f\n\r_startDateStrB\r\n\x0b_endDateStrBA\n\x16\x63om.ib.client.protobufB\x16HistoricalDataEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalDataEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026HistoricalDataEndProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALDATAEND']._serialized_start=38 + _globals['_HISTORICALDATAEND']._serialized_end=171 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalDataEnd_pb2.pyi b/ib_async/protobuf/HistoricalDataEnd_pb2.pyi new file mode 100644 index 00000000..9deaeec5 --- /dev/null +++ b/ib_async/protobuf/HistoricalDataEnd_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalDataEnd(_message.Message): + __slots__ = ("reqId", "startDateStr", "endDateStr") + REQID_FIELD_NUMBER: _ClassVar[int] + STARTDATESTR_FIELD_NUMBER: _ClassVar[int] + ENDDATESTR_FIELD_NUMBER: _ClassVar[int] + reqId: int + startDateStr: str + endDateStr: str + def __init__(self, reqId: _Optional[int] = ..., startDateStr: _Optional[str] = ..., endDateStr: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalDataRequest_pb2.py b/ib_async/protobuf/HistoricalDataRequest_pb2.py new file mode 100644 index 00000000..65e544b1 --- /dev/null +++ b/ib_async/protobuf/HistoricalDataRequest_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalDataRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalDataRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bHistoricalDataRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\x85\x04\n\x15HistoricalDataRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x18\n\x0b\x65ndDateTime\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0e\x62\x61rSizeSetting\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x15\n\x08\x64uration\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06useRTH\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x17\n\nwhatToShow\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x17\n\nformatDate\x18\x08 \x01(\x05H\x07\x88\x01\x01\x12\x19\n\x0ckeepUpToDate\x18\t \x01(\x08H\x08\x88\x01\x01\x12G\n\x0c\x63hartOptions\x18\n \x03(\x0b\x32\x31.protobuf.HistoricalDataRequest.ChartOptionsEntry\x1a\x33\n\x11\x43hartOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\x0e\n\x0c_endDateTimeB\x11\n\x0f_barSizeSettingB\x0b\n\t_durationB\t\n\x07_useRTHB\r\n\x0b_whatToShowB\r\n\x0b_formatDateB\x0f\n\r_keepUpToDateBE\n\x16\x63om.ib.client.protobufB\x1aHistoricalDataRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalDataRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032HistoricalDataRequestProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALDATAREQUEST_CHARTOPTIONSENTRY']._loaded_options = None + _globals['_HISTORICALDATAREQUEST_CHARTOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_HISTORICALDATAREQUEST']._serialized_start=58 + _globals['_HISTORICALDATAREQUEST']._serialized_end=575 + _globals['_HISTORICALDATAREQUEST_CHARTOPTIONSENTRY']._serialized_start=395 + _globals['_HISTORICALDATAREQUEST_CHARTOPTIONSENTRY']._serialized_end=446 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalDataRequest_pb2.pyi b/ib_async/protobuf/HistoricalDataRequest_pb2.pyi new file mode 100644 index 00000000..7dd1b674 --- /dev/null +++ b/ib_async/protobuf/HistoricalDataRequest_pb2.pyi @@ -0,0 +1,39 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalDataRequest(_message.Message): + __slots__ = ("reqId", "contract", "endDateTime", "barSizeSetting", "duration", "useRTH", "whatToShow", "formatDate", "keepUpToDate", "chartOptions") + class ChartOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + ENDDATETIME_FIELD_NUMBER: _ClassVar[int] + BARSIZESETTING_FIELD_NUMBER: _ClassVar[int] + DURATION_FIELD_NUMBER: _ClassVar[int] + USERTH_FIELD_NUMBER: _ClassVar[int] + WHATTOSHOW_FIELD_NUMBER: _ClassVar[int] + FORMATDATE_FIELD_NUMBER: _ClassVar[int] + KEEPUPTODATE_FIELD_NUMBER: _ClassVar[int] + CHARTOPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + endDateTime: str + barSizeSetting: str + duration: str + useRTH: bool + whatToShow: str + formatDate: int + keepUpToDate: bool + chartOptions: _containers.ScalarMap[str, str] + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., endDateTime: _Optional[str] = ..., barSizeSetting: _Optional[str] = ..., duration: _Optional[str] = ..., useRTH: bool = ..., whatToShow: _Optional[str] = ..., formatDate: _Optional[int] = ..., keepUpToDate: bool = ..., chartOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalDataUpdate_pb2.py b/ib_async/protobuf/HistoricalDataUpdate_pb2.py new file mode 100644 index 00000000..86b26c0e --- /dev/null +++ b/ib_async/protobuf/HistoricalDataUpdate_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalDataUpdate.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalDataUpdate.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import HistoricalDataBar_pb2 as HistoricalDataBar__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aHistoricalDataUpdate.proto\x12\x08protobuf\x1a\x17HistoricalDataBar.proto\"\x87\x01\n\x14HistoricalDataUpdate\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12;\n\x11historicalDataBar\x18\x02 \x01(\x0b\x32\x1b.protobuf.HistoricalDataBarH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x14\n\x12_historicalDataBarBD\n\x16\x63om.ib.client.protobufB\x19HistoricalDataUpdateProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalDataUpdate_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031HistoricalDataUpdateProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALDATAUPDATE']._serialized_start=66 + _globals['_HISTORICALDATAUPDATE']._serialized_end=201 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalDataUpdate_pb2.pyi b/ib_async/protobuf/HistoricalDataUpdate_pb2.pyi new file mode 100644 index 00000000..d4e05004 --- /dev/null +++ b/ib_async/protobuf/HistoricalDataUpdate_pb2.pyi @@ -0,0 +1,15 @@ +import HistoricalDataBar_pb2 as _HistoricalDataBar_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalDataUpdate(_message.Message): + __slots__ = ("reqId", "historicalDataBar") + REQID_FIELD_NUMBER: _ClassVar[int] + HISTORICALDATABAR_FIELD_NUMBER: _ClassVar[int] + reqId: int + historicalDataBar: _HistoricalDataBar_pb2.HistoricalDataBar + def __init__(self, reqId: _Optional[int] = ..., historicalDataBar: _Optional[_Union[_HistoricalDataBar_pb2.HistoricalDataBar, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalData_pb2.py b/ib_async/protobuf/HistoricalData_pb2.py new file mode 100644 index 00000000..ee49216c --- /dev/null +++ b/ib_async/protobuf/HistoricalData_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import HistoricalDataBar_pb2 as HistoricalDataBar__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14HistoricalData.proto\x12\x08protobuf\x1a\x17HistoricalDataBar.proto\"g\n\x0eHistoricalData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x37\n\x12historicalDataBars\x18\x02 \x03(\x0b\x32\x1b.protobuf.HistoricalDataBarB\x08\n\x06_reqIdB>\n\x16\x63om.ib.client.protobufB\x13HistoricalDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023HistoricalDataProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALDATA']._serialized_start=59 + _globals['_HISTORICALDATA']._serialized_end=162 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalData_pb2.pyi b/ib_async/protobuf/HistoricalData_pb2.pyi new file mode 100644 index 00000000..3e7cd32a --- /dev/null +++ b/ib_async/protobuf/HistoricalData_pb2.pyi @@ -0,0 +1,16 @@ +import HistoricalDataBar_pb2 as _HistoricalDataBar_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalData(_message.Message): + __slots__ = ("reqId", "historicalDataBars") + REQID_FIELD_NUMBER: _ClassVar[int] + HISTORICALDATABARS_FIELD_NUMBER: _ClassVar[int] + reqId: int + historicalDataBars: _containers.RepeatedCompositeFieldContainer[_HistoricalDataBar_pb2.HistoricalDataBar] + def __init__(self, reqId: _Optional[int] = ..., historicalDataBars: _Optional[_Iterable[_Union[_HistoricalDataBar_pb2.HistoricalDataBar, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalNewsEnd_pb2.py b/ib_async/protobuf/HistoricalNewsEnd_pb2.py new file mode 100644 index 00000000..2f05fd07 --- /dev/null +++ b/ib_async/protobuf/HistoricalNewsEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalNewsEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalNewsEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17HistoricalNewsEnd.proto\x12\x08protobuf\"S\n\x11HistoricalNewsEnd\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07hasMore\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_hasMoreBA\n\x16\x63om.ib.client.protobufB\x16HistoricalNewsEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalNewsEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026HistoricalNewsEndProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALNEWSEND']._serialized_start=37 + _globals['_HISTORICALNEWSEND']._serialized_end=120 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalNewsEnd_pb2.pyi b/ib_async/protobuf/HistoricalNewsEnd_pb2.pyi new file mode 100644 index 00000000..7c8f5e59 --- /dev/null +++ b/ib_async/protobuf/HistoricalNewsEnd_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalNewsEnd(_message.Message): + __slots__ = ("reqId", "hasMore") + REQID_FIELD_NUMBER: _ClassVar[int] + HASMORE_FIELD_NUMBER: _ClassVar[int] + reqId: int + hasMore: bool + def __init__(self, reqId: _Optional[int] = ..., hasMore: bool = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalNewsRequest_pb2.py b/ib_async/protobuf/HistoricalNewsRequest_pb2.py new file mode 100644 index 00000000..3540a303 --- /dev/null +++ b/ib_async/protobuf/HistoricalNewsRequest_pb2.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalNewsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalNewsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bHistoricalNewsRequest.proto\x12\x08protobuf\"\x9e\x03\n\x15HistoricalNewsRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x12\n\x05\x63onId\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x1a\n\rproviderCodes\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rstartDateTime\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x65ndDateTime\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x19\n\x0ctotalResults\x18\x06 \x01(\x05H\x05\x88\x01\x01\x12Y\n\x15historicalNewsOptions\x18\x07 \x03(\x0b\x32:.protobuf.HistoricalNewsRequest.HistoricalNewsOptionsEntry\x1a<\n\x1aHistoricalNewsOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x08\n\x06_reqIdB\x08\n\x06_conIdB\x10\n\x0e_providerCodesB\x10\n\x0e_startDateTimeB\x0e\n\x0c_endDateTimeB\x0f\n\r_totalResultsBE\n\x16\x63om.ib.client.protobufB\x1aHistoricalNewsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalNewsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032HistoricalNewsRequestProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALNEWSREQUEST_HISTORICALNEWSOPTIONSENTRY']._loaded_options = None + _globals['_HISTORICALNEWSREQUEST_HISTORICALNEWSOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_HISTORICALNEWSREQUEST']._serialized_start=42 + _globals['_HISTORICALNEWSREQUEST']._serialized_end=456 + _globals['_HISTORICALNEWSREQUEST_HISTORICALNEWSOPTIONSENTRY']._serialized_start=307 + _globals['_HISTORICALNEWSREQUEST_HISTORICALNEWSOPTIONSENTRY']._serialized_end=367 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalNewsRequest_pb2.pyi b/ib_async/protobuf/HistoricalNewsRequest_pb2.pyi new file mode 100644 index 00000000..c55c7d94 --- /dev/null +++ b/ib_async/protobuf/HistoricalNewsRequest_pb2.pyi @@ -0,0 +1,32 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalNewsRequest(_message.Message): + __slots__ = ("reqId", "conId", "providerCodes", "startDateTime", "endDateTime", "totalResults", "historicalNewsOptions") + class HistoricalNewsOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + REQID_FIELD_NUMBER: _ClassVar[int] + CONID_FIELD_NUMBER: _ClassVar[int] + PROVIDERCODES_FIELD_NUMBER: _ClassVar[int] + STARTDATETIME_FIELD_NUMBER: _ClassVar[int] + ENDDATETIME_FIELD_NUMBER: _ClassVar[int] + TOTALRESULTS_FIELD_NUMBER: _ClassVar[int] + HISTORICALNEWSOPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + conId: int + providerCodes: str + startDateTime: str + endDateTime: str + totalResults: int + historicalNewsOptions: _containers.ScalarMap[str, str] + def __init__(self, reqId: _Optional[int] = ..., conId: _Optional[int] = ..., providerCodes: _Optional[str] = ..., startDateTime: _Optional[str] = ..., endDateTime: _Optional[str] = ..., totalResults: _Optional[int] = ..., historicalNewsOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalNews_pb2.py b/ib_async/protobuf/HistoricalNews_pb2.py new file mode 100644 index 00000000..eaa0067c --- /dev/null +++ b/ib_async/protobuf/HistoricalNews_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalNews.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalNews.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14HistoricalNews.proto\x12\x08protobuf\"\xc0\x01\n\x0eHistoricalNews\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x11\n\x04time\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cproviderCode\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tarticleId\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x15\n\x08headline\x18\x05 \x01(\tH\x04\x88\x01\x01\x42\x08\n\x06_reqIdB\x07\n\x05_timeB\x0f\n\r_providerCodeB\x0c\n\n_articleIdB\x0b\n\t_headlineB>\n\x16\x63om.ib.client.protobufB\x13HistoricalNewsProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalNews_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023HistoricalNewsProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALNEWS']._serialized_start=35 + _globals['_HISTORICALNEWS']._serialized_end=227 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalNews_pb2.pyi b/ib_async/protobuf/HistoricalNews_pb2.pyi new file mode 100644 index 00000000..0775015c --- /dev/null +++ b/ib_async/protobuf/HistoricalNews_pb2.pyi @@ -0,0 +1,19 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalNews(_message.Message): + __slots__ = ("reqId", "time", "providerCode", "articleId", "headline") + REQID_FIELD_NUMBER: _ClassVar[int] + TIME_FIELD_NUMBER: _ClassVar[int] + PROVIDERCODE_FIELD_NUMBER: _ClassVar[int] + ARTICLEID_FIELD_NUMBER: _ClassVar[int] + HEADLINE_FIELD_NUMBER: _ClassVar[int] + reqId: int + time: str + providerCode: str + articleId: str + headline: str + def __init__(self, reqId: _Optional[int] = ..., time: _Optional[str] = ..., providerCode: _Optional[str] = ..., articleId: _Optional[str] = ..., headline: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalSchedule_pb2.py b/ib_async/protobuf/HistoricalSchedule_pb2.py new file mode 100644 index 00000000..6dd7e61e --- /dev/null +++ b/ib_async/protobuf/HistoricalSchedule_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalSchedule.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalSchedule.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import HistoricalSession_pb2 as HistoricalSession__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18HistoricalSchedule.proto\x12\x08protobuf\x1a\x17HistoricalSession.proto\"\xe7\x01\n\x12HistoricalSchedule\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x1a\n\rstartDateTime\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x65ndDateTime\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x15\n\x08timeZone\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x37\n\x12historicalSessions\x18\x05 \x03(\x0b\x32\x1b.protobuf.HistoricalSessionB\x08\n\x06_reqIdB\x10\n\x0e_startDateTimeB\x0e\n\x0c_endDateTimeB\x0b\n\t_timeZoneBB\n\x16\x63om.ib.client.protobufB\x17HistoricalScheduleProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalSchedule_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027HistoricalScheduleProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALSCHEDULE']._serialized_start=64 + _globals['_HISTORICALSCHEDULE']._serialized_end=295 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalSchedule_pb2.pyi b/ib_async/protobuf/HistoricalSchedule_pb2.pyi new file mode 100644 index 00000000..5c61fe0c --- /dev/null +++ b/ib_async/protobuf/HistoricalSchedule_pb2.pyi @@ -0,0 +1,22 @@ +import HistoricalSession_pb2 as _HistoricalSession_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalSchedule(_message.Message): + __slots__ = ("reqId", "startDateTime", "endDateTime", "timeZone", "historicalSessions") + REQID_FIELD_NUMBER: _ClassVar[int] + STARTDATETIME_FIELD_NUMBER: _ClassVar[int] + ENDDATETIME_FIELD_NUMBER: _ClassVar[int] + TIMEZONE_FIELD_NUMBER: _ClassVar[int] + HISTORICALSESSIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + startDateTime: str + endDateTime: str + timeZone: str + historicalSessions: _containers.RepeatedCompositeFieldContainer[_HistoricalSession_pb2.HistoricalSession] + def __init__(self, reqId: _Optional[int] = ..., startDateTime: _Optional[str] = ..., endDateTime: _Optional[str] = ..., timeZone: _Optional[str] = ..., historicalSessions: _Optional[_Iterable[_Union[_HistoricalSession_pb2.HistoricalSession, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalSession_pb2.py b/ib_async/protobuf/HistoricalSession_pb2.py new file mode 100644 index 00000000..960cd9e4 --- /dev/null +++ b/ib_async/protobuf/HistoricalSession_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalSession.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalSession.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17HistoricalSession.proto\x12\x08protobuf\"\x8d\x01\n\x11HistoricalSession\x12\x1a\n\rstartDateTime\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x65ndDateTime\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07refDate\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x10\n\x0e_startDateTimeB\x0e\n\x0c_endDateTimeB\n\n\x08_refDateBA\n\x16\x63om.ib.client.protobufB\x16HistoricalSessionProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalSession_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026HistoricalSessionProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALSESSION']._serialized_start=38 + _globals['_HISTORICALSESSION']._serialized_end=179 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalSession_pb2.pyi b/ib_async/protobuf/HistoricalSession_pb2.pyi new file mode 100644 index 00000000..ae206ac9 --- /dev/null +++ b/ib_async/protobuf/HistoricalSession_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalSession(_message.Message): + __slots__ = ("startDateTime", "endDateTime", "refDate") + STARTDATETIME_FIELD_NUMBER: _ClassVar[int] + ENDDATETIME_FIELD_NUMBER: _ClassVar[int] + REFDATE_FIELD_NUMBER: _ClassVar[int] + startDateTime: str + endDateTime: str + refDate: str + def __init__(self, startDateTime: _Optional[str] = ..., endDateTime: _Optional[str] = ..., refDate: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalTickBidAsk_pb2.py b/ib_async/protobuf/HistoricalTickBidAsk_pb2.py new file mode 100644 index 00000000..072ce3a9 --- /dev/null +++ b/ib_async/protobuf/HistoricalTickBidAsk_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalTickBidAsk.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalTickBidAsk.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import TickAttribBidAsk_pb2 as TickAttribBidAsk__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aHistoricalTickBidAsk.proto\x12\x08protobuf\x1a\x16TickAttribBidAsk.proto\"\x8e\x02\n\x14HistoricalTickBidAsk\x12\x11\n\x04time\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12\x39\n\x10tickAttribBidAsk\x18\x02 \x01(\x0b\x32\x1a.protobuf.TickAttribBidAskH\x01\x88\x01\x01\x12\x15\n\x08priceBid\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x15\n\x08priceAsk\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x14\n\x07sizeBid\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x14\n\x07sizeAsk\x18\x06 \x01(\tH\x05\x88\x01\x01\x42\x07\n\x05_timeB\x13\n\x11_tickAttribBidAskB\x0b\n\t_priceBidB\x0b\n\t_priceAskB\n\n\x08_sizeBidB\n\n\x08_sizeAskBD\n\x16\x63om.ib.client.protobufB\x19HistoricalTickBidAskProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalTickBidAsk_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031HistoricalTickBidAskProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALTICKBIDASK']._serialized_start=65 + _globals['_HISTORICALTICKBIDASK']._serialized_end=335 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalTickBidAsk_pb2.pyi b/ib_async/protobuf/HistoricalTickBidAsk_pb2.pyi new file mode 100644 index 00000000..1844a642 --- /dev/null +++ b/ib_async/protobuf/HistoricalTickBidAsk_pb2.pyi @@ -0,0 +1,23 @@ +import TickAttribBidAsk_pb2 as _TickAttribBidAsk_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalTickBidAsk(_message.Message): + __slots__ = ("time", "tickAttribBidAsk", "priceBid", "priceAsk", "sizeBid", "sizeAsk") + TIME_FIELD_NUMBER: _ClassVar[int] + TICKATTRIBBIDASK_FIELD_NUMBER: _ClassVar[int] + PRICEBID_FIELD_NUMBER: _ClassVar[int] + PRICEASK_FIELD_NUMBER: _ClassVar[int] + SIZEBID_FIELD_NUMBER: _ClassVar[int] + SIZEASK_FIELD_NUMBER: _ClassVar[int] + time: int + tickAttribBidAsk: _TickAttribBidAsk_pb2.TickAttribBidAsk + priceBid: float + priceAsk: float + sizeBid: str + sizeAsk: str + def __init__(self, time: _Optional[int] = ..., tickAttribBidAsk: _Optional[_Union[_TickAttribBidAsk_pb2.TickAttribBidAsk, _Mapping]] = ..., priceBid: _Optional[float] = ..., priceAsk: _Optional[float] = ..., sizeBid: _Optional[str] = ..., sizeAsk: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalTickLast_pb2.py b/ib_async/protobuf/HistoricalTickLast_pb2.py new file mode 100644 index 00000000..6929c200 --- /dev/null +++ b/ib_async/protobuf/HistoricalTickLast_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalTickLast.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalTickLast.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import TickAttribLast_pb2 as TickAttribLast__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18HistoricalTickLast.proto\x12\x08protobuf\x1a\x14TickAttribLast.proto\"\x8e\x02\n\x12HistoricalTickLast\x12\x11\n\x04time\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12\x35\n\x0etickAttribLast\x18\x02 \x01(\x0b\x32\x18.protobuf.TickAttribLastH\x01\x88\x01\x01\x12\x12\n\x05price\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x11\n\x04size\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x15\n\x08\x65xchange\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x1e\n\x11specialConditions\x18\x06 \x01(\tH\x05\x88\x01\x01\x42\x07\n\x05_timeB\x11\n\x0f_tickAttribLastB\x08\n\x06_priceB\x07\n\x05_sizeB\x0b\n\t_exchangeB\x14\n\x12_specialConditionsBB\n\x16\x63om.ib.client.protobufB\x17HistoricalTickLastProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalTickLast_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027HistoricalTickLastProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALTICKLAST']._serialized_start=61 + _globals['_HISTORICALTICKLAST']._serialized_end=331 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalTickLast_pb2.pyi b/ib_async/protobuf/HistoricalTickLast_pb2.pyi new file mode 100644 index 00000000..2c19b554 --- /dev/null +++ b/ib_async/protobuf/HistoricalTickLast_pb2.pyi @@ -0,0 +1,23 @@ +import TickAttribLast_pb2 as _TickAttribLast_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalTickLast(_message.Message): + __slots__ = ("time", "tickAttribLast", "price", "size", "exchange", "specialConditions") + TIME_FIELD_NUMBER: _ClassVar[int] + TICKATTRIBLAST_FIELD_NUMBER: _ClassVar[int] + PRICE_FIELD_NUMBER: _ClassVar[int] + SIZE_FIELD_NUMBER: _ClassVar[int] + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + SPECIALCONDITIONS_FIELD_NUMBER: _ClassVar[int] + time: int + tickAttribLast: _TickAttribLast_pb2.TickAttribLast + price: float + size: str + exchange: str + specialConditions: str + def __init__(self, time: _Optional[int] = ..., tickAttribLast: _Optional[_Union[_TickAttribLast_pb2.TickAttribLast, _Mapping]] = ..., price: _Optional[float] = ..., size: _Optional[str] = ..., exchange: _Optional[str] = ..., specialConditions: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalTick_pb2.py b/ib_async/protobuf/HistoricalTick_pb2.py new file mode 100644 index 00000000..7939549d --- /dev/null +++ b/ib_async/protobuf/HistoricalTick_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalTick.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalTick.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14HistoricalTick.proto\x12\x08protobuf\"f\n\x0eHistoricalTick\x12\x11\n\x04time\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12\x12\n\x05price\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x11\n\x04size\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x07\n\x05_timeB\x08\n\x06_priceB\x07\n\x05_sizeB>\n\x16\x63om.ib.client.protobufB\x13HistoricalTickProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalTick_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023HistoricalTickProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALTICK']._serialized_start=34 + _globals['_HISTORICALTICK']._serialized_end=136 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalTick_pb2.pyi b/ib_async/protobuf/HistoricalTick_pb2.pyi new file mode 100644 index 00000000..0aa0490d --- /dev/null +++ b/ib_async/protobuf/HistoricalTick_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalTick(_message.Message): + __slots__ = ("time", "price", "size") + TIME_FIELD_NUMBER: _ClassVar[int] + PRICE_FIELD_NUMBER: _ClassVar[int] + SIZE_FIELD_NUMBER: _ClassVar[int] + time: int + price: float + size: str + def __init__(self, time: _Optional[int] = ..., price: _Optional[float] = ..., size: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalTicksBidAsk_pb2.py b/ib_async/protobuf/HistoricalTicksBidAsk_pb2.py new file mode 100644 index 00000000..d26604fc --- /dev/null +++ b/ib_async/protobuf/HistoricalTicksBidAsk_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalTicksBidAsk.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalTicksBidAsk.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import HistoricalTickBidAsk_pb2 as HistoricalTickBidAsk__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bHistoricalTicksBidAsk.proto\x12\x08protobuf\x1a\x1aHistoricalTickBidAsk.proto\"\x94\x01\n\x15HistoricalTicksBidAsk\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12=\n\x15historicalTicksBidAsk\x18\x02 \x03(\x0b\x32\x1e.protobuf.HistoricalTickBidAsk\x12\x13\n\x06isDone\x18\x03 \x01(\x08H\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\t\n\x07_isDoneBE\n\x16\x63om.ib.client.protobufB\x1aHistoricalTicksBidAskProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalTicksBidAsk_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032HistoricalTicksBidAskProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALTICKSBIDASK']._serialized_start=70 + _globals['_HISTORICALTICKSBIDASK']._serialized_end=218 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalTicksBidAsk_pb2.pyi b/ib_async/protobuf/HistoricalTicksBidAsk_pb2.pyi new file mode 100644 index 00000000..ff34aa8a --- /dev/null +++ b/ib_async/protobuf/HistoricalTicksBidAsk_pb2.pyi @@ -0,0 +1,18 @@ +import HistoricalTickBidAsk_pb2 as _HistoricalTickBidAsk_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalTicksBidAsk(_message.Message): + __slots__ = ("reqId", "historicalTicksBidAsk", "isDone") + REQID_FIELD_NUMBER: _ClassVar[int] + HISTORICALTICKSBIDASK_FIELD_NUMBER: _ClassVar[int] + ISDONE_FIELD_NUMBER: _ClassVar[int] + reqId: int + historicalTicksBidAsk: _containers.RepeatedCompositeFieldContainer[_HistoricalTickBidAsk_pb2.HistoricalTickBidAsk] + isDone: bool + def __init__(self, reqId: _Optional[int] = ..., historicalTicksBidAsk: _Optional[_Iterable[_Union[_HistoricalTickBidAsk_pb2.HistoricalTickBidAsk, _Mapping]]] = ..., isDone: bool = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalTicksLast_pb2.py b/ib_async/protobuf/HistoricalTicksLast_pb2.py new file mode 100644 index 00000000..b1df4890 --- /dev/null +++ b/ib_async/protobuf/HistoricalTicksLast_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalTicksLast.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalTicksLast.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import HistoricalTickLast_pb2 as HistoricalTickLast__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19HistoricalTicksLast.proto\x12\x08protobuf\x1a\x18HistoricalTickLast.proto\"\x8e\x01\n\x13HistoricalTicksLast\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x39\n\x13historicalTicksLast\x18\x02 \x03(\x0b\x32\x1c.protobuf.HistoricalTickLast\x12\x13\n\x06isDone\x18\x03 \x01(\x08H\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\t\n\x07_isDoneBC\n\x16\x63om.ib.client.protobufB\x18HistoricalTicksLastProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalTicksLast_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030HistoricalTicksLastProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALTICKSLAST']._serialized_start=66 + _globals['_HISTORICALTICKSLAST']._serialized_end=208 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalTicksLast_pb2.pyi b/ib_async/protobuf/HistoricalTicksLast_pb2.pyi new file mode 100644 index 00000000..fc9fc926 --- /dev/null +++ b/ib_async/protobuf/HistoricalTicksLast_pb2.pyi @@ -0,0 +1,18 @@ +import HistoricalTickLast_pb2 as _HistoricalTickLast_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalTicksLast(_message.Message): + __slots__ = ("reqId", "historicalTicksLast", "isDone") + REQID_FIELD_NUMBER: _ClassVar[int] + HISTORICALTICKSLAST_FIELD_NUMBER: _ClassVar[int] + ISDONE_FIELD_NUMBER: _ClassVar[int] + reqId: int + historicalTicksLast: _containers.RepeatedCompositeFieldContainer[_HistoricalTickLast_pb2.HistoricalTickLast] + isDone: bool + def __init__(self, reqId: _Optional[int] = ..., historicalTicksLast: _Optional[_Iterable[_Union[_HistoricalTickLast_pb2.HistoricalTickLast, _Mapping]]] = ..., isDone: bool = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalTicksRequest_pb2.py b/ib_async/protobuf/HistoricalTicksRequest_pb2.py new file mode 100644 index 00000000..2a7ab02b --- /dev/null +++ b/ib_async/protobuf/HistoricalTicksRequest_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalTicksRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalTicksRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cHistoricalTicksRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xe0\x03\n\x16HistoricalTicksRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x1a\n\rstartDateTime\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x65ndDateTime\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x1a\n\rnumberOfTicks\x18\x05 \x01(\x05H\x04\x88\x01\x01\x12\x17\n\nwhatToShow\x18\x06 \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06useRTH\x18\x07 \x01(\x08H\x06\x88\x01\x01\x12\x17\n\nignoreSize\x18\x08 \x01(\x08H\x07\x88\x01\x01\x12\x46\n\x0bmiscOptions\x18\t \x03(\x0b\x32\x31.protobuf.HistoricalTicksRequest.MiscOptionsEntry\x1a\x32\n\x10MiscOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\x10\n\x0e_startDateTimeB\x0e\n\x0c_endDateTimeB\x10\n\x0e_numberOfTicksB\r\n\x0b_whatToShowB\t\n\x07_useRTHB\r\n\x0b_ignoreSizeBF\n\x16\x63om.ib.client.protobufB\x1bHistoricalTicksRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalTicksRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\033HistoricalTicksRequestProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALTICKSREQUEST_MISCOPTIONSENTRY']._loaded_options = None + _globals['_HISTORICALTICKSREQUEST_MISCOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_HISTORICALTICKSREQUEST']._serialized_start=59 + _globals['_HISTORICALTICKSREQUEST']._serialized_end=539 + _globals['_HISTORICALTICKSREQUEST_MISCOPTIONSENTRY']._serialized_start=373 + _globals['_HISTORICALTICKSREQUEST_MISCOPTIONSENTRY']._serialized_end=423 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalTicksRequest_pb2.pyi b/ib_async/protobuf/HistoricalTicksRequest_pb2.pyi new file mode 100644 index 00000000..388af137 --- /dev/null +++ b/ib_async/protobuf/HistoricalTicksRequest_pb2.pyi @@ -0,0 +1,37 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalTicksRequest(_message.Message): + __slots__ = ("reqId", "contract", "startDateTime", "endDateTime", "numberOfTicks", "whatToShow", "useRTH", "ignoreSize", "miscOptions") + class MiscOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + STARTDATETIME_FIELD_NUMBER: _ClassVar[int] + ENDDATETIME_FIELD_NUMBER: _ClassVar[int] + NUMBEROFTICKS_FIELD_NUMBER: _ClassVar[int] + WHATTOSHOW_FIELD_NUMBER: _ClassVar[int] + USERTH_FIELD_NUMBER: _ClassVar[int] + IGNORESIZE_FIELD_NUMBER: _ClassVar[int] + MISCOPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + startDateTime: str + endDateTime: str + numberOfTicks: int + whatToShow: str + useRTH: bool + ignoreSize: bool + miscOptions: _containers.ScalarMap[str, str] + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., startDateTime: _Optional[str] = ..., endDateTime: _Optional[str] = ..., numberOfTicks: _Optional[int] = ..., whatToShow: _Optional[str] = ..., useRTH: bool = ..., ignoreSize: bool = ..., miscOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/HistoricalTicks_pb2.py b/ib_async/protobuf/HistoricalTicks_pb2.py new file mode 100644 index 00000000..b51ed396 --- /dev/null +++ b/ib_async/protobuf/HistoricalTicks_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: HistoricalTicks.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'HistoricalTicks.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import HistoricalTick_pb2 as HistoricalTick__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15HistoricalTicks.proto\x12\x08protobuf\x1a\x14HistoricalTick.proto\"\x82\x01\n\x0fHistoricalTicks\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x31\n\x0fhistoricalTicks\x18\x02 \x03(\x0b\x32\x18.protobuf.HistoricalTick\x12\x13\n\x06isDone\x18\x03 \x01(\x08H\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\t\n\x07_isDoneB?\n\x16\x63om.ib.client.protobufB\x14HistoricalTicksProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'HistoricalTicks_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024HistoricalTicksProto\252\002\016IBApi.protobuf' + _globals['_HISTORICALTICKS']._serialized_start=58 + _globals['_HISTORICALTICKS']._serialized_end=188 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/HistoricalTicks_pb2.pyi b/ib_async/protobuf/HistoricalTicks_pb2.pyi new file mode 100644 index 00000000..629b6272 --- /dev/null +++ b/ib_async/protobuf/HistoricalTicks_pb2.pyi @@ -0,0 +1,18 @@ +import HistoricalTick_pb2 as _HistoricalTick_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HistoricalTicks(_message.Message): + __slots__ = ("reqId", "historicalTicks", "isDone") + REQID_FIELD_NUMBER: _ClassVar[int] + HISTORICALTICKS_FIELD_NUMBER: _ClassVar[int] + ISDONE_FIELD_NUMBER: _ClassVar[int] + reqId: int + historicalTicks: _containers.RepeatedCompositeFieldContainer[_HistoricalTick_pb2.HistoricalTick] + isDone: bool + def __init__(self, reqId: _Optional[int] = ..., historicalTicks: _Optional[_Iterable[_Union[_HistoricalTick_pb2.HistoricalTick, _Mapping]]] = ..., isDone: bool = ...) -> None: ... diff --git a/ib_async/protobuf/IdsRequest_pb2.py b/ib_async/protobuf/IdsRequest_pb2.py new file mode 100644 index 00000000..e1133f23 --- /dev/null +++ b/ib_async/protobuf/IdsRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: IdsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'IdsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10IdsRequest.proto\x12\x08protobuf\",\n\nIdsRequest\x12\x13\n\x06numIds\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\t\n\x07_numIdsB:\n\x16\x63om.ib.client.protobufB\x0fIdsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'IdsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\017IdsRequestProto\252\002\016IBApi.protobuf' + _globals['_IDSREQUEST']._serialized_start=30 + _globals['_IDSREQUEST']._serialized_end=74 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/IdsRequest_pb2.pyi b/ib_async/protobuf/IdsRequest_pb2.pyi new file mode 100644 index 00000000..867a4ab1 --- /dev/null +++ b/ib_async/protobuf/IdsRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class IdsRequest(_message.Message): + __slots__ = ("numIds",) + NUMIDS_FIELD_NUMBER: _ClassVar[int] + numIds: int + def __init__(self, numIds: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/IneligibilityReason_pb2.py b/ib_async/protobuf/IneligibilityReason_pb2.py new file mode 100644 index 00000000..baf7f10f --- /dev/null +++ b/ib_async/protobuf/IneligibilityReason_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: IneligibilityReason.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'IneligibilityReason.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19IneligibilityReason.proto\x12\x08protobuf\"W\n\x13IneligibilityReason\x12\x0f\n\x02id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x0e\n\x0c_descriptionBC\n\x16\x63om.ib.client.protobufB\x18IneligibilityReasonProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'IneligibilityReason_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030IneligibilityReasonProto\252\002\016IBApi.protobuf' + _globals['_INELIGIBILITYREASON']._serialized_start=39 + _globals['_INELIGIBILITYREASON']._serialized_end=126 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/IneligibilityReason_pb2.pyi b/ib_async/protobuf/IneligibilityReason_pb2.pyi new file mode 100644 index 00000000..a3bdb41f --- /dev/null +++ b/ib_async/protobuf/IneligibilityReason_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class IneligibilityReason(_message.Message): + __slots__ = ("id", "description") + ID_FIELD_NUMBER: _ClassVar[int] + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + id: str + description: str + def __init__(self, id: _Optional[str] = ..., description: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/ManagedAccountsRequest_pb2.py b/ib_async/protobuf/ManagedAccountsRequest_pb2.py new file mode 100644 index 00000000..65dacd8c --- /dev/null +++ b/ib_async/protobuf/ManagedAccountsRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ManagedAccountsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ManagedAccountsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cManagedAccountsRequest.proto\x12\x08protobuf\"\x18\n\x16ManagedAccountsRequestBF\n\x16\x63om.ib.client.protobufB\x1bManagedAccountsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ManagedAccountsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\033ManagedAccountsRequestProto\252\002\016IBApi.protobuf' + _globals['_MANAGEDACCOUNTSREQUEST']._serialized_start=42 + _globals['_MANAGEDACCOUNTSREQUEST']._serialized_end=66 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ManagedAccountsRequest_pb2.pyi b/ib_async/protobuf/ManagedAccountsRequest_pb2.pyi new file mode 100644 index 00000000..b74d781f --- /dev/null +++ b/ib_async/protobuf/ManagedAccountsRequest_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class ManagedAccountsRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/ManagedAccounts_pb2.py b/ib_async/protobuf/ManagedAccounts_pb2.py new file mode 100644 index 00000000..e0c51668 --- /dev/null +++ b/ib_async/protobuf/ManagedAccounts_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ManagedAccounts.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ManagedAccounts.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15ManagedAccounts.proto\x12\x08protobuf\"=\n\x0fManagedAccounts\x12\x19\n\x0c\x61\x63\x63ountsList\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x0f\n\r_accountsListB?\n\x16\x63om.ib.client.protobufB\x14ManagedAccountsProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ManagedAccounts_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024ManagedAccountsProto\252\002\016IBApi.protobuf' + _globals['_MANAGEDACCOUNTS']._serialized_start=35 + _globals['_MANAGEDACCOUNTS']._serialized_end=96 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ManagedAccounts_pb2.pyi b/ib_async/protobuf/ManagedAccounts_pb2.pyi new file mode 100644 index 00000000..07562804 --- /dev/null +++ b/ib_async/protobuf/ManagedAccounts_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ManagedAccounts(_message.Message): + __slots__ = ("accountsList",) + ACCOUNTSLIST_FIELD_NUMBER: _ClassVar[int] + accountsList: str + def __init__(self, accountsList: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/MarketDataRequest_pb2.py b/ib_async/protobuf/MarketDataRequest_pb2.py new file mode 100644 index 00000000..c6ca8aaf --- /dev/null +++ b/ib_async/protobuf/MarketDataRequest_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketDataRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketDataRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17MarketDataRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\x80\x03\n\x11MarketDataRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x1c\n\x0fgenericTickList\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x15\n\x08snapshot\x18\x04 \x01(\x08H\x03\x88\x01\x01\x12\x1f\n\x12regulatorySnapshot\x18\x05 \x01(\x08H\x04\x88\x01\x01\x12M\n\x11marketDataOptions\x18\x06 \x03(\x0b\x32\x32.protobuf.MarketDataRequest.MarketDataOptionsEntry\x1a\x38\n\x16MarketDataOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\x12\n\x10_genericTickListB\x0b\n\t_snapshotB\x15\n\x13_regulatorySnapshotBA\n\x16\x63om.ib.client.protobufB\x16MarketDataRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketDataRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026MarketDataRequestProto\252\002\016IBApi.protobuf' + _globals['_MARKETDATAREQUEST_MARKETDATAOPTIONSENTRY']._loaded_options = None + _globals['_MARKETDATAREQUEST_MARKETDATAOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_MARKETDATAREQUEST']._serialized_start=54 + _globals['_MARKETDATAREQUEST']._serialized_end=438 + _globals['_MARKETDATAREQUEST_MARKETDATAOPTIONSENTRY']._serialized_start=303 + _globals['_MARKETDATAREQUEST_MARKETDATAOPTIONSENTRY']._serialized_end=359 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketDataRequest_pb2.pyi b/ib_async/protobuf/MarketDataRequest_pb2.pyi new file mode 100644 index 00000000..ba9386c3 --- /dev/null +++ b/ib_async/protobuf/MarketDataRequest_pb2.pyi @@ -0,0 +1,31 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketDataRequest(_message.Message): + __slots__ = ("reqId", "contract", "genericTickList", "snapshot", "regulatorySnapshot", "marketDataOptions") + class MarketDataOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + GENERICTICKLIST_FIELD_NUMBER: _ClassVar[int] + SNAPSHOT_FIELD_NUMBER: _ClassVar[int] + REGULATORYSNAPSHOT_FIELD_NUMBER: _ClassVar[int] + MARKETDATAOPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + genericTickList: str + snapshot: bool + regulatorySnapshot: bool + marketDataOptions: _containers.ScalarMap[str, str] + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., genericTickList: _Optional[str] = ..., snapshot: bool = ..., regulatorySnapshot: bool = ..., marketDataOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/MarketDataTypeRequest_pb2.py b/ib_async/protobuf/MarketDataTypeRequest_pb2.py new file mode 100644 index 00000000..f9c38c9b --- /dev/null +++ b/ib_async/protobuf/MarketDataTypeRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketDataTypeRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketDataTypeRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bMarketDataTypeRequest.proto\x12\x08protobuf\"G\n\x15MarketDataTypeRequest\x12\x1b\n\x0emarketDataType\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x11\n\x0f_marketDataTypeBE\n\x16\x63om.ib.client.protobufB\x1aMarketDataTypeRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketDataTypeRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032MarketDataTypeRequestProto\252\002\016IBApi.protobuf' + _globals['_MARKETDATATYPEREQUEST']._serialized_start=41 + _globals['_MARKETDATATYPEREQUEST']._serialized_end=112 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketDataTypeRequest_pb2.pyi b/ib_async/protobuf/MarketDataTypeRequest_pb2.pyi new file mode 100644 index 00000000..eadda12e --- /dev/null +++ b/ib_async/protobuf/MarketDataTypeRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketDataTypeRequest(_message.Message): + __slots__ = ("marketDataType",) + MARKETDATATYPE_FIELD_NUMBER: _ClassVar[int] + marketDataType: int + def __init__(self, marketDataType: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/MarketDataType_pb2.py b/ib_async/protobuf/MarketDataType_pb2.py new file mode 100644 index 00000000..6061d522 --- /dev/null +++ b/ib_async/protobuf/MarketDataType_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketDataType.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketDataType.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14MarketDataType.proto\x12\x08protobuf\"^\n\x0eMarketDataType\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x1b\n\x0emarketDataType\x18\x02 \x01(\x05H\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x11\n\x0f_marketDataTypeB>\n\x16\x63om.ib.client.protobufB\x13MarketDataTypeProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketDataType_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023MarketDataTypeProto\252\002\016IBApi.protobuf' + _globals['_MARKETDATATYPE']._serialized_start=34 + _globals['_MARKETDATATYPE']._serialized_end=128 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketDataType_pb2.pyi b/ib_async/protobuf/MarketDataType_pb2.pyi new file mode 100644 index 00000000..e1efb94c --- /dev/null +++ b/ib_async/protobuf/MarketDataType_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketDataType(_message.Message): + __slots__ = ("reqId", "marketDataType") + REQID_FIELD_NUMBER: _ClassVar[int] + MARKETDATATYPE_FIELD_NUMBER: _ClassVar[int] + reqId: int + marketDataType: int + def __init__(self, reqId: _Optional[int] = ..., marketDataType: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/MarketDepthData_pb2.py b/ib_async/protobuf/MarketDepthData_pb2.py new file mode 100644 index 00000000..961f93f2 --- /dev/null +++ b/ib_async/protobuf/MarketDepthData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketDepthData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketDepthData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15MarketDepthData.proto\x12\x08protobuf\"\x87\x02\n\x0fMarketDepthData\x12\x15\n\x08position\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x16\n\toperation\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x11\n\x04side\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12\x12\n\x05price\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x11\n\x04size\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0bmarketMaker\x18\x06 \x01(\tH\x05\x88\x01\x01\x12\x19\n\x0cisSmartDepth\x18\x07 \x01(\x08H\x06\x88\x01\x01\x42\x0b\n\t_positionB\x0c\n\n_operationB\x07\n\x05_sideB\x08\n\x06_priceB\x07\n\x05_sizeB\x0e\n\x0c_marketMakerB\x0f\n\r_isSmartDepthB?\n\x16\x63om.ib.client.protobufB\x14MarketDepthDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketDepthData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024MarketDepthDataProto\252\002\016IBApi.protobuf' + _globals['_MARKETDEPTHDATA']._serialized_start=36 + _globals['_MARKETDEPTHDATA']._serialized_end=299 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketDepthData_pb2.pyi b/ib_async/protobuf/MarketDepthData_pb2.pyi new file mode 100644 index 00000000..37390afa --- /dev/null +++ b/ib_async/protobuf/MarketDepthData_pb2.pyi @@ -0,0 +1,23 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketDepthData(_message.Message): + __slots__ = ("position", "operation", "side", "price", "size", "marketMaker", "isSmartDepth") + POSITION_FIELD_NUMBER: _ClassVar[int] + OPERATION_FIELD_NUMBER: _ClassVar[int] + SIDE_FIELD_NUMBER: _ClassVar[int] + PRICE_FIELD_NUMBER: _ClassVar[int] + SIZE_FIELD_NUMBER: _ClassVar[int] + MARKETMAKER_FIELD_NUMBER: _ClassVar[int] + ISSMARTDEPTH_FIELD_NUMBER: _ClassVar[int] + position: int + operation: int + side: int + price: float + size: str + marketMaker: str + isSmartDepth: bool + def __init__(self, position: _Optional[int] = ..., operation: _Optional[int] = ..., side: _Optional[int] = ..., price: _Optional[float] = ..., size: _Optional[str] = ..., marketMaker: _Optional[str] = ..., isSmartDepth: bool = ...) -> None: ... diff --git a/ib_async/protobuf/MarketDepthExchangesRequest_pb2.py b/ib_async/protobuf/MarketDepthExchangesRequest_pb2.py new file mode 100644 index 00000000..d133d125 --- /dev/null +++ b/ib_async/protobuf/MarketDepthExchangesRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketDepthExchangesRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketDepthExchangesRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!MarketDepthExchangesRequest.proto\x12\x08protobuf\"\x1d\n\x1bMarketDepthExchangesRequestBK\n\x16\x63om.ib.client.protobufB MarketDepthExchangesRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketDepthExchangesRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB MarketDepthExchangesRequestProto\252\002\016IBApi.protobuf' + _globals['_MARKETDEPTHEXCHANGESREQUEST']._serialized_start=47 + _globals['_MARKETDEPTHEXCHANGESREQUEST']._serialized_end=76 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketDepthExchangesRequest_pb2.pyi b/ib_async/protobuf/MarketDepthExchangesRequest_pb2.pyi new file mode 100644 index 00000000..1a926d0b --- /dev/null +++ b/ib_async/protobuf/MarketDepthExchangesRequest_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketDepthExchangesRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/MarketDepthExchanges_pb2.py b/ib_async/protobuf/MarketDepthExchanges_pb2.py new file mode 100644 index 00000000..9235062a --- /dev/null +++ b/ib_async/protobuf/MarketDepthExchanges_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketDepthExchanges.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketDepthExchanges.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import DepthMarketDataDescription_pb2 as DepthMarketDataDescription__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aMarketDepthExchanges.proto\x12\x08protobuf\x1a DepthMarketDataDescription.proto\"a\n\x14MarketDepthExchanges\x12I\n\x1b\x64\x65pthMarketDataDescriptions\x18\x01 \x03(\x0b\x32$.protobuf.DepthMarketDataDescriptionBD\n\x16\x63om.ib.client.protobufB\x19MarketDepthExchangesProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketDepthExchanges_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031MarketDepthExchangesProto\252\002\016IBApi.protobuf' + _globals['_MARKETDEPTHEXCHANGES']._serialized_start=74 + _globals['_MARKETDEPTHEXCHANGES']._serialized_end=171 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketDepthExchanges_pb2.pyi b/ib_async/protobuf/MarketDepthExchanges_pb2.pyi new file mode 100644 index 00000000..0bff2790 --- /dev/null +++ b/ib_async/protobuf/MarketDepthExchanges_pb2.pyi @@ -0,0 +1,14 @@ +import DepthMarketDataDescription_pb2 as _DepthMarketDataDescription_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketDepthExchanges(_message.Message): + __slots__ = ("depthMarketDataDescriptions",) + DEPTHMARKETDATADESCRIPTIONS_FIELD_NUMBER: _ClassVar[int] + depthMarketDataDescriptions: _containers.RepeatedCompositeFieldContainer[_DepthMarketDataDescription_pb2.DepthMarketDataDescription] + def __init__(self, depthMarketDataDescriptions: _Optional[_Iterable[_Union[_DepthMarketDataDescription_pb2.DepthMarketDataDescription, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/MarketDepthL2_pb2.py b/ib_async/protobuf/MarketDepthL2_pb2.py new file mode 100644 index 00000000..d325a531 --- /dev/null +++ b/ib_async/protobuf/MarketDepthL2_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketDepthL2.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketDepthL2.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import MarketDepthData_pb2 as MarketDepthData__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13MarketDepthL2.proto\x12\x08protobuf\x1a\x15MarketDepthData.proto\"z\n\rMarketDepthL2\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x37\n\x0fmarketDepthData\x18\x02 \x01(\x0b\x32\x19.protobuf.MarketDepthDataH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x12\n\x10_marketDepthDataB=\n\x16\x63om.ib.client.protobufB\x12MarketDepthL2Proto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketDepthL2_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\022MarketDepthL2Proto\252\002\016IBApi.protobuf' + _globals['_MARKETDEPTHL2']._serialized_start=56 + _globals['_MARKETDEPTHL2']._serialized_end=178 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketDepthL2_pb2.pyi b/ib_async/protobuf/MarketDepthL2_pb2.pyi new file mode 100644 index 00000000..ac4bff58 --- /dev/null +++ b/ib_async/protobuf/MarketDepthL2_pb2.pyi @@ -0,0 +1,15 @@ +import MarketDepthData_pb2 as _MarketDepthData_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketDepthL2(_message.Message): + __slots__ = ("reqId", "marketDepthData") + REQID_FIELD_NUMBER: _ClassVar[int] + MARKETDEPTHDATA_FIELD_NUMBER: _ClassVar[int] + reqId: int + marketDepthData: _MarketDepthData_pb2.MarketDepthData + def __init__(self, reqId: _Optional[int] = ..., marketDepthData: _Optional[_Union[_MarketDepthData_pb2.MarketDepthData, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/MarketDepthRequest_pb2.py b/ib_async/protobuf/MarketDepthRequest_pb2.py new file mode 100644 index 00000000..34706a4d --- /dev/null +++ b/ib_async/protobuf/MarketDepthRequest_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketDepthRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketDepthRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18MarketDepthRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xc5\x02\n\x12MarketDepthRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x14\n\x07numRows\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12\x19\n\x0cisSmartDepth\x18\x04 \x01(\x08H\x03\x88\x01\x01\x12P\n\x12marketDepthOptions\x18\x05 \x03(\x0b\x32\x34.protobuf.MarketDepthRequest.MarketDepthOptionsEntry\x1a\x39\n\x17MarketDepthOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\n\n\x08_numRowsB\x0f\n\r_isSmartDepthBB\n\x16\x63om.ib.client.protobufB\x17MarketDepthRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketDepthRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027MarketDepthRequestProto\252\002\016IBApi.protobuf' + _globals['_MARKETDEPTHREQUEST_MARKETDEPTHOPTIONSENTRY']._loaded_options = None + _globals['_MARKETDEPTHREQUEST_MARKETDEPTHOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_MARKETDEPTHREQUEST']._serialized_start=55 + _globals['_MARKETDEPTHREQUEST']._serialized_end=380 + _globals['_MARKETDEPTHREQUEST_MARKETDEPTHOPTIONSENTRY']._serialized_start=271 + _globals['_MARKETDEPTHREQUEST_MARKETDEPTHOPTIONSENTRY']._serialized_end=328 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketDepthRequest_pb2.pyi b/ib_async/protobuf/MarketDepthRequest_pb2.pyi new file mode 100644 index 00000000..423be6de --- /dev/null +++ b/ib_async/protobuf/MarketDepthRequest_pb2.pyi @@ -0,0 +1,29 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketDepthRequest(_message.Message): + __slots__ = ("reqId", "contract", "numRows", "isSmartDepth", "marketDepthOptions") + class MarketDepthOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + NUMROWS_FIELD_NUMBER: _ClassVar[int] + ISSMARTDEPTH_FIELD_NUMBER: _ClassVar[int] + MARKETDEPTHOPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + numRows: int + isSmartDepth: bool + marketDepthOptions: _containers.ScalarMap[str, str] + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., numRows: _Optional[int] = ..., isSmartDepth: bool = ..., marketDepthOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/MarketDepth_pb2.py b/ib_async/protobuf/MarketDepth_pb2.py new file mode 100644 index 00000000..23bcc5e5 --- /dev/null +++ b/ib_async/protobuf/MarketDepth_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketDepth.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketDepth.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import MarketDepthData_pb2 as MarketDepthData__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11MarketDepth.proto\x12\x08protobuf\x1a\x15MarketDepthData.proto\"x\n\x0bMarketDepth\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x37\n\x0fmarketDepthData\x18\x02 \x01(\x0b\x32\x19.protobuf.MarketDepthDataH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x12\n\x10_marketDepthDataB;\n\x16\x63om.ib.client.protobufB\x10MarketDepthProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketDepth_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020MarketDepthProto\252\002\016IBApi.protobuf' + _globals['_MARKETDEPTH']._serialized_start=54 + _globals['_MARKETDEPTH']._serialized_end=174 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketDepth_pb2.pyi b/ib_async/protobuf/MarketDepth_pb2.pyi new file mode 100644 index 00000000..8ec9338a --- /dev/null +++ b/ib_async/protobuf/MarketDepth_pb2.pyi @@ -0,0 +1,15 @@ +import MarketDepthData_pb2 as _MarketDepthData_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketDepth(_message.Message): + __slots__ = ("reqId", "marketDepthData") + REQID_FIELD_NUMBER: _ClassVar[int] + MARKETDEPTHDATA_FIELD_NUMBER: _ClassVar[int] + reqId: int + marketDepthData: _MarketDepthData_pb2.MarketDepthData + def __init__(self, reqId: _Optional[int] = ..., marketDepthData: _Optional[_Union[_MarketDepthData_pb2.MarketDepthData, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/MarketRuleRequest_pb2.py b/ib_async/protobuf/MarketRuleRequest_pb2.py new file mode 100644 index 00000000..a3faa94c --- /dev/null +++ b/ib_async/protobuf/MarketRuleRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketRuleRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketRuleRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17MarketRuleRequest.proto\x12\x08protobuf\"?\n\x11MarketRuleRequest\x12\x19\n\x0cmarketRuleId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x0f\n\r_marketRuleIdBA\n\x16\x63om.ib.client.protobufB\x16MarketRuleRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketRuleRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026MarketRuleRequestProto\252\002\016IBApi.protobuf' + _globals['_MARKETRULEREQUEST']._serialized_start=37 + _globals['_MARKETRULEREQUEST']._serialized_end=100 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketRuleRequest_pb2.pyi b/ib_async/protobuf/MarketRuleRequest_pb2.pyi new file mode 100644 index 00000000..4f072ff4 --- /dev/null +++ b/ib_async/protobuf/MarketRuleRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketRuleRequest(_message.Message): + __slots__ = ("marketRuleId",) + MARKETRULEID_FIELD_NUMBER: _ClassVar[int] + marketRuleId: int + def __init__(self, marketRuleId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/MarketRule_pb2.py b/ib_async/protobuf/MarketRule_pb2.py new file mode 100644 index 00000000..c7de668c --- /dev/null +++ b/ib_async/protobuf/MarketRule_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MarketRule.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MarketRule.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import PriceIncrement_pb2 as PriceIncrement__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10MarketRule.proto\x12\x08protobuf\x1a\x14PriceIncrement.proto\"k\n\nMarketRule\x12\x19\n\x0cmarketRuleId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x31\n\x0fpriceIncrements\x18\x02 \x03(\x0b\x32\x18.protobuf.PriceIncrementB\x0f\n\r_marketRuleIdB:\n\x16\x63om.ib.client.protobufB\x0fMarketRuleProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MarketRule_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\017MarketRuleProto\252\002\016IBApi.protobuf' + _globals['_MARKETRULE']._serialized_start=52 + _globals['_MARKETRULE']._serialized_end=159 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MarketRule_pb2.pyi b/ib_async/protobuf/MarketRule_pb2.pyi new file mode 100644 index 00000000..8584fda2 --- /dev/null +++ b/ib_async/protobuf/MarketRule_pb2.pyi @@ -0,0 +1,16 @@ +import PriceIncrement_pb2 as _PriceIncrement_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class MarketRule(_message.Message): + __slots__ = ("marketRuleId", "priceIncrements") + MARKETRULEID_FIELD_NUMBER: _ClassVar[int] + PRICEINCREMENTS_FIELD_NUMBER: _ClassVar[int] + marketRuleId: int + priceIncrements: _containers.RepeatedCompositeFieldContainer[_PriceIncrement_pb2.PriceIncrement] + def __init__(self, marketRuleId: _Optional[int] = ..., priceIncrements: _Optional[_Iterable[_Union[_PriceIncrement_pb2.PriceIncrement, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/MatchingSymbolsRequest_pb2.py b/ib_async/protobuf/MatchingSymbolsRequest_pb2.py new file mode 100644 index 00000000..692e1c2f --- /dev/null +++ b/ib_async/protobuf/MatchingSymbolsRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: MatchingSymbolsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'MatchingSymbolsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cMatchingSymbolsRequest.proto\x12\x08protobuf\"X\n\x16MatchingSymbolsRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07pattern\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_patternBF\n\x16\x63om.ib.client.protobufB\x1bMatchingSymbolsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MatchingSymbolsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\033MatchingSymbolsRequestProto\252\002\016IBApi.protobuf' + _globals['_MATCHINGSYMBOLSREQUEST']._serialized_start=42 + _globals['_MATCHINGSYMBOLSREQUEST']._serialized_end=130 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/MatchingSymbolsRequest_pb2.pyi b/ib_async/protobuf/MatchingSymbolsRequest_pb2.pyi new file mode 100644 index 00000000..efa19da2 --- /dev/null +++ b/ib_async/protobuf/MatchingSymbolsRequest_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class MatchingSymbolsRequest(_message.Message): + __slots__ = ("reqId", "pattern") + REQID_FIELD_NUMBER: _ClassVar[int] + PATTERN_FIELD_NUMBER: _ClassVar[int] + reqId: int + pattern: str + def __init__(self, reqId: _Optional[int] = ..., pattern: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/NewsArticleRequest_pb2.py b/ib_async/protobuf/NewsArticleRequest_pb2.py new file mode 100644 index 00000000..0d29977b --- /dev/null +++ b/ib_async/protobuf/NewsArticleRequest_pb2.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: NewsArticleRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'NewsArticleRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18NewsArticleRequest.proto\x12\x08protobuf\"\x91\x02\n\x12NewsArticleRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x19\n\x0cproviderCode\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x16\n\tarticleId\x18\x03 \x01(\tH\x02\x88\x01\x01\x12P\n\x12newsArticleOptions\x18\x04 \x03(\x0b\x32\x34.protobuf.NewsArticleRequest.NewsArticleOptionsEntry\x1a\x39\n\x17NewsArticleOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x08\n\x06_reqIdB\x0f\n\r_providerCodeB\x0c\n\n_articleIdBB\n\x16\x63om.ib.client.protobufB\x17NewsArticleRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'NewsArticleRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027NewsArticleRequestProto\252\002\016IBApi.protobuf' + _globals['_NEWSARTICLEREQUEST_NEWSARTICLEOPTIONSENTRY']._loaded_options = None + _globals['_NEWSARTICLEREQUEST_NEWSARTICLEOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_NEWSARTICLEREQUEST']._serialized_start=39 + _globals['_NEWSARTICLEREQUEST']._serialized_end=312 + _globals['_NEWSARTICLEREQUEST_NEWSARTICLEOPTIONSENTRY']._serialized_start=214 + _globals['_NEWSARTICLEREQUEST_NEWSARTICLEOPTIONSENTRY']._serialized_end=271 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/NewsArticleRequest_pb2.pyi b/ib_async/protobuf/NewsArticleRequest_pb2.pyi new file mode 100644 index 00000000..b994b01c --- /dev/null +++ b/ib_async/protobuf/NewsArticleRequest_pb2.pyi @@ -0,0 +1,26 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class NewsArticleRequest(_message.Message): + __slots__ = ("reqId", "providerCode", "articleId", "newsArticleOptions") + class NewsArticleOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + REQID_FIELD_NUMBER: _ClassVar[int] + PROVIDERCODE_FIELD_NUMBER: _ClassVar[int] + ARTICLEID_FIELD_NUMBER: _ClassVar[int] + NEWSARTICLEOPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + providerCode: str + articleId: str + newsArticleOptions: _containers.ScalarMap[str, str] + def __init__(self, reqId: _Optional[int] = ..., providerCode: _Optional[str] = ..., articleId: _Optional[str] = ..., newsArticleOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/NewsArticle_pb2.py b/ib_async/protobuf/NewsArticle_pb2.py new file mode 100644 index 00000000..5fe3f5ba --- /dev/null +++ b/ib_async/protobuf/NewsArticle_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: NewsArticle.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'NewsArticle.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11NewsArticle.proto\x12\x08protobuf\"\x7f\n\x0bNewsArticle\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x18\n\x0b\x61rticleType\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x18\n\x0b\x61rticleText\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\x0e\n\x0c_articleTypeB\x0e\n\x0c_articleTextB;\n\x16\x63om.ib.client.protobufB\x10NewsArticleProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'NewsArticle_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020NewsArticleProto\252\002\016IBApi.protobuf' + _globals['_NEWSARTICLE']._serialized_start=31 + _globals['_NEWSARTICLE']._serialized_end=158 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/NewsArticle_pb2.pyi b/ib_async/protobuf/NewsArticle_pb2.pyi new file mode 100644 index 00000000..18df03ce --- /dev/null +++ b/ib_async/protobuf/NewsArticle_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class NewsArticle(_message.Message): + __slots__ = ("reqId", "articleType", "articleText") + REQID_FIELD_NUMBER: _ClassVar[int] + ARTICLETYPE_FIELD_NUMBER: _ClassVar[int] + ARTICLETEXT_FIELD_NUMBER: _ClassVar[int] + reqId: int + articleType: int + articleText: str + def __init__(self, reqId: _Optional[int] = ..., articleType: _Optional[int] = ..., articleText: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/NewsBulletin_pb2.py b/ib_async/protobuf/NewsBulletin_pb2.py new file mode 100644 index 00000000..0cd3d1a3 --- /dev/null +++ b/ib_async/protobuf/NewsBulletin_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: NewsBulletin.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'NewsBulletin.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12NewsBulletin.proto\x12\x08protobuf\"\xba\x01\n\x0cNewsBulletin\x12\x16\n\tnewsMsgId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x18\n\x0bnewsMsgType\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x18\n\x0bnewsMessage\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1c\n\x0foriginatingExch\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x0c\n\n_newsMsgIdB\x0e\n\x0c_newsMsgTypeB\x0e\n\x0c_newsMessageB\x12\n\x10_originatingExchB<\n\x16\x63om.ib.client.protobufB\x11NewsBulletinProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'NewsBulletin_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\021NewsBulletinProto\252\002\016IBApi.protobuf' + _globals['_NEWSBULLETIN']._serialized_start=33 + _globals['_NEWSBULLETIN']._serialized_end=219 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/NewsBulletin_pb2.pyi b/ib_async/protobuf/NewsBulletin_pb2.pyi new file mode 100644 index 00000000..ae21f681 --- /dev/null +++ b/ib_async/protobuf/NewsBulletin_pb2.pyi @@ -0,0 +1,17 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class NewsBulletin(_message.Message): + __slots__ = ("newsMsgId", "newsMsgType", "newsMessage", "originatingExch") + NEWSMSGID_FIELD_NUMBER: _ClassVar[int] + NEWSMSGTYPE_FIELD_NUMBER: _ClassVar[int] + NEWSMESSAGE_FIELD_NUMBER: _ClassVar[int] + ORIGINATINGEXCH_FIELD_NUMBER: _ClassVar[int] + newsMsgId: int + newsMsgType: int + newsMessage: str + originatingExch: str + def __init__(self, newsMsgId: _Optional[int] = ..., newsMsgType: _Optional[int] = ..., newsMessage: _Optional[str] = ..., originatingExch: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/NewsBulletinsRequest_pb2.py b/ib_async/protobuf/NewsBulletinsRequest_pb2.py new file mode 100644 index 00000000..2b283253 --- /dev/null +++ b/ib_async/protobuf/NewsBulletinsRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: NewsBulletinsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'NewsBulletinsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aNewsBulletinsRequest.proto\x12\x08protobuf\"@\n\x14NewsBulletinsRequest\x12\x18\n\x0b\x61llMessages\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x0e\n\x0c_allMessagesBD\n\x16\x63om.ib.client.protobufB\x19NewsBulletinsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'NewsBulletinsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031NewsBulletinsRequestProto\252\002\016IBApi.protobuf' + _globals['_NEWSBULLETINSREQUEST']._serialized_start=40 + _globals['_NEWSBULLETINSREQUEST']._serialized_end=104 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/NewsBulletinsRequest_pb2.pyi b/ib_async/protobuf/NewsBulletinsRequest_pb2.pyi new file mode 100644 index 00000000..6b369a14 --- /dev/null +++ b/ib_async/protobuf/NewsBulletinsRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class NewsBulletinsRequest(_message.Message): + __slots__ = ("allMessages",) + ALLMESSAGES_FIELD_NUMBER: _ClassVar[int] + allMessages: bool + def __init__(self, allMessages: bool = ...) -> None: ... diff --git a/ib_async/protobuf/NewsProvider_pb2.py b/ib_async/protobuf/NewsProvider_pb2.py new file mode 100644 index 00000000..f8bc4ed5 --- /dev/null +++ b/ib_async/protobuf/NewsProvider_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: NewsProvider.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'NewsProvider.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12NewsProvider.proto\x12\x08protobuf\"f\n\x0cNewsProvider\x12\x19\n\x0cproviderCode\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cproviderName\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x0f\n\r_providerCodeB\x0f\n\r_providerNameB<\n\x16\x63om.ib.client.protobufB\x11NewsProviderProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'NewsProvider_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\021NewsProviderProto\252\002\016IBApi.protobuf' + _globals['_NEWSPROVIDER']._serialized_start=32 + _globals['_NEWSPROVIDER']._serialized_end=134 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/NewsProvider_pb2.pyi b/ib_async/protobuf/NewsProvider_pb2.pyi new file mode 100644 index 00000000..755b02a8 --- /dev/null +++ b/ib_async/protobuf/NewsProvider_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class NewsProvider(_message.Message): + __slots__ = ("providerCode", "providerName") + PROVIDERCODE_FIELD_NUMBER: _ClassVar[int] + PROVIDERNAME_FIELD_NUMBER: _ClassVar[int] + providerCode: str + providerName: str + def __init__(self, providerCode: _Optional[str] = ..., providerName: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/NewsProvidersRequest_pb2.py b/ib_async/protobuf/NewsProvidersRequest_pb2.py new file mode 100644 index 00000000..5e03d25f --- /dev/null +++ b/ib_async/protobuf/NewsProvidersRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: NewsProvidersRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'NewsProvidersRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aNewsProvidersRequest.proto\x12\x08protobuf\"\x16\n\x14NewsProvidersRequestBD\n\x16\x63om.ib.client.protobufB\x19NewsProvidersRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'NewsProvidersRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031NewsProvidersRequestProto\252\002\016IBApi.protobuf' + _globals['_NEWSPROVIDERSREQUEST']._serialized_start=40 + _globals['_NEWSPROVIDERSREQUEST']._serialized_end=62 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/NewsProvidersRequest_pb2.pyi b/ib_async/protobuf/NewsProvidersRequest_pb2.pyi new file mode 100644 index 00000000..c6794303 --- /dev/null +++ b/ib_async/protobuf/NewsProvidersRequest_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class NewsProvidersRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/NewsProviders_pb2.py b/ib_async/protobuf/NewsProviders_pb2.py new file mode 100644 index 00000000..b3c60d9f --- /dev/null +++ b/ib_async/protobuf/NewsProviders_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: NewsProviders.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'NewsProviders.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import NewsProvider_pb2 as NewsProvider__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13NewsProviders.proto\x12\x08protobuf\x1a\x12NewsProvider.proto\">\n\rNewsProviders\x12-\n\rnewsProviders\x18\x01 \x03(\x0b\x32\x16.protobuf.NewsProviderB=\n\x16\x63om.ib.client.protobufB\x12NewsProvidersProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'NewsProviders_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\022NewsProvidersProto\252\002\016IBApi.protobuf' + _globals['_NEWSPROVIDERS']._serialized_start=53 + _globals['_NEWSPROVIDERS']._serialized_end=115 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/NewsProviders_pb2.pyi b/ib_async/protobuf/NewsProviders_pb2.pyi new file mode 100644 index 00000000..f2836ae5 --- /dev/null +++ b/ib_async/protobuf/NewsProviders_pb2.pyi @@ -0,0 +1,14 @@ +import NewsProvider_pb2 as _NewsProvider_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class NewsProviders(_message.Message): + __slots__ = ("newsProviders",) + NEWSPROVIDERS_FIELD_NUMBER: _ClassVar[int] + newsProviders: _containers.RepeatedCompositeFieldContainer[_NewsProvider_pb2.NewsProvider] + def __init__(self, newsProviders: _Optional[_Iterable[_Union[_NewsProvider_pb2.NewsProvider, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/NextValidId_pb2.py b/ib_async/protobuf/NextValidId_pb2.py new file mode 100644 index 00000000..8cba65b8 --- /dev/null +++ b/ib_async/protobuf/NextValidId_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: NextValidId.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'NextValidId.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11NextValidId.proto\x12\x08protobuf\"/\n\x0bNextValidId\x12\x14\n\x07orderId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\n\n\x08_orderIdB;\n\x16\x63om.ib.client.protobufB\x10NextValidIdProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'NextValidId_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020NextValidIdProto\252\002\016IBApi.protobuf' + _globals['_NEXTVALIDID']._serialized_start=31 + _globals['_NEXTVALIDID']._serialized_end=78 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/NextValidId_pb2.pyi b/ib_async/protobuf/NextValidId_pb2.pyi new file mode 100644 index 00000000..4704283a --- /dev/null +++ b/ib_async/protobuf/NextValidId_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class NextValidId(_message.Message): + __slots__ = ("orderId",) + ORDERID_FIELD_NUMBER: _ClassVar[int] + orderId: int + def __init__(self, orderId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/OpenOrder_pb2.py b/ib_async/protobuf/OpenOrder_pb2.py new file mode 100644 index 00000000..a1d38a29 --- /dev/null +++ b/ib_async/protobuf/OpenOrder_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: OpenOrder.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'OpenOrder.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 +from . import Order_pb2 as Order__pb2 +from . import OrderState_pb2 as OrderState__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fOpenOrder.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\x1a\x0bOrder.proto\x1a\x10OrderState.proto\"\xd2\x01\n\tOpenOrder\x12\x14\n\x07orderId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12#\n\x05order\x18\x03 \x01(\x0b\x32\x0f.protobuf.OrderH\x02\x88\x01\x01\x12-\n\norderState\x18\x04 \x01(\x0b\x32\x14.protobuf.OrderStateH\x03\x88\x01\x01\x42\n\n\x08_orderIdB\x0b\n\t_contractB\x08\n\x06_orderB\r\n\x0b_orderStateB9\n\x16\x63om.ib.client.protobufB\x0eOpenOrderProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OpenOrder_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\016OpenOrderProto\252\002\016IBApi.protobuf' + _globals['_OPENORDER']._serialized_start=77 + _globals['_OPENORDER']._serialized_end=287 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/OpenOrder_pb2.pyi b/ib_async/protobuf/OpenOrder_pb2.pyi new file mode 100644 index 00000000..32063c7c --- /dev/null +++ b/ib_async/protobuf/OpenOrder_pb2.pyi @@ -0,0 +1,21 @@ +import Contract_pb2 as _Contract_pb2 +import Order_pb2 as _Order_pb2 +import OrderState_pb2 as _OrderState_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class OpenOrder(_message.Message): + __slots__ = ("orderId", "contract", "order", "orderState") + ORDERID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + ORDER_FIELD_NUMBER: _ClassVar[int] + ORDERSTATE_FIELD_NUMBER: _ClassVar[int] + orderId: int + contract: _Contract_pb2.Contract + order: _Order_pb2.Order + orderState: _OrderState_pb2.OrderState + def __init__(self, orderId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., order: _Optional[_Union[_Order_pb2.Order, _Mapping]] = ..., orderState: _Optional[_Union[_OrderState_pb2.OrderState, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/OpenOrdersEnd_pb2.py b/ib_async/protobuf/OpenOrdersEnd_pb2.py new file mode 100644 index 00000000..ad168a71 --- /dev/null +++ b/ib_async/protobuf/OpenOrdersEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: OpenOrdersEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'OpenOrdersEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13OpenOrdersEnd.proto\x12\x08protobuf\"\x0f\n\rOpenOrdersEndB=\n\x16\x63om.ib.client.protobufB\x12OpenOrdersEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OpenOrdersEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\022OpenOrdersEndProto\252\002\016IBApi.protobuf' + _globals['_OPENORDERSEND']._serialized_start=33 + _globals['_OPENORDERSEND']._serialized_end=48 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/OpenOrdersEnd_pb2.pyi b/ib_async/protobuf/OpenOrdersEnd_pb2.pyi new file mode 100644 index 00000000..adc1f414 --- /dev/null +++ b/ib_async/protobuf/OpenOrdersEnd_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class OpenOrdersEnd(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/OpenOrdersRequest_pb2.py b/ib_async/protobuf/OpenOrdersRequest_pb2.py new file mode 100644 index 00000000..5bec189a --- /dev/null +++ b/ib_async/protobuf/OpenOrdersRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: OpenOrdersRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'OpenOrdersRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17OpenOrdersRequest.proto\x12\x08protobuf\"\x13\n\x11OpenOrdersRequestBA\n\x16\x63om.ib.client.protobufB\x16OpenOrdersRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OpenOrdersRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026OpenOrdersRequestProto\252\002\016IBApi.protobuf' + _globals['_OPENORDERSREQUEST']._serialized_start=37 + _globals['_OPENORDERSREQUEST']._serialized_end=56 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/OpenOrdersRequest_pb2.pyi b/ib_async/protobuf/OpenOrdersRequest_pb2.pyi new file mode 100644 index 00000000..d390b88e --- /dev/null +++ b/ib_async/protobuf/OpenOrdersRequest_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class OpenOrdersRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/OrderAllocation_pb2.py b/ib_async/protobuf/OrderAllocation_pb2.py new file mode 100644 index 00000000..5ba31c17 --- /dev/null +++ b/ib_async/protobuf/OrderAllocation_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: OrderAllocation.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'OrderAllocation.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15OrderAllocation.proto\x12\x08protobuf\"\xc3\x02\n\x0fOrderAllocation\x12\x14\n\x07\x61\x63\x63ount\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08position\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0fpositionDesired\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rpositionAfter\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x1c\n\x0f\x64\x65siredAllocQty\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x1c\n\x0f\x61llowedAllocQty\x18\x06 \x01(\tH\x05\x88\x01\x01\x12\x17\n\nisMonetary\x18\x07 \x01(\x08H\x06\x88\x01\x01\x42\n\n\x08_accountB\x0b\n\t_positionB\x12\n\x10_positionDesiredB\x10\n\x0e_positionAfterB\x12\n\x10_desiredAllocQtyB\x12\n\x10_allowedAllocQtyB\r\n\x0b_isMonetaryB?\n\x16\x63om.ib.client.protobufB\x14OrderAllocationProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OrderAllocation_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024OrderAllocationProto\252\002\016IBApi.protobuf' + _globals['_ORDERALLOCATION']._serialized_start=36 + _globals['_ORDERALLOCATION']._serialized_end=359 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/OrderAllocation_pb2.pyi b/ib_async/protobuf/OrderAllocation_pb2.pyi new file mode 100644 index 00000000..876bff2e --- /dev/null +++ b/ib_async/protobuf/OrderAllocation_pb2.pyi @@ -0,0 +1,23 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class OrderAllocation(_message.Message): + __slots__ = ("account", "position", "positionDesired", "positionAfter", "desiredAllocQty", "allowedAllocQty", "isMonetary") + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + POSITION_FIELD_NUMBER: _ClassVar[int] + POSITIONDESIRED_FIELD_NUMBER: _ClassVar[int] + POSITIONAFTER_FIELD_NUMBER: _ClassVar[int] + DESIREDALLOCQTY_FIELD_NUMBER: _ClassVar[int] + ALLOWEDALLOCQTY_FIELD_NUMBER: _ClassVar[int] + ISMONETARY_FIELD_NUMBER: _ClassVar[int] + account: str + position: str + positionDesired: str + positionAfter: str + desiredAllocQty: str + allowedAllocQty: str + isMonetary: bool + def __init__(self, account: _Optional[str] = ..., position: _Optional[str] = ..., positionDesired: _Optional[str] = ..., positionAfter: _Optional[str] = ..., desiredAllocQty: _Optional[str] = ..., allowedAllocQty: _Optional[str] = ..., isMonetary: bool = ...) -> None: ... diff --git a/ib_async/protobuf/OrderBound_pb2.py b/ib_async/protobuf/OrderBound_pb2.py new file mode 100644 index 00000000..5cac21ba --- /dev/null +++ b/ib_async/protobuf/OrderBound_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: OrderBound.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'OrderBound.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10OrderBound.proto\x12\x08protobuf\"r\n\nOrderBound\x12\x13\n\x06permId\x18\x01 \x01(\x03H\x00\x88\x01\x01\x12\x15\n\x08\x63lientId\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x14\n\x07orderId\x18\x03 \x01(\x05H\x02\x88\x01\x01\x42\t\n\x07_permIdB\x0b\n\t_clientIdB\n\n\x08_orderIdB:\n\x16\x63om.ib.client.protobufB\x0fOrderBoundProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OrderBound_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\017OrderBoundProto\252\002\016IBApi.protobuf' + _globals['_ORDERBOUND']._serialized_start=30 + _globals['_ORDERBOUND']._serialized_end=144 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/OrderBound_pb2.pyi b/ib_async/protobuf/OrderBound_pb2.pyi new file mode 100644 index 00000000..e8b71fe6 --- /dev/null +++ b/ib_async/protobuf/OrderBound_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class OrderBound(_message.Message): + __slots__ = ("permId", "clientId", "orderId") + PERMID_FIELD_NUMBER: _ClassVar[int] + CLIENTID_FIELD_NUMBER: _ClassVar[int] + ORDERID_FIELD_NUMBER: _ClassVar[int] + permId: int + clientId: int + orderId: int + def __init__(self, permId: _Optional[int] = ..., clientId: _Optional[int] = ..., orderId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/OrderCancel_pb2.py b/ib_async/protobuf/OrderCancel_pb2.py new file mode 100644 index 00000000..35c53edc --- /dev/null +++ b/ib_async/protobuf/OrderCancel_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: OrderCancel.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'OrderCancel.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11OrderCancel.proto\x12\x08protobuf\"\xb1\x01\n\x0bOrderCancel\x12\"\n\x15manualOrderCancelTime\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x65xtOperator\x18\x02 \x01(\tH\x01\x88\x01\x01\x12!\n\x14manualOrderIndicator\x18\x03 \x01(\x05H\x02\x88\x01\x01\x42\x18\n\x16_manualOrderCancelTimeB\x0e\n\x0c_extOperatorB\x17\n\x15_manualOrderIndicatorB;\n\x16\x63om.ib.client.protobufB\x10OrderCancelProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OrderCancel_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020OrderCancelProto\252\002\016IBApi.protobuf' + _globals['_ORDERCANCEL']._serialized_start=32 + _globals['_ORDERCANCEL']._serialized_end=209 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/OrderCancel_pb2.pyi b/ib_async/protobuf/OrderCancel_pb2.pyi new file mode 100644 index 00000000..778fa56d --- /dev/null +++ b/ib_async/protobuf/OrderCancel_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class OrderCancel(_message.Message): + __slots__ = ("manualOrderCancelTime", "extOperator", "manualOrderIndicator") + MANUALORDERCANCELTIME_FIELD_NUMBER: _ClassVar[int] + EXTOPERATOR_FIELD_NUMBER: _ClassVar[int] + MANUALORDERINDICATOR_FIELD_NUMBER: _ClassVar[int] + manualOrderCancelTime: str + extOperator: str + manualOrderIndicator: int + def __init__(self, manualOrderCancelTime: _Optional[str] = ..., extOperator: _Optional[str] = ..., manualOrderIndicator: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/OrderCondition_pb2.py b/ib_async/protobuf/OrderCondition_pb2.py new file mode 100644 index 00000000..ad39d6ae --- /dev/null +++ b/ib_async/protobuf/OrderCondition_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: OrderCondition.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'OrderCondition.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14OrderCondition.proto\x12\x08protobuf\"\xea\x03\n\x0eOrderCondition\x12\x11\n\x04type\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12$\n\x17isConjunctionConnection\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12\x13\n\x06isMore\x18\x03 \x01(\x08H\x02\x88\x01\x01\x12\x12\n\x05\x63onId\x18\x04 \x01(\x05H\x03\x88\x01\x01\x12\x15\n\x08\x65xchange\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06symbol\x18\x06 \x01(\tH\x05\x88\x01\x01\x12\x14\n\x07secType\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x14\n\x07percent\x18\x08 \x01(\x05H\x07\x88\x01\x01\x12\x1a\n\rchangePercent\x18\t \x01(\x01H\x08\x88\x01\x01\x12\x12\n\x05price\x18\n \x01(\x01H\t\x88\x01\x01\x12\x1a\n\rtriggerMethod\x18\x0b \x01(\x05H\n\x88\x01\x01\x12\x11\n\x04time\x18\x0c \x01(\tH\x0b\x88\x01\x01\x12\x13\n\x06volume\x18\r \x01(\x05H\x0c\x88\x01\x01\x42\x07\n\x05_typeB\x1a\n\x18_isConjunctionConnectionB\t\n\x07_isMoreB\x08\n\x06_conIdB\x0b\n\t_exchangeB\t\n\x07_symbolB\n\n\x08_secTypeB\n\n\x08_percentB\x10\n\x0e_changePercentB\x08\n\x06_priceB\x10\n\x0e_triggerMethodB\x07\n\x05_timeB\t\n\x07_volumeB>\n\x16\x63om.ib.client.protobufB\x13OrderConditionProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OrderCondition_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023OrderConditionProto\252\002\016IBApi.protobuf' + _globals['_ORDERCONDITION']._serialized_start=35 + _globals['_ORDERCONDITION']._serialized_end=525 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/OrderCondition_pb2.pyi b/ib_async/protobuf/OrderCondition_pb2.pyi new file mode 100644 index 00000000..c7ad38be --- /dev/null +++ b/ib_async/protobuf/OrderCondition_pb2.pyi @@ -0,0 +1,35 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class OrderCondition(_message.Message): + __slots__ = ("type", "isConjunctionConnection", "isMore", "conId", "exchange", "symbol", "secType", "percent", "changePercent", "price", "triggerMethod", "time", "volume") + TYPE_FIELD_NUMBER: _ClassVar[int] + ISCONJUNCTIONCONNECTION_FIELD_NUMBER: _ClassVar[int] + ISMORE_FIELD_NUMBER: _ClassVar[int] + CONID_FIELD_NUMBER: _ClassVar[int] + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + SYMBOL_FIELD_NUMBER: _ClassVar[int] + SECTYPE_FIELD_NUMBER: _ClassVar[int] + PERCENT_FIELD_NUMBER: _ClassVar[int] + CHANGEPERCENT_FIELD_NUMBER: _ClassVar[int] + PRICE_FIELD_NUMBER: _ClassVar[int] + TRIGGERMETHOD_FIELD_NUMBER: _ClassVar[int] + TIME_FIELD_NUMBER: _ClassVar[int] + VOLUME_FIELD_NUMBER: _ClassVar[int] + type: int + isConjunctionConnection: bool + isMore: bool + conId: int + exchange: str + symbol: str + secType: str + percent: int + changePercent: float + price: float + triggerMethod: int + time: str + volume: int + def __init__(self, type: _Optional[int] = ..., isConjunctionConnection: bool = ..., isMore: bool = ..., conId: _Optional[int] = ..., exchange: _Optional[str] = ..., symbol: _Optional[str] = ..., secType: _Optional[str] = ..., percent: _Optional[int] = ..., changePercent: _Optional[float] = ..., price: _Optional[float] = ..., triggerMethod: _Optional[int] = ..., time: _Optional[str] = ..., volume: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/OrderState_pb2.py b/ib_async/protobuf/OrderState_pb2.py new file mode 100644 index 00000000..d722ef0d --- /dev/null +++ b/ib_async/protobuf/OrderState_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: OrderState.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'OrderState.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import OrderAllocation_pb2 as OrderAllocation__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10OrderState.proto\x12\x08protobuf\x1a\x15OrderAllocation.proto\"\xf5\r\n\nOrderState\x12\x13\n\x06status\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x1d\n\x10initMarginBefore\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x1e\n\x11maintMarginBefore\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12!\n\x14\x65quityWithLoanBefore\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x1d\n\x10initMarginChange\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x1e\n\x11maintMarginChange\x18\x06 \x01(\x01H\x05\x88\x01\x01\x12!\n\x14\x65quityWithLoanChange\x18\x07 \x01(\x01H\x06\x88\x01\x01\x12\x1c\n\x0finitMarginAfter\x18\x08 \x01(\x01H\x07\x88\x01\x01\x12\x1d\n\x10maintMarginAfter\x18\t \x01(\x01H\x08\x88\x01\x01\x12 \n\x13\x65quityWithLoanAfter\x18\n \x01(\x01H\t\x88\x01\x01\x12\x1e\n\x11\x63ommissionAndFees\x18\x0b \x01(\x01H\n\x88\x01\x01\x12!\n\x14minCommissionAndFees\x18\x0c \x01(\x01H\x0b\x88\x01\x01\x12!\n\x14maxCommissionAndFees\x18\r \x01(\x01H\x0c\x88\x01\x01\x12&\n\x19\x63ommissionAndFeesCurrency\x18\x0e \x01(\tH\r\x88\x01\x01\x12\x1b\n\x0emarginCurrency\x18\x0f \x01(\tH\x0e\x88\x01\x01\x12\'\n\x1ainitMarginBeforeOutsideRTH\x18\x10 \x01(\x01H\x0f\x88\x01\x01\x12(\n\x1bmaintMarginBeforeOutsideRTH\x18\x11 \x01(\x01H\x10\x88\x01\x01\x12+\n\x1e\x65quityWithLoanBeforeOutsideRTH\x18\x12 \x01(\x01H\x11\x88\x01\x01\x12\'\n\x1ainitMarginChangeOutsideRTH\x18\x13 \x01(\x01H\x12\x88\x01\x01\x12(\n\x1bmaintMarginChangeOutsideRTH\x18\x14 \x01(\x01H\x13\x88\x01\x01\x12+\n\x1e\x65quityWithLoanChangeOutsideRTH\x18\x15 \x01(\x01H\x14\x88\x01\x01\x12&\n\x19initMarginAfterOutsideRTH\x18\x16 \x01(\x01H\x15\x88\x01\x01\x12\'\n\x1amaintMarginAfterOutsideRTH\x18\x17 \x01(\x01H\x16\x88\x01\x01\x12*\n\x1d\x65quityWithLoanAfterOutsideRTH\x18\x18 \x01(\x01H\x17\x88\x01\x01\x12\x1a\n\rsuggestedSize\x18\x19 \x01(\tH\x18\x88\x01\x01\x12\x19\n\x0crejectReason\x18\x1a \x01(\tH\x19\x88\x01\x01\x12\x33\n\x10orderAllocations\x18\x1b \x03(\x0b\x32\x19.protobuf.OrderAllocation\x12\x18\n\x0bwarningText\x18\x1c \x01(\tH\x1a\x88\x01\x01\x12\x1a\n\rcompletedTime\x18\x1d \x01(\tH\x1b\x88\x01\x01\x12\x1c\n\x0f\x63ompletedStatus\x18\x1e \x01(\tH\x1c\x88\x01\x01\x42\t\n\x07_statusB\x13\n\x11_initMarginBeforeB\x14\n\x12_maintMarginBeforeB\x17\n\x15_equityWithLoanBeforeB\x13\n\x11_initMarginChangeB\x14\n\x12_maintMarginChangeB\x17\n\x15_equityWithLoanChangeB\x12\n\x10_initMarginAfterB\x13\n\x11_maintMarginAfterB\x16\n\x14_equityWithLoanAfterB\x14\n\x12_commissionAndFeesB\x17\n\x15_minCommissionAndFeesB\x17\n\x15_maxCommissionAndFeesB\x1c\n\x1a_commissionAndFeesCurrencyB\x11\n\x0f_marginCurrencyB\x1d\n\x1b_initMarginBeforeOutsideRTHB\x1e\n\x1c_maintMarginBeforeOutsideRTHB!\n\x1f_equityWithLoanBeforeOutsideRTHB\x1d\n\x1b_initMarginChangeOutsideRTHB\x1e\n\x1c_maintMarginChangeOutsideRTHB!\n\x1f_equityWithLoanChangeOutsideRTHB\x1c\n\x1a_initMarginAfterOutsideRTHB\x1d\n\x1b_maintMarginAfterOutsideRTHB \n\x1e_equityWithLoanAfterOutsideRTHB\x10\n\x0e_suggestedSizeB\x0f\n\r_rejectReasonB\x0e\n\x0c_warningTextB\x10\n\x0e_completedTimeB\x12\n\x10_completedStatusB:\n\x16\x63om.ib.client.protobufB\x0fOrderStateProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OrderState_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\017OrderStateProto\252\002\016IBApi.protobuf' + _globals['_ORDERSTATE']._serialized_start=54 + _globals['_ORDERSTATE']._serialized_end=1835 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/OrderState_pb2.pyi b/ib_async/protobuf/OrderState_pb2.pyi new file mode 100644 index 00000000..61a3f409 --- /dev/null +++ b/ib_async/protobuf/OrderState_pb2.pyi @@ -0,0 +1,72 @@ +import OrderAllocation_pb2 as _OrderAllocation_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class OrderState(_message.Message): + __slots__ = ("status", "initMarginBefore", "maintMarginBefore", "equityWithLoanBefore", "initMarginChange", "maintMarginChange", "equityWithLoanChange", "initMarginAfter", "maintMarginAfter", "equityWithLoanAfter", "commissionAndFees", "minCommissionAndFees", "maxCommissionAndFees", "commissionAndFeesCurrency", "marginCurrency", "initMarginBeforeOutsideRTH", "maintMarginBeforeOutsideRTH", "equityWithLoanBeforeOutsideRTH", "initMarginChangeOutsideRTH", "maintMarginChangeOutsideRTH", "equityWithLoanChangeOutsideRTH", "initMarginAfterOutsideRTH", "maintMarginAfterOutsideRTH", "equityWithLoanAfterOutsideRTH", "suggestedSize", "rejectReason", "orderAllocations", "warningText", "completedTime", "completedStatus") + STATUS_FIELD_NUMBER: _ClassVar[int] + INITMARGINBEFORE_FIELD_NUMBER: _ClassVar[int] + MAINTMARGINBEFORE_FIELD_NUMBER: _ClassVar[int] + EQUITYWITHLOANBEFORE_FIELD_NUMBER: _ClassVar[int] + INITMARGINCHANGE_FIELD_NUMBER: _ClassVar[int] + MAINTMARGINCHANGE_FIELD_NUMBER: _ClassVar[int] + EQUITYWITHLOANCHANGE_FIELD_NUMBER: _ClassVar[int] + INITMARGINAFTER_FIELD_NUMBER: _ClassVar[int] + MAINTMARGINAFTER_FIELD_NUMBER: _ClassVar[int] + EQUITYWITHLOANAFTER_FIELD_NUMBER: _ClassVar[int] + COMMISSIONANDFEES_FIELD_NUMBER: _ClassVar[int] + MINCOMMISSIONANDFEES_FIELD_NUMBER: _ClassVar[int] + MAXCOMMISSIONANDFEES_FIELD_NUMBER: _ClassVar[int] + COMMISSIONANDFEESCURRENCY_FIELD_NUMBER: _ClassVar[int] + MARGINCURRENCY_FIELD_NUMBER: _ClassVar[int] + INITMARGINBEFOREOUTSIDERTH_FIELD_NUMBER: _ClassVar[int] + MAINTMARGINBEFOREOUTSIDERTH_FIELD_NUMBER: _ClassVar[int] + EQUITYWITHLOANBEFOREOUTSIDERTH_FIELD_NUMBER: _ClassVar[int] + INITMARGINCHANGEOUTSIDERTH_FIELD_NUMBER: _ClassVar[int] + MAINTMARGINCHANGEOUTSIDERTH_FIELD_NUMBER: _ClassVar[int] + EQUITYWITHLOANCHANGEOUTSIDERTH_FIELD_NUMBER: _ClassVar[int] + INITMARGINAFTEROUTSIDERTH_FIELD_NUMBER: _ClassVar[int] + MAINTMARGINAFTEROUTSIDERTH_FIELD_NUMBER: _ClassVar[int] + EQUITYWITHLOANAFTEROUTSIDERTH_FIELD_NUMBER: _ClassVar[int] + SUGGESTEDSIZE_FIELD_NUMBER: _ClassVar[int] + REJECTREASON_FIELD_NUMBER: _ClassVar[int] + ORDERALLOCATIONS_FIELD_NUMBER: _ClassVar[int] + WARNINGTEXT_FIELD_NUMBER: _ClassVar[int] + COMPLETEDTIME_FIELD_NUMBER: _ClassVar[int] + COMPLETEDSTATUS_FIELD_NUMBER: _ClassVar[int] + status: str + initMarginBefore: float + maintMarginBefore: float + equityWithLoanBefore: float + initMarginChange: float + maintMarginChange: float + equityWithLoanChange: float + initMarginAfter: float + maintMarginAfter: float + equityWithLoanAfter: float + commissionAndFees: float + minCommissionAndFees: float + maxCommissionAndFees: float + commissionAndFeesCurrency: str + marginCurrency: str + initMarginBeforeOutsideRTH: float + maintMarginBeforeOutsideRTH: float + equityWithLoanBeforeOutsideRTH: float + initMarginChangeOutsideRTH: float + maintMarginChangeOutsideRTH: float + equityWithLoanChangeOutsideRTH: float + initMarginAfterOutsideRTH: float + maintMarginAfterOutsideRTH: float + equityWithLoanAfterOutsideRTH: float + suggestedSize: str + rejectReason: str + orderAllocations: _containers.RepeatedCompositeFieldContainer[_OrderAllocation_pb2.OrderAllocation] + warningText: str + completedTime: str + completedStatus: str + def __init__(self, status: _Optional[str] = ..., initMarginBefore: _Optional[float] = ..., maintMarginBefore: _Optional[float] = ..., equityWithLoanBefore: _Optional[float] = ..., initMarginChange: _Optional[float] = ..., maintMarginChange: _Optional[float] = ..., equityWithLoanChange: _Optional[float] = ..., initMarginAfter: _Optional[float] = ..., maintMarginAfter: _Optional[float] = ..., equityWithLoanAfter: _Optional[float] = ..., commissionAndFees: _Optional[float] = ..., minCommissionAndFees: _Optional[float] = ..., maxCommissionAndFees: _Optional[float] = ..., commissionAndFeesCurrency: _Optional[str] = ..., marginCurrency: _Optional[str] = ..., initMarginBeforeOutsideRTH: _Optional[float] = ..., maintMarginBeforeOutsideRTH: _Optional[float] = ..., equityWithLoanBeforeOutsideRTH: _Optional[float] = ..., initMarginChangeOutsideRTH: _Optional[float] = ..., maintMarginChangeOutsideRTH: _Optional[float] = ..., equityWithLoanChangeOutsideRTH: _Optional[float] = ..., initMarginAfterOutsideRTH: _Optional[float] = ..., maintMarginAfterOutsideRTH: _Optional[float] = ..., equityWithLoanAfterOutsideRTH: _Optional[float] = ..., suggestedSize: _Optional[str] = ..., rejectReason: _Optional[str] = ..., orderAllocations: _Optional[_Iterable[_Union[_OrderAllocation_pb2.OrderAllocation, _Mapping]]] = ..., warningText: _Optional[str] = ..., completedTime: _Optional[str] = ..., completedStatus: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/OrderStatus_pb2.py b/ib_async/protobuf/OrderStatus_pb2.py new file mode 100644 index 00000000..e48a6748 --- /dev/null +++ b/ib_async/protobuf/OrderStatus_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: OrderStatus.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'OrderStatus.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11OrderStatus.proto\x12\x08protobuf\"\xa3\x03\n\x0bOrderStatus\x12\x14\n\x07orderId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x66illed\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tremaining\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x19\n\x0c\x61vgFillPrice\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x13\n\x06permId\x18\x06 \x01(\x03H\x05\x88\x01\x01\x12\x15\n\x08parentId\x18\x07 \x01(\x05H\x06\x88\x01\x01\x12\x1a\n\rlastFillPrice\x18\x08 \x01(\x01H\x07\x88\x01\x01\x12\x15\n\x08\x63lientId\x18\t \x01(\x05H\x08\x88\x01\x01\x12\x14\n\x07whyHeld\x18\n \x01(\tH\t\x88\x01\x01\x12\x18\n\x0bmktCapPrice\x18\x0b \x01(\x01H\n\x88\x01\x01\x42\n\n\x08_orderIdB\t\n\x07_statusB\t\n\x07_filledB\x0c\n\n_remainingB\x0f\n\r_avgFillPriceB\t\n\x07_permIdB\x0b\n\t_parentIdB\x10\n\x0e_lastFillPriceB\x0b\n\t_clientIdB\n\n\x08_whyHeldB\x0e\n\x0c_mktCapPriceB;\n\x16\x63om.ib.client.protobufB\x10OrderStatusProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OrderStatus_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020OrderStatusProto\252\002\016IBApi.protobuf' + _globals['_ORDERSTATUS']._serialized_start=32 + _globals['_ORDERSTATUS']._serialized_end=451 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/OrderStatus_pb2.pyi b/ib_async/protobuf/OrderStatus_pb2.pyi new file mode 100644 index 00000000..4263ca64 --- /dev/null +++ b/ib_async/protobuf/OrderStatus_pb2.pyi @@ -0,0 +1,31 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class OrderStatus(_message.Message): + __slots__ = ("orderId", "status", "filled", "remaining", "avgFillPrice", "permId", "parentId", "lastFillPrice", "clientId", "whyHeld", "mktCapPrice") + ORDERID_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + FILLED_FIELD_NUMBER: _ClassVar[int] + REMAINING_FIELD_NUMBER: _ClassVar[int] + AVGFILLPRICE_FIELD_NUMBER: _ClassVar[int] + PERMID_FIELD_NUMBER: _ClassVar[int] + PARENTID_FIELD_NUMBER: _ClassVar[int] + LASTFILLPRICE_FIELD_NUMBER: _ClassVar[int] + CLIENTID_FIELD_NUMBER: _ClassVar[int] + WHYHELD_FIELD_NUMBER: _ClassVar[int] + MKTCAPPRICE_FIELD_NUMBER: _ClassVar[int] + orderId: int + status: str + filled: str + remaining: str + avgFillPrice: float + permId: int + parentId: int + lastFillPrice: float + clientId: int + whyHeld: str + mktCapPrice: float + def __init__(self, orderId: _Optional[int] = ..., status: _Optional[str] = ..., filled: _Optional[str] = ..., remaining: _Optional[str] = ..., avgFillPrice: _Optional[float] = ..., permId: _Optional[int] = ..., parentId: _Optional[int] = ..., lastFillPrice: _Optional[float] = ..., clientId: _Optional[int] = ..., whyHeld: _Optional[str] = ..., mktCapPrice: _Optional[float] = ...) -> None: ... diff --git a/ib_async/protobuf/Order_pb2.py b/ib_async/protobuf/Order_pb2.py new file mode 100644 index 00000000..26fe65d9 --- /dev/null +++ b/ib_async/protobuf/Order_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: Order.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'Order.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import OrderCondition_pb2 as OrderCondition__pb2 +from . import SoftDollarTier_pb2 as SoftDollarTier__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0bOrder.proto\x12\x08protobuf\x1a\x14OrderCondition.proto\x1a\x14SoftDollarTier.proto\"\xbf\x36\n\x05Order\x12\x15\n\x08\x63lientId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07orderId\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x13\n\x06permId\x18\x03 \x01(\x03H\x02\x88\x01\x01\x12\x15\n\x08parentId\x18\x04 \x01(\x05H\x03\x88\x01\x01\x12\x13\n\x06\x61\x63tion\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x1a\n\rtotalQuantity\x18\x06 \x01(\tH\x05\x88\x01\x01\x12\x18\n\x0b\x64isplaySize\x18\x07 \x01(\x05H\x06\x88\x01\x01\x12\x16\n\torderType\x18\x08 \x01(\tH\x07\x88\x01\x01\x12\x15\n\x08lmtPrice\x18\t \x01(\x01H\x08\x88\x01\x01\x12\x15\n\x08\x61uxPrice\x18\n \x01(\x01H\t\x88\x01\x01\x12\x10\n\x03tif\x18\x0b \x01(\tH\n\x88\x01\x01\x12\x14\n\x07\x61\x63\x63ount\x18\x0c \x01(\tH\x0b\x88\x01\x01\x12\x19\n\x0csettlingFirm\x18\r \x01(\tH\x0c\x88\x01\x01\x12\x1c\n\x0f\x63learingAccount\x18\x0e \x01(\tH\r\x88\x01\x01\x12\x1b\n\x0e\x63learingIntent\x18\x0f \x01(\tH\x0e\x88\x01\x01\x12\x16\n\tallOrNone\x18\x10 \x01(\x08H\x0f\x88\x01\x01\x12\x17\n\nblockOrder\x18\x11 \x01(\x08H\x10\x88\x01\x01\x12\x13\n\x06hidden\x18\x12 \x01(\x08H\x11\x88\x01\x01\x12\x17\n\noutsideRth\x18\x13 \x01(\x08H\x12\x88\x01\x01\x12\x18\n\x0bsweepToFill\x18\x14 \x01(\x08H\x13\x88\x01\x01\x12\x1a\n\rpercentOffset\x18\x15 \x01(\x01H\x14\x88\x01\x01\x12\x1c\n\x0ftrailingPercent\x18\x16 \x01(\x01H\x15\x88\x01\x01\x12\x1b\n\x0etrailStopPrice\x18\x17 \x01(\x01H\x16\x88\x01\x01\x12\x13\n\x06minQty\x18\x18 \x01(\x05H\x17\x88\x01\x01\x12\x1a\n\rgoodAfterTime\x18\x19 \x01(\tH\x18\x88\x01\x01\x12\x19\n\x0cgoodTillDate\x18\x1a \x01(\tH\x19\x88\x01\x01\x12\x15\n\x08ocaGroup\x18\x1b \x01(\tH\x1a\x88\x01\x01\x12\x15\n\x08orderRef\x18\x1c \x01(\tH\x1b\x88\x01\x01\x12\x14\n\x07rule80A\x18\x1d \x01(\tH\x1c\x88\x01\x01\x12\x14\n\x07ocaType\x18\x1e \x01(\x05H\x1d\x88\x01\x01\x12\x1a\n\rtriggerMethod\x18\x1f \x01(\x05H\x1e\x88\x01\x01\x12\x1c\n\x0f\x61\x63tiveStartTime\x18 \x01(\tH\x1f\x88\x01\x01\x12\x1b\n\x0e\x61\x63tiveStopTime\x18! \x01(\tH \x88\x01\x01\x12\x14\n\x07\x66\x61Group\x18\" \x01(\tH!\x88\x01\x01\x12\x15\n\x08\x66\x61Method\x18# \x01(\tH\"\x88\x01\x01\x12\x19\n\x0c\x66\x61Percentage\x18$ \x01(\tH#\x88\x01\x01\x12\x17\n\nvolatility\x18% \x01(\x01H$\x88\x01\x01\x12\x1b\n\x0evolatilityType\x18& \x01(\x05H%\x88\x01\x01\x12\x1d\n\x10\x63ontinuousUpdate\x18\' \x01(\x08H&\x88\x01\x01\x12\x1f\n\x12referencePriceType\x18( \x01(\x05H\'\x88\x01\x01\x12\"\n\x15\x64\x65ltaNeutralOrderType\x18) \x01(\tH(\x88\x01\x01\x12!\n\x14\x64\x65ltaNeutralAuxPrice\x18* \x01(\x01H)\x88\x01\x01\x12\x1e\n\x11\x64\x65ltaNeutralConId\x18+ \x01(\x05H*\x88\x01\x01\x12\"\n\x15\x64\x65ltaNeutralOpenClose\x18, \x01(\tH+\x88\x01\x01\x12\"\n\x15\x64\x65ltaNeutralShortSale\x18- \x01(\x08H,\x88\x01\x01\x12&\n\x19\x64\x65ltaNeutralShortSaleSlot\x18. \x01(\x05H-\x88\x01\x01\x12+\n\x1e\x64\x65ltaNeutralDesignatedLocation\x18/ \x01(\tH.\x88\x01\x01\x12\x1f\n\x12scaleInitLevelSize\x18\x30 \x01(\x05H/\x88\x01\x01\x12\x1f\n\x12scaleSubsLevelSize\x18\x31 \x01(\x05H0\x88\x01\x01\x12 \n\x13scalePriceIncrement\x18\x32 \x01(\x01H1\x88\x01\x01\x12\"\n\x15scalePriceAdjustValue\x18\x33 \x01(\x01H2\x88\x01\x01\x12%\n\x18scalePriceAdjustInterval\x18\x34 \x01(\x05H3\x88\x01\x01\x12\x1e\n\x11scaleProfitOffset\x18\x35 \x01(\x01H4\x88\x01\x01\x12\x1b\n\x0escaleAutoReset\x18\x36 \x01(\x08H5\x88\x01\x01\x12\x1e\n\x11scaleInitPosition\x18\x37 \x01(\x05H6\x88\x01\x01\x12\x1d\n\x10scaleInitFillQty\x18\x38 \x01(\x05H7\x88\x01\x01\x12\x1f\n\x12scaleRandomPercent\x18\x39 \x01(\x08H8\x88\x01\x01\x12\x17\n\nscaleTable\x18: \x01(\tH9\x88\x01\x01\x12\x16\n\thedgeType\x18; \x01(\tH:\x88\x01\x01\x12\x17\n\nhedgeParam\x18< \x01(\tH;\x88\x01\x01\x12\x19\n\x0c\x61lgoStrategy\x18= \x01(\tH<\x88\x01\x01\x12\x33\n\nalgoParams\x18> \x03(\x0b\x32\x1f.protobuf.Order.AlgoParamsEntry\x12\x13\n\x06\x61lgoId\x18? \x01(\tH=\x88\x01\x01\x12M\n\x17smartComboRoutingParams\x18@ \x03(\x0b\x32,.protobuf.Order.SmartComboRoutingParamsEntry\x12\x13\n\x06whatIf\x18\x41 \x01(\x08H>\x88\x01\x01\x12\x15\n\x08transmit\x18\x42 \x01(\x08H?\x88\x01\x01\x12*\n\x1doverridePercentageConstraints\x18\x43 \x01(\x08H@\x88\x01\x01\x12\x16\n\topenClose\x18\x44 \x01(\tHA\x88\x01\x01\x12\x13\n\x06origin\x18\x45 \x01(\x05HB\x88\x01\x01\x12\x1a\n\rshortSaleSlot\x18\x46 \x01(\x05HC\x88\x01\x01\x12\x1f\n\x12\x64\x65signatedLocation\x18G \x01(\tHD\x88\x01\x01\x12\x17\n\nexemptCode\x18H \x01(\x05HE\x88\x01\x01\x12%\n\x18\x64\x65ltaNeutralSettlingFirm\x18I \x01(\tHF\x88\x01\x01\x12(\n\x1b\x64\x65ltaNeutralClearingAccount\x18J \x01(\tHG\x88\x01\x01\x12\'\n\x1a\x64\x65ltaNeutralClearingIntent\x18K \x01(\tHH\x88\x01\x01\x12\x1d\n\x10\x64iscretionaryAmt\x18L \x01(\x01HI\x88\x01\x01\x12\x1f\n\x12optOutSmartRouting\x18M \x01(\x08HJ\x88\x01\x01\x12\x1a\n\rstartingPrice\x18N \x01(\x01HK\x88\x01\x01\x12\x1a\n\rstockRefPrice\x18O \x01(\x01HL\x88\x01\x01\x12\x12\n\x05\x64\x65lta\x18P \x01(\x01HM\x88\x01\x01\x12\x1c\n\x0fstockRangeLower\x18Q \x01(\x01HN\x88\x01\x01\x12\x1c\n\x0fstockRangeUpper\x18R \x01(\x01HO\x88\x01\x01\x12\x14\n\x07notHeld\x18S \x01(\x08HP\x88\x01\x01\x12?\n\x10orderMiscOptions\x18T \x03(\x0b\x32%.protobuf.Order.OrderMiscOptionsEntry\x12\x16\n\tsolicited\x18U \x01(\x08HQ\x88\x01\x01\x12\x1a\n\rrandomizeSize\x18V \x01(\x08HR\x88\x01\x01\x12\x1b\n\x0erandomizePrice\x18W \x01(\x08HS\x88\x01\x01\x12 \n\x13referenceContractId\x18X \x01(\x05HT\x88\x01\x01\x12\x1f\n\x12peggedChangeAmount\x18Y \x01(\x01HU\x88\x01\x01\x12)\n\x1cisPeggedChangeAmountDecrease\x18Z \x01(\x08HV\x88\x01\x01\x12\"\n\x15referenceChangeAmount\x18[ \x01(\x01HW\x88\x01\x01\x12 \n\x13referenceExchangeId\x18\\ \x01(\tHX\x88\x01\x01\x12\x1e\n\x11\x61\x64justedOrderType\x18] \x01(\tHY\x88\x01\x01\x12\x19\n\x0ctriggerPrice\x18^ \x01(\x01HZ\x88\x01\x01\x12\x1e\n\x11\x61\x64justedStopPrice\x18_ \x01(\x01H[\x88\x01\x01\x12#\n\x16\x61\x64justedStopLimitPrice\x18` \x01(\x01H\\\x88\x01\x01\x12#\n\x16\x61\x64justedTrailingAmount\x18\x61 \x01(\x01H]\x88\x01\x01\x12#\n\x16\x61\x64justableTrailingUnit\x18\x62 \x01(\x05H^\x88\x01\x01\x12\x1b\n\x0elmtPriceOffset\x18\x63 \x01(\x01H_\x88\x01\x01\x12,\n\nconditions\x18\x64 \x03(\x0b\x32\x18.protobuf.OrderCondition\x12\"\n\x15\x63onditionsCancelOrder\x18\x65 \x01(\x08H`\x88\x01\x01\x12 \n\x13\x63onditionsIgnoreRth\x18\x66 \x01(\x08Ha\x88\x01\x01\x12\x16\n\tmodelCode\x18g \x01(\tHb\x88\x01\x01\x12\x18\n\x0b\x65xtOperator\x18h \x01(\tHc\x88\x01\x01\x12\x35\n\x0esoftDollarTier\x18i \x01(\x0b\x32\x18.protobuf.SoftDollarTierHd\x88\x01\x01\x12\x14\n\x07\x63\x61shQty\x18j \x01(\x01He\x88\x01\x01\x12 \n\x13mifid2DecisionMaker\x18k \x01(\tHf\x88\x01\x01\x12\x1f\n\x12mifid2DecisionAlgo\x18l \x01(\tHg\x88\x01\x01\x12\"\n\x15mifid2ExecutionTrader\x18m \x01(\tHh\x88\x01\x01\x12 \n\x13mifid2ExecutionAlgo\x18n \x01(\tHi\x88\x01\x01\x12%\n\x18\x64ontUseAutoPriceForHedge\x18o \x01(\x08Hj\x88\x01\x01\x12\x1b\n\x0eisOmsContainer\x18p \x01(\x08Hk\x88\x01\x01\x12(\n\x1b\x64iscretionaryUpToLimitPrice\x18q \x01(\x08Hl\x88\x01\x01\x12\x1b\n\x0e\x61utoCancelDate\x18r \x01(\tHm\x88\x01\x01\x12\x1b\n\x0e\x66illedQuantity\x18s \x01(\tHn\x88\x01\x01\x12\x1c\n\x0frefFuturesConId\x18t \x01(\x05Ho\x88\x01\x01\x12\x1d\n\x10\x61utoCancelParent\x18u \x01(\x08Hp\x88\x01\x01\x12\x18\n\x0bshareholder\x18v \x01(\tHq\x88\x01\x01\x12\x1a\n\rimbalanceOnly\x18w \x01(\x08Hr\x88\x01\x01\x12!\n\x14routeMarketableToBbo\x18x \x01(\x08Hs\x88\x01\x01\x12\x19\n\x0cparentPermId\x18y \x01(\x03Ht\x88\x01\x01\x12\x1d\n\x10usePriceMgmtAlgo\x18z \x01(\x05Hu\x88\x01\x01\x12\x15\n\x08\x64uration\x18{ \x01(\x05Hv\x88\x01\x01\x12\x16\n\tpostToAts\x18| \x01(\x05Hw\x88\x01\x01\x12\"\n\x15\x61\x64vancedErrorOverride\x18} \x01(\tHx\x88\x01\x01\x12\x1c\n\x0fmanualOrderTime\x18~ \x01(\tHy\x88\x01\x01\x12\x18\n\x0bminTradeQty\x18\x7f \x01(\x05Hz\x88\x01\x01\x12\x1c\n\x0eminCompeteSize\x18\x80\x01 \x01(\x05H{\x88\x01\x01\x12&\n\x18\x63ompeteAgainstBestOffset\x18\x81\x01 \x01(\x01H|\x88\x01\x01\x12\x1e\n\x10midOffsetAtWhole\x18\x82\x01 \x01(\x01H}\x88\x01\x01\x12\x1d\n\x0fmidOffsetAtHalf\x18\x83\x01 \x01(\x01H~\x88\x01\x01\x12\x1d\n\x0f\x63ustomerAccount\x18\x84\x01 \x01(\tH\x7f\x88\x01\x01\x12#\n\x14professionalCustomer\x18\x85\x01 \x01(\x08H\x80\x01\x88\x01\x01\x12\"\n\x13\x62ondAccruedInterest\x18\x86\x01 \x01(\tH\x81\x01\x88\x01\x01\x12\x1f\n\x10includeOvernight\x18\x87\x01 \x01(\x08H\x82\x01\x88\x01\x01\x12#\n\x14manualOrderIndicator\x18\x88\x01 \x01(\x05H\x83\x01\x88\x01\x01\x12\x18\n\tsubmitter\x18\x89\x01 \x01(\tH\x84\x01\x88\x01\x01\x1a\x31\n\x0f\x41lgoParamsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a>\n\x1cSmartComboRoutingParamsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x37\n\x15OrderMiscOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x0b\n\t_clientIdB\n\n\x08_orderIdB\t\n\x07_permIdB\x0b\n\t_parentIdB\t\n\x07_actionB\x10\n\x0e_totalQuantityB\x0e\n\x0c_displaySizeB\x0c\n\n_orderTypeB\x0b\n\t_lmtPriceB\x0b\n\t_auxPriceB\x06\n\x04_tifB\n\n\x08_accountB\x0f\n\r_settlingFirmB\x12\n\x10_clearingAccountB\x11\n\x0f_clearingIntentB\x0c\n\n_allOrNoneB\r\n\x0b_blockOrderB\t\n\x07_hiddenB\r\n\x0b_outsideRthB\x0e\n\x0c_sweepToFillB\x10\n\x0e_percentOffsetB\x12\n\x10_trailingPercentB\x11\n\x0f_trailStopPriceB\t\n\x07_minQtyB\x10\n\x0e_goodAfterTimeB\x0f\n\r_goodTillDateB\x0b\n\t_ocaGroupB\x0b\n\t_orderRefB\n\n\x08_rule80AB\n\n\x08_ocaTypeB\x10\n\x0e_triggerMethodB\x12\n\x10_activeStartTimeB\x11\n\x0f_activeStopTimeB\n\n\x08_faGroupB\x0b\n\t_faMethodB\x0f\n\r_faPercentageB\r\n\x0b_volatilityB\x11\n\x0f_volatilityTypeB\x13\n\x11_continuousUpdateB\x15\n\x13_referencePriceTypeB\x18\n\x16_deltaNeutralOrderTypeB\x17\n\x15_deltaNeutralAuxPriceB\x14\n\x12_deltaNeutralConIdB\x18\n\x16_deltaNeutralOpenCloseB\x18\n\x16_deltaNeutralShortSaleB\x1c\n\x1a_deltaNeutralShortSaleSlotB!\n\x1f_deltaNeutralDesignatedLocationB\x15\n\x13_scaleInitLevelSizeB\x15\n\x13_scaleSubsLevelSizeB\x16\n\x14_scalePriceIncrementB\x18\n\x16_scalePriceAdjustValueB\x1b\n\x19_scalePriceAdjustIntervalB\x14\n\x12_scaleProfitOffsetB\x11\n\x0f_scaleAutoResetB\x14\n\x12_scaleInitPositionB\x13\n\x11_scaleInitFillQtyB\x15\n\x13_scaleRandomPercentB\r\n\x0b_scaleTableB\x0c\n\n_hedgeTypeB\r\n\x0b_hedgeParamB\x0f\n\r_algoStrategyB\t\n\x07_algoIdB\t\n\x07_whatIfB\x0b\n\t_transmitB \n\x1e_overridePercentageConstraintsB\x0c\n\n_openCloseB\t\n\x07_originB\x10\n\x0e_shortSaleSlotB\x15\n\x13_designatedLocationB\r\n\x0b_exemptCodeB\x1b\n\x19_deltaNeutralSettlingFirmB\x1e\n\x1c_deltaNeutralClearingAccountB\x1d\n\x1b_deltaNeutralClearingIntentB\x13\n\x11_discretionaryAmtB\x15\n\x13_optOutSmartRoutingB\x10\n\x0e_startingPriceB\x10\n\x0e_stockRefPriceB\x08\n\x06_deltaB\x12\n\x10_stockRangeLowerB\x12\n\x10_stockRangeUpperB\n\n\x08_notHeldB\x0c\n\n_solicitedB\x10\n\x0e_randomizeSizeB\x11\n\x0f_randomizePriceB\x16\n\x14_referenceContractIdB\x15\n\x13_peggedChangeAmountB\x1f\n\x1d_isPeggedChangeAmountDecreaseB\x18\n\x16_referenceChangeAmountB\x16\n\x14_referenceExchangeIdB\x14\n\x12_adjustedOrderTypeB\x0f\n\r_triggerPriceB\x14\n\x12_adjustedStopPriceB\x19\n\x17_adjustedStopLimitPriceB\x19\n\x17_adjustedTrailingAmountB\x19\n\x17_adjustableTrailingUnitB\x11\n\x0f_lmtPriceOffsetB\x18\n\x16_conditionsCancelOrderB\x16\n\x14_conditionsIgnoreRthB\x0c\n\n_modelCodeB\x0e\n\x0c_extOperatorB\x11\n\x0f_softDollarTierB\n\n\x08_cashQtyB\x16\n\x14_mifid2DecisionMakerB\x15\n\x13_mifid2DecisionAlgoB\x18\n\x16_mifid2ExecutionTraderB\x16\n\x14_mifid2ExecutionAlgoB\x1b\n\x19_dontUseAutoPriceForHedgeB\x11\n\x0f_isOmsContainerB\x1e\n\x1c_discretionaryUpToLimitPriceB\x11\n\x0f_autoCancelDateB\x11\n\x0f_filledQuantityB\x12\n\x10_refFuturesConIdB\x13\n\x11_autoCancelParentB\x0e\n\x0c_shareholderB\x10\n\x0e_imbalanceOnlyB\x17\n\x15_routeMarketableToBboB\x0f\n\r_parentPermIdB\x13\n\x11_usePriceMgmtAlgoB\x0b\n\t_durationB\x0c\n\n_postToAtsB\x18\n\x16_advancedErrorOverrideB\x12\n\x10_manualOrderTimeB\x0e\n\x0c_minTradeQtyB\x11\n\x0f_minCompeteSizeB\x1b\n\x19_competeAgainstBestOffsetB\x13\n\x11_midOffsetAtWholeB\x12\n\x10_midOffsetAtHalfB\x12\n\x10_customerAccountB\x17\n\x15_professionalCustomerB\x16\n\x14_bondAccruedInterestB\x13\n\x11_includeOvernightB\x17\n\x15_manualOrderIndicatorB\x0c\n\n_submitterB5\n\x16\x63om.ib.client.protobufB\nOrderProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Order_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\nOrderProto\252\002\016IBApi.protobuf' + _globals['_ORDER_ALGOPARAMSENTRY']._loaded_options = None + _globals['_ORDER_ALGOPARAMSENTRY']._serialized_options = b'8\001' + _globals['_ORDER_SMARTCOMBOROUTINGPARAMSENTRY']._loaded_options = None + _globals['_ORDER_SMARTCOMBOROUTINGPARAMSENTRY']._serialized_options = b'8\001' + _globals['_ORDER_ORDERMISCOPTIONSENTRY']._loaded_options = None + _globals['_ORDER_ORDERMISCOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_ORDER']._serialized_start=70 + _globals['_ORDER']._serialized_end=7045 + _globals['_ORDER_ALGOPARAMSENTRY']._serialized_start=4284 + _globals['_ORDER_ALGOPARAMSENTRY']._serialized_end=4333 + _globals['_ORDER_SMARTCOMBOROUTINGPARAMSENTRY']._serialized_start=4335 + _globals['_ORDER_SMARTCOMBOROUTINGPARAMSENTRY']._serialized_end=4397 + _globals['_ORDER_ORDERMISCOPTIONSENTRY']._serialized_start=4399 + _globals['_ORDER_ORDERMISCOPTIONSENTRY']._serialized_end=4454 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/Order_pb2.pyi b/ib_async/protobuf/Order_pb2.pyi new file mode 100644 index 00000000..44ab8d80 --- /dev/null +++ b/ib_async/protobuf/Order_pb2.pyi @@ -0,0 +1,308 @@ +import OrderCondition_pb2 as _OrderCondition_pb2 +import SoftDollarTier_pb2 as _SoftDollarTier_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class Order(_message.Message): + __slots__ = ("clientId", "orderId", "permId", "parentId", "action", "totalQuantity", "displaySize", "orderType", "lmtPrice", "auxPrice", "tif", "account", "settlingFirm", "clearingAccount", "clearingIntent", "allOrNone", "blockOrder", "hidden", "outsideRth", "sweepToFill", "percentOffset", "trailingPercent", "trailStopPrice", "minQty", "goodAfterTime", "goodTillDate", "ocaGroup", "orderRef", "rule80A", "ocaType", "triggerMethod", "activeStartTime", "activeStopTime", "faGroup", "faMethod", "faPercentage", "volatility", "volatilityType", "continuousUpdate", "referencePriceType", "deltaNeutralOrderType", "deltaNeutralAuxPrice", "deltaNeutralConId", "deltaNeutralOpenClose", "deltaNeutralShortSale", "deltaNeutralShortSaleSlot", "deltaNeutralDesignatedLocation", "scaleInitLevelSize", "scaleSubsLevelSize", "scalePriceIncrement", "scalePriceAdjustValue", "scalePriceAdjustInterval", "scaleProfitOffset", "scaleAutoReset", "scaleInitPosition", "scaleInitFillQty", "scaleRandomPercent", "scaleTable", "hedgeType", "hedgeParam", "algoStrategy", "algoParams", "algoId", "smartComboRoutingParams", "whatIf", "transmit", "overridePercentageConstraints", "openClose", "origin", "shortSaleSlot", "designatedLocation", "exemptCode", "deltaNeutralSettlingFirm", "deltaNeutralClearingAccount", "deltaNeutralClearingIntent", "discretionaryAmt", "optOutSmartRouting", "startingPrice", "stockRefPrice", "delta", "stockRangeLower", "stockRangeUpper", "notHeld", "orderMiscOptions", "solicited", "randomizeSize", "randomizePrice", "referenceContractId", "peggedChangeAmount", "isPeggedChangeAmountDecrease", "referenceChangeAmount", "referenceExchangeId", "adjustedOrderType", "triggerPrice", "adjustedStopPrice", "adjustedStopLimitPrice", "adjustedTrailingAmount", "adjustableTrailingUnit", "lmtPriceOffset", "conditions", "conditionsCancelOrder", "conditionsIgnoreRth", "modelCode", "extOperator", "softDollarTier", "cashQty", "mifid2DecisionMaker", "mifid2DecisionAlgo", "mifid2ExecutionTrader", "mifid2ExecutionAlgo", "dontUseAutoPriceForHedge", "isOmsContainer", "discretionaryUpToLimitPrice", "autoCancelDate", "filledQuantity", "refFuturesConId", "autoCancelParent", "shareholder", "imbalanceOnly", "routeMarketableToBbo", "parentPermId", "usePriceMgmtAlgo", "duration", "postToAts", "advancedErrorOverride", "manualOrderTime", "minTradeQty", "minCompeteSize", "competeAgainstBestOffset", "midOffsetAtWhole", "midOffsetAtHalf", "customerAccount", "professionalCustomer", "bondAccruedInterest", "includeOvernight", "manualOrderIndicator", "submitter") + class AlgoParamsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class SmartComboRoutingParamsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class OrderMiscOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + CLIENTID_FIELD_NUMBER: _ClassVar[int] + ORDERID_FIELD_NUMBER: _ClassVar[int] + PERMID_FIELD_NUMBER: _ClassVar[int] + PARENTID_FIELD_NUMBER: _ClassVar[int] + ACTION_FIELD_NUMBER: _ClassVar[int] + TOTALQUANTITY_FIELD_NUMBER: _ClassVar[int] + DISPLAYSIZE_FIELD_NUMBER: _ClassVar[int] + ORDERTYPE_FIELD_NUMBER: _ClassVar[int] + LMTPRICE_FIELD_NUMBER: _ClassVar[int] + AUXPRICE_FIELD_NUMBER: _ClassVar[int] + TIF_FIELD_NUMBER: _ClassVar[int] + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + SETTLINGFIRM_FIELD_NUMBER: _ClassVar[int] + CLEARINGACCOUNT_FIELD_NUMBER: _ClassVar[int] + CLEARINGINTENT_FIELD_NUMBER: _ClassVar[int] + ALLORNONE_FIELD_NUMBER: _ClassVar[int] + BLOCKORDER_FIELD_NUMBER: _ClassVar[int] + HIDDEN_FIELD_NUMBER: _ClassVar[int] + OUTSIDERTH_FIELD_NUMBER: _ClassVar[int] + SWEEPTOFILL_FIELD_NUMBER: _ClassVar[int] + PERCENTOFFSET_FIELD_NUMBER: _ClassVar[int] + TRAILINGPERCENT_FIELD_NUMBER: _ClassVar[int] + TRAILSTOPPRICE_FIELD_NUMBER: _ClassVar[int] + MINQTY_FIELD_NUMBER: _ClassVar[int] + GOODAFTERTIME_FIELD_NUMBER: _ClassVar[int] + GOODTILLDATE_FIELD_NUMBER: _ClassVar[int] + OCAGROUP_FIELD_NUMBER: _ClassVar[int] + ORDERREF_FIELD_NUMBER: _ClassVar[int] + RULE80A_FIELD_NUMBER: _ClassVar[int] + OCATYPE_FIELD_NUMBER: _ClassVar[int] + TRIGGERMETHOD_FIELD_NUMBER: _ClassVar[int] + ACTIVESTARTTIME_FIELD_NUMBER: _ClassVar[int] + ACTIVESTOPTIME_FIELD_NUMBER: _ClassVar[int] + FAGROUP_FIELD_NUMBER: _ClassVar[int] + FAMETHOD_FIELD_NUMBER: _ClassVar[int] + FAPERCENTAGE_FIELD_NUMBER: _ClassVar[int] + VOLATILITY_FIELD_NUMBER: _ClassVar[int] + VOLATILITYTYPE_FIELD_NUMBER: _ClassVar[int] + CONTINUOUSUPDATE_FIELD_NUMBER: _ClassVar[int] + REFERENCEPRICETYPE_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALORDERTYPE_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALAUXPRICE_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALCONID_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALOPENCLOSE_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALSHORTSALE_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALSHORTSALESLOT_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALDESIGNATEDLOCATION_FIELD_NUMBER: _ClassVar[int] + SCALEINITLEVELSIZE_FIELD_NUMBER: _ClassVar[int] + SCALESUBSLEVELSIZE_FIELD_NUMBER: _ClassVar[int] + SCALEPRICEINCREMENT_FIELD_NUMBER: _ClassVar[int] + SCALEPRICEADJUSTVALUE_FIELD_NUMBER: _ClassVar[int] + SCALEPRICEADJUSTINTERVAL_FIELD_NUMBER: _ClassVar[int] + SCALEPROFITOFFSET_FIELD_NUMBER: _ClassVar[int] + SCALEAUTORESET_FIELD_NUMBER: _ClassVar[int] + SCALEINITPOSITION_FIELD_NUMBER: _ClassVar[int] + SCALEINITFILLQTY_FIELD_NUMBER: _ClassVar[int] + SCALERANDOMPERCENT_FIELD_NUMBER: _ClassVar[int] + SCALETABLE_FIELD_NUMBER: _ClassVar[int] + HEDGETYPE_FIELD_NUMBER: _ClassVar[int] + HEDGEPARAM_FIELD_NUMBER: _ClassVar[int] + ALGOSTRATEGY_FIELD_NUMBER: _ClassVar[int] + ALGOPARAMS_FIELD_NUMBER: _ClassVar[int] + ALGOID_FIELD_NUMBER: _ClassVar[int] + SMARTCOMBOROUTINGPARAMS_FIELD_NUMBER: _ClassVar[int] + WHATIF_FIELD_NUMBER: _ClassVar[int] + TRANSMIT_FIELD_NUMBER: _ClassVar[int] + OVERRIDEPERCENTAGECONSTRAINTS_FIELD_NUMBER: _ClassVar[int] + OPENCLOSE_FIELD_NUMBER: _ClassVar[int] + ORIGIN_FIELD_NUMBER: _ClassVar[int] + SHORTSALESLOT_FIELD_NUMBER: _ClassVar[int] + DESIGNATEDLOCATION_FIELD_NUMBER: _ClassVar[int] + EXEMPTCODE_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALSETTLINGFIRM_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALCLEARINGACCOUNT_FIELD_NUMBER: _ClassVar[int] + DELTANEUTRALCLEARINGINTENT_FIELD_NUMBER: _ClassVar[int] + DISCRETIONARYAMT_FIELD_NUMBER: _ClassVar[int] + OPTOUTSMARTROUTING_FIELD_NUMBER: _ClassVar[int] + STARTINGPRICE_FIELD_NUMBER: _ClassVar[int] + STOCKREFPRICE_FIELD_NUMBER: _ClassVar[int] + DELTA_FIELD_NUMBER: _ClassVar[int] + STOCKRANGELOWER_FIELD_NUMBER: _ClassVar[int] + STOCKRANGEUPPER_FIELD_NUMBER: _ClassVar[int] + NOTHELD_FIELD_NUMBER: _ClassVar[int] + ORDERMISCOPTIONS_FIELD_NUMBER: _ClassVar[int] + SOLICITED_FIELD_NUMBER: _ClassVar[int] + RANDOMIZESIZE_FIELD_NUMBER: _ClassVar[int] + RANDOMIZEPRICE_FIELD_NUMBER: _ClassVar[int] + REFERENCECONTRACTID_FIELD_NUMBER: _ClassVar[int] + PEGGEDCHANGEAMOUNT_FIELD_NUMBER: _ClassVar[int] + ISPEGGEDCHANGEAMOUNTDECREASE_FIELD_NUMBER: _ClassVar[int] + REFERENCECHANGEAMOUNT_FIELD_NUMBER: _ClassVar[int] + REFERENCEEXCHANGEID_FIELD_NUMBER: _ClassVar[int] + ADJUSTEDORDERTYPE_FIELD_NUMBER: _ClassVar[int] + TRIGGERPRICE_FIELD_NUMBER: _ClassVar[int] + ADJUSTEDSTOPPRICE_FIELD_NUMBER: _ClassVar[int] + ADJUSTEDSTOPLIMITPRICE_FIELD_NUMBER: _ClassVar[int] + ADJUSTEDTRAILINGAMOUNT_FIELD_NUMBER: _ClassVar[int] + ADJUSTABLETRAILINGUNIT_FIELD_NUMBER: _ClassVar[int] + LMTPRICEOFFSET_FIELD_NUMBER: _ClassVar[int] + CONDITIONS_FIELD_NUMBER: _ClassVar[int] + CONDITIONSCANCELORDER_FIELD_NUMBER: _ClassVar[int] + CONDITIONSIGNORERTH_FIELD_NUMBER: _ClassVar[int] + MODELCODE_FIELD_NUMBER: _ClassVar[int] + EXTOPERATOR_FIELD_NUMBER: _ClassVar[int] + SOFTDOLLARTIER_FIELD_NUMBER: _ClassVar[int] + CASHQTY_FIELD_NUMBER: _ClassVar[int] + MIFID2DECISIONMAKER_FIELD_NUMBER: _ClassVar[int] + MIFID2DECISIONALGO_FIELD_NUMBER: _ClassVar[int] + MIFID2EXECUTIONTRADER_FIELD_NUMBER: _ClassVar[int] + MIFID2EXECUTIONALGO_FIELD_NUMBER: _ClassVar[int] + DONTUSEAUTOPRICEFORHEDGE_FIELD_NUMBER: _ClassVar[int] + ISOMSCONTAINER_FIELD_NUMBER: _ClassVar[int] + DISCRETIONARYUPTOLIMITPRICE_FIELD_NUMBER: _ClassVar[int] + AUTOCANCELDATE_FIELD_NUMBER: _ClassVar[int] + FILLEDQUANTITY_FIELD_NUMBER: _ClassVar[int] + REFFUTURESCONID_FIELD_NUMBER: _ClassVar[int] + AUTOCANCELPARENT_FIELD_NUMBER: _ClassVar[int] + SHAREHOLDER_FIELD_NUMBER: _ClassVar[int] + IMBALANCEONLY_FIELD_NUMBER: _ClassVar[int] + ROUTEMARKETABLETOBBO_FIELD_NUMBER: _ClassVar[int] + PARENTPERMID_FIELD_NUMBER: _ClassVar[int] + USEPRICEMGMTALGO_FIELD_NUMBER: _ClassVar[int] + DURATION_FIELD_NUMBER: _ClassVar[int] + POSTTOATS_FIELD_NUMBER: _ClassVar[int] + ADVANCEDERROROVERRIDE_FIELD_NUMBER: _ClassVar[int] + MANUALORDERTIME_FIELD_NUMBER: _ClassVar[int] + MINTRADEQTY_FIELD_NUMBER: _ClassVar[int] + MINCOMPETESIZE_FIELD_NUMBER: _ClassVar[int] + COMPETEAGAINSTBESTOFFSET_FIELD_NUMBER: _ClassVar[int] + MIDOFFSETATWHOLE_FIELD_NUMBER: _ClassVar[int] + MIDOFFSETATHALF_FIELD_NUMBER: _ClassVar[int] + CUSTOMERACCOUNT_FIELD_NUMBER: _ClassVar[int] + PROFESSIONALCUSTOMER_FIELD_NUMBER: _ClassVar[int] + BONDACCRUEDINTEREST_FIELD_NUMBER: _ClassVar[int] + INCLUDEOVERNIGHT_FIELD_NUMBER: _ClassVar[int] + MANUALORDERINDICATOR_FIELD_NUMBER: _ClassVar[int] + SUBMITTER_FIELD_NUMBER: _ClassVar[int] + clientId: int + orderId: int + permId: int + parentId: int + action: str + totalQuantity: str + displaySize: int + orderType: str + lmtPrice: float + auxPrice: float + tif: str + account: str + settlingFirm: str + clearingAccount: str + clearingIntent: str + allOrNone: bool + blockOrder: bool + hidden: bool + outsideRth: bool + sweepToFill: bool + percentOffset: float + trailingPercent: float + trailStopPrice: float + minQty: int + goodAfterTime: str + goodTillDate: str + ocaGroup: str + orderRef: str + rule80A: str + ocaType: int + triggerMethod: int + activeStartTime: str + activeStopTime: str + faGroup: str + faMethod: str + faPercentage: str + volatility: float + volatilityType: int + continuousUpdate: bool + referencePriceType: int + deltaNeutralOrderType: str + deltaNeutralAuxPrice: float + deltaNeutralConId: int + deltaNeutralOpenClose: str + deltaNeutralShortSale: bool + deltaNeutralShortSaleSlot: int + deltaNeutralDesignatedLocation: str + scaleInitLevelSize: int + scaleSubsLevelSize: int + scalePriceIncrement: float + scalePriceAdjustValue: float + scalePriceAdjustInterval: int + scaleProfitOffset: float + scaleAutoReset: bool + scaleInitPosition: int + scaleInitFillQty: int + scaleRandomPercent: bool + scaleTable: str + hedgeType: str + hedgeParam: str + algoStrategy: str + algoParams: _containers.ScalarMap[str, str] + algoId: str + smartComboRoutingParams: _containers.ScalarMap[str, str] + whatIf: bool + transmit: bool + overridePercentageConstraints: bool + openClose: str + origin: int + shortSaleSlot: int + designatedLocation: str + exemptCode: int + deltaNeutralSettlingFirm: str + deltaNeutralClearingAccount: str + deltaNeutralClearingIntent: str + discretionaryAmt: float + optOutSmartRouting: bool + startingPrice: float + stockRefPrice: float + delta: float + stockRangeLower: float + stockRangeUpper: float + notHeld: bool + orderMiscOptions: _containers.ScalarMap[str, str] + solicited: bool + randomizeSize: bool + randomizePrice: bool + referenceContractId: int + peggedChangeAmount: float + isPeggedChangeAmountDecrease: bool + referenceChangeAmount: float + referenceExchangeId: str + adjustedOrderType: str + triggerPrice: float + adjustedStopPrice: float + adjustedStopLimitPrice: float + adjustedTrailingAmount: float + adjustableTrailingUnit: int + lmtPriceOffset: float + conditions: _containers.RepeatedCompositeFieldContainer[_OrderCondition_pb2.OrderCondition] + conditionsCancelOrder: bool + conditionsIgnoreRth: bool + modelCode: str + extOperator: str + softDollarTier: _SoftDollarTier_pb2.SoftDollarTier + cashQty: float + mifid2DecisionMaker: str + mifid2DecisionAlgo: str + mifid2ExecutionTrader: str + mifid2ExecutionAlgo: str + dontUseAutoPriceForHedge: bool + isOmsContainer: bool + discretionaryUpToLimitPrice: bool + autoCancelDate: str + filledQuantity: str + refFuturesConId: int + autoCancelParent: bool + shareholder: str + imbalanceOnly: bool + routeMarketableToBbo: bool + parentPermId: int + usePriceMgmtAlgo: int + duration: int + postToAts: int + advancedErrorOverride: str + manualOrderTime: str + minTradeQty: int + minCompeteSize: int + competeAgainstBestOffset: float + midOffsetAtWhole: float + midOffsetAtHalf: float + customerAccount: str + professionalCustomer: bool + bondAccruedInterest: str + includeOvernight: bool + manualOrderIndicator: int + submitter: str + def __init__(self, clientId: _Optional[int] = ..., orderId: _Optional[int] = ..., permId: _Optional[int] = ..., parentId: _Optional[int] = ..., action: _Optional[str] = ..., totalQuantity: _Optional[str] = ..., displaySize: _Optional[int] = ..., orderType: _Optional[str] = ..., lmtPrice: _Optional[float] = ..., auxPrice: _Optional[float] = ..., tif: _Optional[str] = ..., account: _Optional[str] = ..., settlingFirm: _Optional[str] = ..., clearingAccount: _Optional[str] = ..., clearingIntent: _Optional[str] = ..., allOrNone: bool = ..., blockOrder: bool = ..., hidden: bool = ..., outsideRth: bool = ..., sweepToFill: bool = ..., percentOffset: _Optional[float] = ..., trailingPercent: _Optional[float] = ..., trailStopPrice: _Optional[float] = ..., minQty: _Optional[int] = ..., goodAfterTime: _Optional[str] = ..., goodTillDate: _Optional[str] = ..., ocaGroup: _Optional[str] = ..., orderRef: _Optional[str] = ..., rule80A: _Optional[str] = ..., ocaType: _Optional[int] = ..., triggerMethod: _Optional[int] = ..., activeStartTime: _Optional[str] = ..., activeStopTime: _Optional[str] = ..., faGroup: _Optional[str] = ..., faMethod: _Optional[str] = ..., faPercentage: _Optional[str] = ..., volatility: _Optional[float] = ..., volatilityType: _Optional[int] = ..., continuousUpdate: bool = ..., referencePriceType: _Optional[int] = ..., deltaNeutralOrderType: _Optional[str] = ..., deltaNeutralAuxPrice: _Optional[float] = ..., deltaNeutralConId: _Optional[int] = ..., deltaNeutralOpenClose: _Optional[str] = ..., deltaNeutralShortSale: bool = ..., deltaNeutralShortSaleSlot: _Optional[int] = ..., deltaNeutralDesignatedLocation: _Optional[str] = ..., scaleInitLevelSize: _Optional[int] = ..., scaleSubsLevelSize: _Optional[int] = ..., scalePriceIncrement: _Optional[float] = ..., scalePriceAdjustValue: _Optional[float] = ..., scalePriceAdjustInterval: _Optional[int] = ..., scaleProfitOffset: _Optional[float] = ..., scaleAutoReset: bool = ..., scaleInitPosition: _Optional[int] = ..., scaleInitFillQty: _Optional[int] = ..., scaleRandomPercent: bool = ..., scaleTable: _Optional[str] = ..., hedgeType: _Optional[str] = ..., hedgeParam: _Optional[str] = ..., algoStrategy: _Optional[str] = ..., algoParams: _Optional[_Mapping[str, str]] = ..., algoId: _Optional[str] = ..., smartComboRoutingParams: _Optional[_Mapping[str, str]] = ..., whatIf: bool = ..., transmit: bool = ..., overridePercentageConstraints: bool = ..., openClose: _Optional[str] = ..., origin: _Optional[int] = ..., shortSaleSlot: _Optional[int] = ..., designatedLocation: _Optional[str] = ..., exemptCode: _Optional[int] = ..., deltaNeutralSettlingFirm: _Optional[str] = ..., deltaNeutralClearingAccount: _Optional[str] = ..., deltaNeutralClearingIntent: _Optional[str] = ..., discretionaryAmt: _Optional[float] = ..., optOutSmartRouting: bool = ..., startingPrice: _Optional[float] = ..., stockRefPrice: _Optional[float] = ..., delta: _Optional[float] = ..., stockRangeLower: _Optional[float] = ..., stockRangeUpper: _Optional[float] = ..., notHeld: bool = ..., orderMiscOptions: _Optional[_Mapping[str, str]] = ..., solicited: bool = ..., randomizeSize: bool = ..., randomizePrice: bool = ..., referenceContractId: _Optional[int] = ..., peggedChangeAmount: _Optional[float] = ..., isPeggedChangeAmountDecrease: bool = ..., referenceChangeAmount: _Optional[float] = ..., referenceExchangeId: _Optional[str] = ..., adjustedOrderType: _Optional[str] = ..., triggerPrice: _Optional[float] = ..., adjustedStopPrice: _Optional[float] = ..., adjustedStopLimitPrice: _Optional[float] = ..., adjustedTrailingAmount: _Optional[float] = ..., adjustableTrailingUnit: _Optional[int] = ..., lmtPriceOffset: _Optional[float] = ..., conditions: _Optional[_Iterable[_Union[_OrderCondition_pb2.OrderCondition, _Mapping]]] = ..., conditionsCancelOrder: bool = ..., conditionsIgnoreRth: bool = ..., modelCode: _Optional[str] = ..., extOperator: _Optional[str] = ..., softDollarTier: _Optional[_Union[_SoftDollarTier_pb2.SoftDollarTier, _Mapping]] = ..., cashQty: _Optional[float] = ..., mifid2DecisionMaker: _Optional[str] = ..., mifid2DecisionAlgo: _Optional[str] = ..., mifid2ExecutionTrader: _Optional[str] = ..., mifid2ExecutionAlgo: _Optional[str] = ..., dontUseAutoPriceForHedge: bool = ..., isOmsContainer: bool = ..., discretionaryUpToLimitPrice: bool = ..., autoCancelDate: _Optional[str] = ..., filledQuantity: _Optional[str] = ..., refFuturesConId: _Optional[int] = ..., autoCancelParent: bool = ..., shareholder: _Optional[str] = ..., imbalanceOnly: bool = ..., routeMarketableToBbo: bool = ..., parentPermId: _Optional[int] = ..., usePriceMgmtAlgo: _Optional[int] = ..., duration: _Optional[int] = ..., postToAts: _Optional[int] = ..., advancedErrorOverride: _Optional[str] = ..., manualOrderTime: _Optional[str] = ..., minTradeQty: _Optional[int] = ..., minCompeteSize: _Optional[int] = ..., competeAgainstBestOffset: _Optional[float] = ..., midOffsetAtWhole: _Optional[float] = ..., midOffsetAtHalf: _Optional[float] = ..., customerAccount: _Optional[str] = ..., professionalCustomer: bool = ..., bondAccruedInterest: _Optional[str] = ..., includeOvernight: bool = ..., manualOrderIndicator: _Optional[int] = ..., submitter: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/PlaceOrderRequest_pb2.py b/ib_async/protobuf/PlaceOrderRequest_pb2.py new file mode 100644 index 00000000..945395cf --- /dev/null +++ b/ib_async/protobuf/PlaceOrderRequest_pb2.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PlaceOrderRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PlaceOrderRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 +from . import Order_pb2 as Order__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17PlaceOrderRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\x1a\x0bOrder.proto\"\x9c\x01\n\x11PlaceOrderRequest\x12\x14\n\x07orderId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12#\n\x05order\x18\x03 \x01(\x0b\x32\x0f.protobuf.OrderH\x02\x88\x01\x01\x42\n\n\x08_orderIdB\x0b\n\t_contractB\x08\n\x06_orderBA\n\x16\x63om.ib.client.protobufB\x16PlaceOrderRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PlaceOrderRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026PlaceOrderRequestProto\252\002\016IBApi.protobuf' + _globals['_PLACEORDERREQUEST']._serialized_start=67 + _globals['_PLACEORDERREQUEST']._serialized_end=223 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PlaceOrderRequest_pb2.pyi b/ib_async/protobuf/PlaceOrderRequest_pb2.pyi new file mode 100644 index 00000000..a1963bbe --- /dev/null +++ b/ib_async/protobuf/PlaceOrderRequest_pb2.pyi @@ -0,0 +1,18 @@ +import Contract_pb2 as _Contract_pb2 +import Order_pb2 as _Order_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class PlaceOrderRequest(_message.Message): + __slots__ = ("orderId", "contract", "order") + ORDERID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + ORDER_FIELD_NUMBER: _ClassVar[int] + orderId: int + contract: _Contract_pb2.Contract + order: _Order_pb2.Order + def __init__(self, orderId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., order: _Optional[_Union[_Order_pb2.Order, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/PnLRequest_pb2.py b/ib_async/protobuf/PnLRequest_pb2.py new file mode 100644 index 00000000..cdc3e533 --- /dev/null +++ b/ib_async/protobuf/PnLRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PnLRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PnLRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10PnLRequest.proto\x12\x08protobuf\"r\n\nPnLRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07\x61\x63\x63ount\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x16\n\tmodelCode\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_accountB\x0c\n\n_modelCodeB:\n\x16\x63om.ib.client.protobufB\x0fPnLRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PnLRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\017PnLRequestProto\252\002\016IBApi.protobuf' + _globals['_PNLREQUEST']._serialized_start=30 + _globals['_PNLREQUEST']._serialized_end=144 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PnLRequest_pb2.pyi b/ib_async/protobuf/PnLRequest_pb2.pyi new file mode 100644 index 00000000..0afec142 --- /dev/null +++ b/ib_async/protobuf/PnLRequest_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class PnLRequest(_message.Message): + __slots__ = ("reqId", "account", "modelCode") + REQID_FIELD_NUMBER: _ClassVar[int] + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + MODELCODE_FIELD_NUMBER: _ClassVar[int] + reqId: int + account: str + modelCode: str + def __init__(self, reqId: _Optional[int] = ..., account: _Optional[str] = ..., modelCode: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/PnLSingleRequest_pb2.py b/ib_async/protobuf/PnLSingleRequest_pb2.py new file mode 100644 index 00000000..e6af821c --- /dev/null +++ b/ib_async/protobuf/PnLSingleRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PnLSingleRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PnLSingleRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16PnLSingleRequest.proto\x12\x08protobuf\"\x96\x01\n\x10PnLSingleRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07\x61\x63\x63ount\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x16\n\tmodelCode\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x12\n\x05\x63onId\x18\x04 \x01(\x05H\x03\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_accountB\x0c\n\n_modelCodeB\x08\n\x06_conIdB@\n\x16\x63om.ib.client.protobufB\x15PnLSingleRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PnLSingleRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025PnLSingleRequestProto\252\002\016IBApi.protobuf' + _globals['_PNLSINGLEREQUEST']._serialized_start=37 + _globals['_PNLSINGLEREQUEST']._serialized_end=187 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PnLSingleRequest_pb2.pyi b/ib_async/protobuf/PnLSingleRequest_pb2.pyi new file mode 100644 index 00000000..98e205d7 --- /dev/null +++ b/ib_async/protobuf/PnLSingleRequest_pb2.pyi @@ -0,0 +1,17 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class PnLSingleRequest(_message.Message): + __slots__ = ("reqId", "account", "modelCode", "conId") + REQID_FIELD_NUMBER: _ClassVar[int] + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + MODELCODE_FIELD_NUMBER: _ClassVar[int] + CONID_FIELD_NUMBER: _ClassVar[int] + reqId: int + account: str + modelCode: str + conId: int + def __init__(self, reqId: _Optional[int] = ..., account: _Optional[str] = ..., modelCode: _Optional[str] = ..., conId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/PnLSingle_pb2.py b/ib_async/protobuf/PnLSingle_pb2.py new file mode 100644 index 00000000..29ae74f9 --- /dev/null +++ b/ib_async/protobuf/PnLSingle_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PnLSingle.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PnLSingle.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fPnLSingle.proto\x12\x08protobuf\"\xe7\x01\n\tPnLSingle\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08position\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x15\n\x08\x64\x61ilyPnL\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\runrealizedPnL\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x18\n\x0brealizedPnL\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x12\n\x05value\x18\x06 \x01(\x01H\x05\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_positionB\x0b\n\t_dailyPnLB\x10\n\x0e_unrealizedPnLB\x0e\n\x0c_realizedPnLB\x08\n\x06_valueB9\n\x16\x63om.ib.client.protobufB\x0ePnLSingleProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PnLSingle_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\016PnLSingleProto\252\002\016IBApi.protobuf' + _globals['_PNLSINGLE']._serialized_start=30 + _globals['_PNLSINGLE']._serialized_end=261 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PnLSingle_pb2.pyi b/ib_async/protobuf/PnLSingle_pb2.pyi new file mode 100644 index 00000000..6865103c --- /dev/null +++ b/ib_async/protobuf/PnLSingle_pb2.pyi @@ -0,0 +1,21 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class PnLSingle(_message.Message): + __slots__ = ("reqId", "position", "dailyPnL", "unrealizedPnL", "realizedPnL", "value") + REQID_FIELD_NUMBER: _ClassVar[int] + POSITION_FIELD_NUMBER: _ClassVar[int] + DAILYPNL_FIELD_NUMBER: _ClassVar[int] + UNREALIZEDPNL_FIELD_NUMBER: _ClassVar[int] + REALIZEDPNL_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + reqId: int + position: str + dailyPnL: float + unrealizedPnL: float + realizedPnL: float + value: float + def __init__(self, reqId: _Optional[int] = ..., position: _Optional[str] = ..., dailyPnL: _Optional[float] = ..., unrealizedPnL: _Optional[float] = ..., realizedPnL: _Optional[float] = ..., value: _Optional[float] = ...) -> None: ... diff --git a/ib_async/protobuf/PnL_pb2.py b/ib_async/protobuf/PnL_pb2.py new file mode 100644 index 00000000..a45004d4 --- /dev/null +++ b/ib_async/protobuf/PnL_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PnL.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PnL.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tPnL.proto\x12\x08protobuf\"\x9f\x01\n\x03PnL\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08\x64\x61ilyPnL\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x1a\n\runrealizedPnL\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x18\n\x0brealizedPnL\x18\x04 \x01(\x01H\x03\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_dailyPnLB\x10\n\x0e_unrealizedPnLB\x0e\n\x0c_realizedPnLB3\n\x16\x63om.ib.client.protobufB\x08PnLProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PnL_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\010PnLProto\252\002\016IBApi.protobuf' + _globals['_PNL']._serialized_start=24 + _globals['_PNL']._serialized_end=183 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PnL_pb2.pyi b/ib_async/protobuf/PnL_pb2.pyi new file mode 100644 index 00000000..e58443d6 --- /dev/null +++ b/ib_async/protobuf/PnL_pb2.pyi @@ -0,0 +1,17 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class PnL(_message.Message): + __slots__ = ("reqId", "dailyPnL", "unrealizedPnL", "realizedPnL") + REQID_FIELD_NUMBER: _ClassVar[int] + DAILYPNL_FIELD_NUMBER: _ClassVar[int] + UNREALIZEDPNL_FIELD_NUMBER: _ClassVar[int] + REALIZEDPNL_FIELD_NUMBER: _ClassVar[int] + reqId: int + dailyPnL: float + unrealizedPnL: float + realizedPnL: float + def __init__(self, reqId: _Optional[int] = ..., dailyPnL: _Optional[float] = ..., unrealizedPnL: _Optional[float] = ..., realizedPnL: _Optional[float] = ...) -> None: ... diff --git a/ib_async/protobuf/PortfolioValue_pb2.py b/ib_async/protobuf/PortfolioValue_pb2.py new file mode 100644 index 00000000..48c7bfcd --- /dev/null +++ b/ib_async/protobuf/PortfolioValue_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PortfolioValue.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PortfolioValue.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14PortfolioValue.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xec\x02\n\x0ePortfolioValue\x12)\n\x08\x63ontract\x18\x01 \x01(\x0b\x32\x12.protobuf.ContractH\x00\x88\x01\x01\x12\x15\n\x08position\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bmarketPrice\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x18\n\x0bmarketValue\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x18\n\x0b\x61verageCost\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x1a\n\runrealizedPNL\x18\x06 \x01(\x01H\x05\x88\x01\x01\x12\x18\n\x0brealizedPNL\x18\x07 \x01(\x01H\x06\x88\x01\x01\x12\x18\n\x0b\x61\x63\x63ountName\x18\x08 \x01(\tH\x07\x88\x01\x01\x42\x0b\n\t_contractB\x0b\n\t_positionB\x0e\n\x0c_marketPriceB\x0e\n\x0c_marketValueB\x0e\n\x0c_averageCostB\x10\n\x0e_unrealizedPNLB\x0e\n\x0c_realizedPNLB\x0e\n\x0c_accountNameB>\n\x16\x63om.ib.client.protobufB\x13PortfolioValueProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PortfolioValue_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023PortfolioValueProto\252\002\016IBApi.protobuf' + _globals['_PORTFOLIOVALUE']._serialized_start=51 + _globals['_PORTFOLIOVALUE']._serialized_end=415 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PortfolioValue_pb2.pyi b/ib_async/protobuf/PortfolioValue_pb2.pyi new file mode 100644 index 00000000..307106d7 --- /dev/null +++ b/ib_async/protobuf/PortfolioValue_pb2.pyi @@ -0,0 +1,27 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class PortfolioValue(_message.Message): + __slots__ = ("contract", "position", "marketPrice", "marketValue", "averageCost", "unrealizedPNL", "realizedPNL", "accountName") + CONTRACT_FIELD_NUMBER: _ClassVar[int] + POSITION_FIELD_NUMBER: _ClassVar[int] + MARKETPRICE_FIELD_NUMBER: _ClassVar[int] + MARKETVALUE_FIELD_NUMBER: _ClassVar[int] + AVERAGECOST_FIELD_NUMBER: _ClassVar[int] + UNREALIZEDPNL_FIELD_NUMBER: _ClassVar[int] + REALIZEDPNL_FIELD_NUMBER: _ClassVar[int] + ACCOUNTNAME_FIELD_NUMBER: _ClassVar[int] + contract: _Contract_pb2.Contract + position: str + marketPrice: float + marketValue: float + averageCost: float + unrealizedPNL: float + realizedPNL: float + accountName: str + def __init__(self, contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., position: _Optional[str] = ..., marketPrice: _Optional[float] = ..., marketValue: _Optional[float] = ..., averageCost: _Optional[float] = ..., unrealizedPNL: _Optional[float] = ..., realizedPNL: _Optional[float] = ..., accountName: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/PositionEnd_pb2.py b/ib_async/protobuf/PositionEnd_pb2.py new file mode 100644 index 00000000..236b081a --- /dev/null +++ b/ib_async/protobuf/PositionEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PositionEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PositionEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11PositionEnd.proto\x12\x08protobuf\"\r\n\x0bPositionEndB;\n\x16\x63om.ib.client.protobufB\x10PositionEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PositionEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020PositionEndProto\252\002\016IBApi.protobuf' + _globals['_POSITIONEND']._serialized_start=31 + _globals['_POSITIONEND']._serialized_end=44 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PositionEnd_pb2.pyi b/ib_async/protobuf/PositionEnd_pb2.pyi new file mode 100644 index 00000000..8d17a7ef --- /dev/null +++ b/ib_async/protobuf/PositionEnd_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class PositionEnd(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/PositionMultiEnd_pb2.py b/ib_async/protobuf/PositionMultiEnd_pb2.py new file mode 100644 index 00000000..c6ef70f9 --- /dev/null +++ b/ib_async/protobuf/PositionMultiEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PositionMultiEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PositionMultiEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16PositionMultiEnd.proto\x12\x08protobuf\"0\n\x10PositionMultiEnd\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdB@\n\x16\x63om.ib.client.protobufB\x15PositionMultiEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PositionMultiEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025PositionMultiEndProto\252\002\016IBApi.protobuf' + _globals['_POSITIONMULTIEND']._serialized_start=36 + _globals['_POSITIONMULTIEND']._serialized_end=84 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PositionMultiEnd_pb2.pyi b/ib_async/protobuf/PositionMultiEnd_pb2.pyi new file mode 100644 index 00000000..330c1fdc --- /dev/null +++ b/ib_async/protobuf/PositionMultiEnd_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class PositionMultiEnd(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/PositionMulti_pb2.py b/ib_async/protobuf/PositionMulti_pb2.py new file mode 100644 index 00000000..66899cfc --- /dev/null +++ b/ib_async/protobuf/PositionMulti_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PositionMulti.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PositionMulti.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13PositionMulti.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xf3\x01\n\rPositionMulti\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07\x61\x63\x63ount\x18\x02 \x01(\tH\x01\x88\x01\x01\x12)\n\x08\x63ontract\x18\x03 \x01(\x0b\x32\x12.protobuf.ContractH\x02\x88\x01\x01\x12\x15\n\x08position\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07\x61vgCost\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x16\n\tmodelCode\x18\x06 \x01(\tH\x05\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_accountB\x0b\n\t_contractB\x0b\n\t_positionB\n\n\x08_avgCostB\x0c\n\n_modelCodeB=\n\x16\x63om.ib.client.protobufB\x12PositionMultiProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PositionMulti_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\022PositionMultiProto\252\002\016IBApi.protobuf' + _globals['_POSITIONMULTI']._serialized_start=50 + _globals['_POSITIONMULTI']._serialized_end=293 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PositionMulti_pb2.pyi b/ib_async/protobuf/PositionMulti_pb2.pyi new file mode 100644 index 00000000..afa1950c --- /dev/null +++ b/ib_async/protobuf/PositionMulti_pb2.pyi @@ -0,0 +1,23 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class PositionMulti(_message.Message): + __slots__ = ("reqId", "account", "contract", "position", "avgCost", "modelCode") + REQID_FIELD_NUMBER: _ClassVar[int] + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + POSITION_FIELD_NUMBER: _ClassVar[int] + AVGCOST_FIELD_NUMBER: _ClassVar[int] + MODELCODE_FIELD_NUMBER: _ClassVar[int] + reqId: int + account: str + contract: _Contract_pb2.Contract + position: str + avgCost: float + modelCode: str + def __init__(self, reqId: _Optional[int] = ..., account: _Optional[str] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., position: _Optional[str] = ..., avgCost: _Optional[float] = ..., modelCode: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/Position_pb2.py b/ib_async/protobuf/Position_pb2.py new file mode 100644 index 00000000..8c6b7a85 --- /dev/null +++ b/ib_async/protobuf/Position_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: Position.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'Position.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0ePosition.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xaa\x01\n\x08Position\x12\x14\n\x07\x61\x63\x63ount\x18\x01 \x01(\tH\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x15\n\x08position\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x14\n\x07\x61vgCost\x18\x04 \x01(\x01H\x03\x88\x01\x01\x42\n\n\x08_accountB\x0b\n\t_contractB\x0b\n\t_positionB\n\n\x08_avgCostB8\n\x16\x63om.ib.client.protobufB\rPositionProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Position_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\rPositionProto\252\002\016IBApi.protobuf' + _globals['_POSITION']._serialized_start=45 + _globals['_POSITION']._serialized_end=215 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/Position_pb2.pyi b/ib_async/protobuf/Position_pb2.pyi new file mode 100644 index 00000000..683911d0 --- /dev/null +++ b/ib_async/protobuf/Position_pb2.pyi @@ -0,0 +1,19 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class Position(_message.Message): + __slots__ = ("account", "contract", "position", "avgCost") + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + POSITION_FIELD_NUMBER: _ClassVar[int] + AVGCOST_FIELD_NUMBER: _ClassVar[int] + account: str + contract: _Contract_pb2.Contract + position: str + avgCost: float + def __init__(self, account: _Optional[str] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., position: _Optional[str] = ..., avgCost: _Optional[float] = ...) -> None: ... diff --git a/ib_async/protobuf/PositionsMultiRequest_pb2.py b/ib_async/protobuf/PositionsMultiRequest_pb2.py new file mode 100644 index 00000000..0e6f8fe7 --- /dev/null +++ b/ib_async/protobuf/PositionsMultiRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PositionsMultiRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PositionsMultiRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bPositionsMultiRequest.proto\x12\x08protobuf\"}\n\x15PositionsMultiRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07\x61\x63\x63ount\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x16\n\tmodelCode\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_accountB\x0c\n\n_modelCodeBE\n\x16\x63om.ib.client.protobufB\x1aPositionsMultiRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PositionsMultiRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032PositionsMultiRequestProto\252\002\016IBApi.protobuf' + _globals['_POSITIONSMULTIREQUEST']._serialized_start=41 + _globals['_POSITIONSMULTIREQUEST']._serialized_end=166 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PositionsMultiRequest_pb2.pyi b/ib_async/protobuf/PositionsMultiRequest_pb2.pyi new file mode 100644 index 00000000..52368e8e --- /dev/null +++ b/ib_async/protobuf/PositionsMultiRequest_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class PositionsMultiRequest(_message.Message): + __slots__ = ("reqId", "account", "modelCode") + REQID_FIELD_NUMBER: _ClassVar[int] + ACCOUNT_FIELD_NUMBER: _ClassVar[int] + MODELCODE_FIELD_NUMBER: _ClassVar[int] + reqId: int + account: str + modelCode: str + def __init__(self, reqId: _Optional[int] = ..., account: _Optional[str] = ..., modelCode: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/PositionsRequest_pb2.py b/ib_async/protobuf/PositionsRequest_pb2.py new file mode 100644 index 00000000..1ba644bb --- /dev/null +++ b/ib_async/protobuf/PositionsRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PositionsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PositionsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16PositionsRequest.proto\x12\x08protobuf\"\x12\n\x10PositionsRequestB@\n\x16\x63om.ib.client.protobufB\x15PositionsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PositionsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025PositionsRequestProto\252\002\016IBApi.protobuf' + _globals['_POSITIONSREQUEST']._serialized_start=36 + _globals['_POSITIONSREQUEST']._serialized_end=54 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PositionsRequest_pb2.pyi b/ib_async/protobuf/PositionsRequest_pb2.pyi new file mode 100644 index 00000000..3bf4ab68 --- /dev/null +++ b/ib_async/protobuf/PositionsRequest_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class PositionsRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/PriceIncrement_pb2.py b/ib_async/protobuf/PriceIncrement_pb2.py new file mode 100644 index 00000000..6a9e1df2 --- /dev/null +++ b/ib_async/protobuf/PriceIncrement_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: PriceIncrement.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'PriceIncrement.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14PriceIncrement.proto\x12\x08protobuf\"X\n\x0ePriceIncrement\x12\x14\n\x07lowEdge\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tincrement\x18\x02 \x01(\x01H\x01\x88\x01\x01\x42\n\n\x08_lowEdgeB\x0c\n\n_incrementB>\n\x16\x63om.ib.client.protobufB\x13PriceIncrementProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'PriceIncrement_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023PriceIncrementProto\252\002\016IBApi.protobuf' + _globals['_PRICEINCREMENT']._serialized_start=34 + _globals['_PRICEINCREMENT']._serialized_end=122 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/PriceIncrement_pb2.pyi b/ib_async/protobuf/PriceIncrement_pb2.pyi new file mode 100644 index 00000000..75634f52 --- /dev/null +++ b/ib_async/protobuf/PriceIncrement_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class PriceIncrement(_message.Message): + __slots__ = ("lowEdge", "increment") + LOWEDGE_FIELD_NUMBER: _ClassVar[int] + INCREMENT_FIELD_NUMBER: _ClassVar[int] + lowEdge: float + increment: float + def __init__(self, lowEdge: _Optional[float] = ..., increment: _Optional[float] = ...) -> None: ... diff --git a/ib_async/protobuf/QueryDisplayGroupsRequest_pb2.py b/ib_async/protobuf/QueryDisplayGroupsRequest_pb2.py new file mode 100644 index 00000000..cf183af2 --- /dev/null +++ b/ib_async/protobuf/QueryDisplayGroupsRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: QueryDisplayGroupsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'QueryDisplayGroupsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fQueryDisplayGroupsRequest.proto\x12\x08protobuf\"9\n\x19QueryDisplayGroupsRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBI\n\x16\x63om.ib.client.protobufB\x1eQueryDisplayGroupsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'QueryDisplayGroupsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\036QueryDisplayGroupsRequestProto\252\002\016IBApi.protobuf' + _globals['_QUERYDISPLAYGROUPSREQUEST']._serialized_start=45 + _globals['_QUERYDISPLAYGROUPSREQUEST']._serialized_end=102 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/QueryDisplayGroupsRequest_pb2.pyi b/ib_async/protobuf/QueryDisplayGroupsRequest_pb2.pyi new file mode 100644 index 00000000..fb72ba09 --- /dev/null +++ b/ib_async/protobuf/QueryDisplayGroupsRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class QueryDisplayGroupsRequest(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/RealTimeBarTick_pb2.py b/ib_async/protobuf/RealTimeBarTick_pb2.py new file mode 100644 index 00000000..00d4c348 --- /dev/null +++ b/ib_async/protobuf/RealTimeBarTick_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: RealTimeBarTick.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'RealTimeBarTick.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15RealTimeBarTick.proto\x12\x08protobuf\"\x93\x02\n\x0fRealTimeBarTick\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x11\n\x04time\x18\x02 \x01(\x03H\x01\x88\x01\x01\x12\x11\n\x04open\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x11\n\x04high\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x10\n\x03low\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x12\n\x05\x63lose\x18\x06 \x01(\x01H\x05\x88\x01\x01\x12\x13\n\x06volume\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x10\n\x03WAP\x18\x08 \x01(\tH\x07\x88\x01\x01\x12\x12\n\x05\x63ount\x18\t \x01(\x05H\x08\x88\x01\x01\x42\x08\n\x06_reqIdB\x07\n\x05_timeB\x07\n\x05_openB\x07\n\x05_highB\x06\n\x04_lowB\x08\n\x06_closeB\t\n\x07_volumeB\x06\n\x04_WAPB\x08\n\x06_countB?\n\x16\x63om.ib.client.protobufB\x14RealTimeBarTickProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'RealTimeBarTick_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024RealTimeBarTickProto\252\002\016IBApi.protobuf' + _globals['_REALTIMEBARTICK']._serialized_start=36 + _globals['_REALTIMEBARTICK']._serialized_end=311 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/RealTimeBarTick_pb2.pyi b/ib_async/protobuf/RealTimeBarTick_pb2.pyi new file mode 100644 index 00000000..c5fd6829 --- /dev/null +++ b/ib_async/protobuf/RealTimeBarTick_pb2.pyi @@ -0,0 +1,27 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class RealTimeBarTick(_message.Message): + __slots__ = ("reqId", "time", "open", "high", "low", "close", "volume", "WAP", "count") + REQID_FIELD_NUMBER: _ClassVar[int] + TIME_FIELD_NUMBER: _ClassVar[int] + OPEN_FIELD_NUMBER: _ClassVar[int] + HIGH_FIELD_NUMBER: _ClassVar[int] + LOW_FIELD_NUMBER: _ClassVar[int] + CLOSE_FIELD_NUMBER: _ClassVar[int] + VOLUME_FIELD_NUMBER: _ClassVar[int] + WAP_FIELD_NUMBER: _ClassVar[int] + COUNT_FIELD_NUMBER: _ClassVar[int] + reqId: int + time: int + open: float + high: float + low: float + close: float + volume: str + WAP: str + count: int + def __init__(self, reqId: _Optional[int] = ..., time: _Optional[int] = ..., open: _Optional[float] = ..., high: _Optional[float] = ..., low: _Optional[float] = ..., close: _Optional[float] = ..., volume: _Optional[str] = ..., WAP: _Optional[str] = ..., count: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/RealTimeBarsRequest_pb2.py b/ib_async/protobuf/RealTimeBarsRequest_pb2.py new file mode 100644 index 00000000..de090d5d --- /dev/null +++ b/ib_async/protobuf/RealTimeBarsRequest_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: RealTimeBarsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'RealTimeBarsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19RealTimeBarsRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xe6\x02\n\x13RealTimeBarsRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x14\n\x07\x62\x61rSize\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12\x17\n\nwhatToShow\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06useRTH\x18\x05 \x01(\x08H\x04\x88\x01\x01\x12S\n\x13realTimeBarsOptions\x18\x06 \x03(\x0b\x32\x36.protobuf.RealTimeBarsRequest.RealTimeBarsOptionsEntry\x1a:\n\x18RealTimeBarsOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\n\n\x08_barSizeB\r\n\x0b_whatToShowB\t\n\x07_useRTHBC\n\x16\x63om.ib.client.protobufB\x18RealTimeBarsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'RealTimeBarsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030RealTimeBarsRequestProto\252\002\016IBApi.protobuf' + _globals['_REALTIMEBARSREQUEST_REALTIMEBARSOPTIONSENTRY']._loaded_options = None + _globals['_REALTIMEBARSREQUEST_REALTIMEBARSOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_REALTIMEBARSREQUEST']._serialized_start=56 + _globals['_REALTIMEBARSREQUEST']._serialized_end=414 + _globals['_REALTIMEBARSREQUEST_REALTIMEBARSOPTIONSENTRY']._serialized_start=295 + _globals['_REALTIMEBARSREQUEST_REALTIMEBARSOPTIONSENTRY']._serialized_end=353 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/RealTimeBarsRequest_pb2.pyi b/ib_async/protobuf/RealTimeBarsRequest_pb2.pyi new file mode 100644 index 00000000..ca5d54ba --- /dev/null +++ b/ib_async/protobuf/RealTimeBarsRequest_pb2.pyi @@ -0,0 +1,31 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class RealTimeBarsRequest(_message.Message): + __slots__ = ("reqId", "contract", "barSize", "whatToShow", "useRTH", "realTimeBarsOptions") + class RealTimeBarsOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + BARSIZE_FIELD_NUMBER: _ClassVar[int] + WHATTOSHOW_FIELD_NUMBER: _ClassVar[int] + USERTH_FIELD_NUMBER: _ClassVar[int] + REALTIMEBARSOPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + barSize: int + whatToShow: str + useRTH: bool + realTimeBarsOptions: _containers.ScalarMap[str, str] + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., barSize: _Optional[int] = ..., whatToShow: _Optional[str] = ..., useRTH: bool = ..., realTimeBarsOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/ReceiveFA_pb2.py b/ib_async/protobuf/ReceiveFA_pb2.py new file mode 100644 index 00000000..407c4b56 --- /dev/null +++ b/ib_async/protobuf/ReceiveFA_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ReceiveFA.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ReceiveFA.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fReceiveFA.proto\x12\x08protobuf\"M\n\tReceiveFA\x12\x17\n\nfaDataType\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x10\n\x03xml\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\r\n\x0b_faDataTypeB\x06\n\x04_xmlB9\n\x16\x63om.ib.client.protobufB\x0eReceiveFAProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ReceiveFA_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\016ReceiveFAProto\252\002\016IBApi.protobuf' + _globals['_RECEIVEFA']._serialized_start=29 + _globals['_RECEIVEFA']._serialized_end=106 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ReceiveFA_pb2.pyi b/ib_async/protobuf/ReceiveFA_pb2.pyi new file mode 100644 index 00000000..43d278de --- /dev/null +++ b/ib_async/protobuf/ReceiveFA_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ReceiveFA(_message.Message): + __slots__ = ("faDataType", "xml") + FADATATYPE_FIELD_NUMBER: _ClassVar[int] + XML_FIELD_NUMBER: _ClassVar[int] + faDataType: int + xml: str + def __init__(self, faDataType: _Optional[int] = ..., xml: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/ReplaceFAEnd_pb2.py b/ib_async/protobuf/ReplaceFAEnd_pb2.py new file mode 100644 index 00000000..6a10f8df --- /dev/null +++ b/ib_async/protobuf/ReplaceFAEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ReplaceFAEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ReplaceFAEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12ReplaceFAEnd.proto\x12\x08protobuf\"H\n\x0cReplaceFAEnd\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x11\n\x04text\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x07\n\x05_textB<\n\x16\x63om.ib.client.protobufB\x11ReplaceFAEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ReplaceFAEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\021ReplaceFAEndProto\252\002\016IBApi.protobuf' + _globals['_REPLACEFAEND']._serialized_start=32 + _globals['_REPLACEFAEND']._serialized_end=104 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ReplaceFAEnd_pb2.pyi b/ib_async/protobuf/ReplaceFAEnd_pb2.pyi new file mode 100644 index 00000000..a0aa7b58 --- /dev/null +++ b/ib_async/protobuf/ReplaceFAEnd_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ReplaceFAEnd(_message.Message): + __slots__ = ("reqId", "text") + REQID_FIELD_NUMBER: _ClassVar[int] + TEXT_FIELD_NUMBER: _ClassVar[int] + reqId: int + text: str + def __init__(self, reqId: _Optional[int] = ..., text: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/RerouteMarketDataRequest_pb2.py b/ib_async/protobuf/RerouteMarketDataRequest_pb2.py new file mode 100644 index 00000000..1cbb1225 --- /dev/null +++ b/ib_async/protobuf/RerouteMarketDataRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: RerouteMarketDataRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'RerouteMarketDataRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eRerouteMarketDataRequest.proto\x12\x08protobuf\"z\n\x18RerouteMarketDataRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x12\n\x05\x63onId\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x15\n\x08\x65xchange\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\x08\n\x06_conIdB\x0b\n\t_exchangeBH\n\x16\x63om.ib.client.protobufB\x1dRerouteMarketDataRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'RerouteMarketDataRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\035RerouteMarketDataRequestProto\252\002\016IBApi.protobuf' + _globals['_REROUTEMARKETDATAREQUEST']._serialized_start=44 + _globals['_REROUTEMARKETDATAREQUEST']._serialized_end=166 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/RerouteMarketDataRequest_pb2.pyi b/ib_async/protobuf/RerouteMarketDataRequest_pb2.pyi new file mode 100644 index 00000000..2f0f7597 --- /dev/null +++ b/ib_async/protobuf/RerouteMarketDataRequest_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class RerouteMarketDataRequest(_message.Message): + __slots__ = ("reqId", "conId", "exchange") + REQID_FIELD_NUMBER: _ClassVar[int] + CONID_FIELD_NUMBER: _ClassVar[int] + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + reqId: int + conId: int + exchange: str + def __init__(self, reqId: _Optional[int] = ..., conId: _Optional[int] = ..., exchange: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/RerouteMarketDepthRequest_pb2.py b/ib_async/protobuf/RerouteMarketDepthRequest_pb2.py new file mode 100644 index 00000000..fcf198eb --- /dev/null +++ b/ib_async/protobuf/RerouteMarketDepthRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: RerouteMarketDepthRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'RerouteMarketDepthRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fRerouteMarketDepthRequest.proto\x12\x08protobuf\"{\n\x19RerouteMarketDepthRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x12\n\x05\x63onId\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x15\n\x08\x65xchange\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\x08\n\x06_conIdB\x0b\n\t_exchangeBI\n\x16\x63om.ib.client.protobufB\x1eRerouteMarketDepthRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'RerouteMarketDepthRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\036RerouteMarketDepthRequestProto\252\002\016IBApi.protobuf' + _globals['_REROUTEMARKETDEPTHREQUEST']._serialized_start=45 + _globals['_REROUTEMARKETDEPTHREQUEST']._serialized_end=168 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/RerouteMarketDepthRequest_pb2.pyi b/ib_async/protobuf/RerouteMarketDepthRequest_pb2.pyi new file mode 100644 index 00000000..5e661a8e --- /dev/null +++ b/ib_async/protobuf/RerouteMarketDepthRequest_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class RerouteMarketDepthRequest(_message.Message): + __slots__ = ("reqId", "conId", "exchange") + REQID_FIELD_NUMBER: _ClassVar[int] + CONID_FIELD_NUMBER: _ClassVar[int] + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + reqId: int + conId: int + exchange: str + def __init__(self, reqId: _Optional[int] = ..., conId: _Optional[int] = ..., exchange: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/ScannerDataElement_pb2.py b/ib_async/protobuf/ScannerDataElement_pb2.py new file mode 100644 index 00000000..6358cbec --- /dev/null +++ b/ib_async/protobuf/ScannerDataElement_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ScannerDataElement.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ScannerDataElement.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18ScannerDataElement.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xa6\x02\n\x12ScannerDataElement\x12\x11\n\x04rank\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x17\n\nmarketName\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x15\n\x08\x64istance\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x16\n\tbenchmark\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x17\n\nprojection\x18\x06 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08\x63omboKey\x18\x07 \x01(\tH\x06\x88\x01\x01\x42\x07\n\x05_rankB\x0b\n\t_contractB\r\n\x0b_marketNameB\x0b\n\t_distanceB\x0c\n\n_benchmarkB\r\n\x0b_projectionB\x0b\n\t_comboKeyBB\n\x16\x63om.ib.client.protobufB\x17ScannerDataElementProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ScannerDataElement_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027ScannerDataElementProto\252\002\016IBApi.protobuf' + _globals['_SCANNERDATAELEMENT']._serialized_start=55 + _globals['_SCANNERDATAELEMENT']._serialized_end=349 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ScannerDataElement_pb2.pyi b/ib_async/protobuf/ScannerDataElement_pb2.pyi new file mode 100644 index 00000000..5604cfaa --- /dev/null +++ b/ib_async/protobuf/ScannerDataElement_pb2.pyi @@ -0,0 +1,25 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ScannerDataElement(_message.Message): + __slots__ = ("rank", "contract", "marketName", "distance", "benchmark", "projection", "comboKey") + RANK_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + MARKETNAME_FIELD_NUMBER: _ClassVar[int] + DISTANCE_FIELD_NUMBER: _ClassVar[int] + BENCHMARK_FIELD_NUMBER: _ClassVar[int] + PROJECTION_FIELD_NUMBER: _ClassVar[int] + COMBOKEY_FIELD_NUMBER: _ClassVar[int] + rank: int + contract: _Contract_pb2.Contract + marketName: str + distance: str + benchmark: str + projection: str + comboKey: str + def __init__(self, rank: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., marketName: _Optional[str] = ..., distance: _Optional[str] = ..., benchmark: _Optional[str] = ..., projection: _Optional[str] = ..., comboKey: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/ScannerData_pb2.py b/ib_async/protobuf/ScannerData_pb2.py new file mode 100644 index 00000000..be55e7f4 --- /dev/null +++ b/ib_async/protobuf/ScannerData_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ScannerData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ScannerData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import ScannerDataElement_pb2 as ScannerDataElement__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11ScannerData.proto\x12\x08protobuf\x1a\x18ScannerDataElement.proto\"e\n\x0bScannerData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x38\n\x12scannerDataElement\x18\x02 \x03(\x0b\x32\x1c.protobuf.ScannerDataElementB\x08\n\x06_reqIdB;\n\x16\x63om.ib.client.protobufB\x10ScannerDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ScannerData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020ScannerDataProto\252\002\016IBApi.protobuf' + _globals['_SCANNERDATA']._serialized_start=57 + _globals['_SCANNERDATA']._serialized_end=158 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ScannerData_pb2.pyi b/ib_async/protobuf/ScannerData_pb2.pyi new file mode 100644 index 00000000..4ec8ca45 --- /dev/null +++ b/ib_async/protobuf/ScannerData_pb2.pyi @@ -0,0 +1,16 @@ +import ScannerDataElement_pb2 as _ScannerDataElement_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ScannerData(_message.Message): + __slots__ = ("reqId", "scannerDataElement") + REQID_FIELD_NUMBER: _ClassVar[int] + SCANNERDATAELEMENT_FIELD_NUMBER: _ClassVar[int] + reqId: int + scannerDataElement: _containers.RepeatedCompositeFieldContainer[_ScannerDataElement_pb2.ScannerDataElement] + def __init__(self, reqId: _Optional[int] = ..., scannerDataElement: _Optional[_Iterable[_Union[_ScannerDataElement_pb2.ScannerDataElement, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/ScannerParametersRequest_pb2.py b/ib_async/protobuf/ScannerParametersRequest_pb2.py new file mode 100644 index 00000000..b4f2d2ce --- /dev/null +++ b/ib_async/protobuf/ScannerParametersRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ScannerParametersRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ScannerParametersRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eScannerParametersRequest.proto\x12\x08protobuf\"\x1a\n\x18ScannerParametersRequestBH\n\x16\x63om.ib.client.protobufB\x1dScannerParametersRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ScannerParametersRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\035ScannerParametersRequestProto\252\002\016IBApi.protobuf' + _globals['_SCANNERPARAMETERSREQUEST']._serialized_start=44 + _globals['_SCANNERPARAMETERSREQUEST']._serialized_end=70 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ScannerParametersRequest_pb2.pyi b/ib_async/protobuf/ScannerParametersRequest_pb2.pyi new file mode 100644 index 00000000..12c5697f --- /dev/null +++ b/ib_async/protobuf/ScannerParametersRequest_pb2.pyi @@ -0,0 +1,9 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class ScannerParametersRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... diff --git a/ib_async/protobuf/ScannerParameters_pb2.py b/ib_async/protobuf/ScannerParameters_pb2.py new file mode 100644 index 00000000..d8f9697a --- /dev/null +++ b/ib_async/protobuf/ScannerParameters_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ScannerParameters.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ScannerParameters.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17ScannerParameters.proto\x12\x08protobuf\"-\n\x11ScannerParameters\x12\x10\n\x03xml\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x06\n\x04_xmlBA\n\x16\x63om.ib.client.protobufB\x16ScannerParametersProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ScannerParameters_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026ScannerParametersProto\252\002\016IBApi.protobuf' + _globals['_SCANNERPARAMETERS']._serialized_start=37 + _globals['_SCANNERPARAMETERS']._serialized_end=82 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ScannerParameters_pb2.pyi b/ib_async/protobuf/ScannerParameters_pb2.pyi new file mode 100644 index 00000000..31f4a45a --- /dev/null +++ b/ib_async/protobuf/ScannerParameters_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ScannerParameters(_message.Message): + __slots__ = ("xml",) + XML_FIELD_NUMBER: _ClassVar[int] + xml: str + def __init__(self, xml: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/ScannerSubscriptionRequest_pb2.py b/ib_async/protobuf/ScannerSubscriptionRequest_pb2.py new file mode 100644 index 00000000..768c9910 --- /dev/null +++ b/ib_async/protobuf/ScannerSubscriptionRequest_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ScannerSubscriptionRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ScannerSubscriptionRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import ScannerSubscription_pb2 as ScannerSubscription__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n ScannerSubscriptionRequest.proto\x12\x08protobuf\x1a\x19ScannerSubscription.proto\"\x93\x01\n\x1aScannerSubscriptionRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12?\n\x13scannerSubscription\x18\x02 \x01(\x0b\x32\x1d.protobuf.ScannerSubscriptionH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x16\n\x14_scannerSubscriptionBJ\n\x16\x63om.ib.client.protobufB\x1fScannerSubscriptionRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ScannerSubscriptionRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\037ScannerSubscriptionRequestProto\252\002\016IBApi.protobuf' + _globals['_SCANNERSUBSCRIPTIONREQUEST']._serialized_start=74 + _globals['_SCANNERSUBSCRIPTIONREQUEST']._serialized_end=221 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ScannerSubscriptionRequest_pb2.pyi b/ib_async/protobuf/ScannerSubscriptionRequest_pb2.pyi new file mode 100644 index 00000000..ba841701 --- /dev/null +++ b/ib_async/protobuf/ScannerSubscriptionRequest_pb2.pyi @@ -0,0 +1,15 @@ +import ScannerSubscription_pb2 as _ScannerSubscription_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ScannerSubscriptionRequest(_message.Message): + __slots__ = ("reqId", "scannerSubscription") + REQID_FIELD_NUMBER: _ClassVar[int] + SCANNERSUBSCRIPTION_FIELD_NUMBER: _ClassVar[int] + reqId: int + scannerSubscription: _ScannerSubscription_pb2.ScannerSubscription + def __init__(self, reqId: _Optional[int] = ..., scannerSubscription: _Optional[_Union[_ScannerSubscription_pb2.ScannerSubscription, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/ScannerSubscription_pb2.py b/ib_async/protobuf/ScannerSubscription_pb2.py new file mode 100644 index 00000000..0be4bd45 --- /dev/null +++ b/ib_async/protobuf/ScannerSubscription_pb2.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: ScannerSubscription.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'ScannerSubscription.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19ScannerSubscription.proto\x12\x08protobuf\"\xed\n\n\x13ScannerSubscription\x12\x19\n\x0cnumberOfRows\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x17\n\ninstrument\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0clocationCode\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x15\n\x08scanCode\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nabovePrice\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x17\n\nbelowPrice\x18\x06 \x01(\x01H\x05\x88\x01\x01\x12\x18\n\x0b\x61\x62oveVolume\x18\x07 \x01(\x03H\x06\x88\x01\x01\x12\x1b\n\x0emarketCapAbove\x18\x08 \x01(\x01H\x07\x88\x01\x01\x12\x1b\n\x0emarketCapBelow\x18\t \x01(\x01H\x08\x88\x01\x01\x12\x1d\n\x10moodyRatingAbove\x18\n \x01(\tH\t\x88\x01\x01\x12\x1d\n\x10moodyRatingBelow\x18\x0b \x01(\tH\n\x88\x01\x01\x12\x1a\n\rspRatingAbove\x18\x0c \x01(\tH\x0b\x88\x01\x01\x12\x1a\n\rspRatingBelow\x18\r \x01(\tH\x0c\x88\x01\x01\x12\x1e\n\x11maturityDateAbove\x18\x0e \x01(\tH\r\x88\x01\x01\x12\x1e\n\x11maturityDateBelow\x18\x0f \x01(\tH\x0e\x88\x01\x01\x12\x1c\n\x0f\x63ouponRateAbove\x18\x10 \x01(\x01H\x0f\x88\x01\x01\x12\x1c\n\x0f\x63ouponRateBelow\x18\x11 \x01(\x01H\x10\x88\x01\x01\x12\x1f\n\x12\x65xcludeConvertible\x18\x12 \x01(\x08H\x11\x88\x01\x01\x12%\n\x18\x61verageOptionVolumeAbove\x18\x13 \x01(\x03H\x12\x88\x01\x01\x12 \n\x13scannerSettingPairs\x18\x14 \x01(\tH\x13\x88\x01\x01\x12\x1c\n\x0fstockTypeFilter\x18\x15 \x01(\tH\x14\x88\x01\x01\x12m\n scannerSubscriptionFilterOptions\x18\x16 \x03(\x0b\x32\x43.protobuf.ScannerSubscription.ScannerSubscriptionFilterOptionsEntry\x12\x61\n\x1ascannerSubscriptionOptions\x18\x17 \x03(\x0b\x32=.protobuf.ScannerSubscription.ScannerSubscriptionOptionsEntry\x1aG\n%ScannerSubscriptionFilterOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x41\n\x1fScannerSubscriptionOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x0f\n\r_numberOfRowsB\r\n\x0b_instrumentB\x0f\n\r_locationCodeB\x0b\n\t_scanCodeB\r\n\x0b_abovePriceB\r\n\x0b_belowPriceB\x0e\n\x0c_aboveVolumeB\x11\n\x0f_marketCapAboveB\x11\n\x0f_marketCapBelowB\x13\n\x11_moodyRatingAboveB\x13\n\x11_moodyRatingBelowB\x10\n\x0e_spRatingAboveB\x10\n\x0e_spRatingBelowB\x14\n\x12_maturityDateAboveB\x14\n\x12_maturityDateBelowB\x12\n\x10_couponRateAboveB\x12\n\x10_couponRateBelowB\x15\n\x13_excludeConvertibleB\x1b\n\x19_averageOptionVolumeAboveB\x16\n\x14_scannerSettingPairsB\x12\n\x10_stockTypeFilterBC\n\x16\x63om.ib.client.protobufB\x18ScannerSubscriptionProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ScannerSubscription_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030ScannerSubscriptionProto\252\002\016IBApi.protobuf' + _globals['_SCANNERSUBSCRIPTION_SCANNERSUBSCRIPTIONFILTEROPTIONSENTRY']._loaded_options = None + _globals['_SCANNERSUBSCRIPTION_SCANNERSUBSCRIPTIONFILTEROPTIONSENTRY']._serialized_options = b'8\001' + _globals['_SCANNERSUBSCRIPTION_SCANNERSUBSCRIPTIONOPTIONSENTRY']._loaded_options = None + _globals['_SCANNERSUBSCRIPTION_SCANNERSUBSCRIPTIONOPTIONSENTRY']._serialized_options = b'8\001' + _globals['_SCANNERSUBSCRIPTION']._serialized_start=40 + _globals['_SCANNERSUBSCRIPTION']._serialized_end=1429 + _globals['_SCANNERSUBSCRIPTION_SCANNERSUBSCRIPTIONFILTEROPTIONSENTRY']._serialized_start=887 + _globals['_SCANNERSUBSCRIPTION_SCANNERSUBSCRIPTIONFILTEROPTIONSENTRY']._serialized_end=958 + _globals['_SCANNERSUBSCRIPTION_SCANNERSUBSCRIPTIONOPTIONSENTRY']._serialized_start=960 + _globals['_SCANNERSUBSCRIPTION_SCANNERSUBSCRIPTIONOPTIONSENTRY']._serialized_end=1025 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/ScannerSubscription_pb2.pyi b/ib_async/protobuf/ScannerSubscription_pb2.pyi new file mode 100644 index 00000000..9aefa63a --- /dev/null +++ b/ib_async/protobuf/ScannerSubscription_pb2.pyi @@ -0,0 +1,71 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ScannerSubscription(_message.Message): + __slots__ = ("numberOfRows", "instrument", "locationCode", "scanCode", "abovePrice", "belowPrice", "aboveVolume", "marketCapAbove", "marketCapBelow", "moodyRatingAbove", "moodyRatingBelow", "spRatingAbove", "spRatingBelow", "maturityDateAbove", "maturityDateBelow", "couponRateAbove", "couponRateBelow", "excludeConvertible", "averageOptionVolumeAbove", "scannerSettingPairs", "stockTypeFilter", "scannerSubscriptionFilterOptions", "scannerSubscriptionOptions") + class ScannerSubscriptionFilterOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class ScannerSubscriptionOptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + NUMBEROFROWS_FIELD_NUMBER: _ClassVar[int] + INSTRUMENT_FIELD_NUMBER: _ClassVar[int] + LOCATIONCODE_FIELD_NUMBER: _ClassVar[int] + SCANCODE_FIELD_NUMBER: _ClassVar[int] + ABOVEPRICE_FIELD_NUMBER: _ClassVar[int] + BELOWPRICE_FIELD_NUMBER: _ClassVar[int] + ABOVEVOLUME_FIELD_NUMBER: _ClassVar[int] + MARKETCAPABOVE_FIELD_NUMBER: _ClassVar[int] + MARKETCAPBELOW_FIELD_NUMBER: _ClassVar[int] + MOODYRATINGABOVE_FIELD_NUMBER: _ClassVar[int] + MOODYRATINGBELOW_FIELD_NUMBER: _ClassVar[int] + SPRATINGABOVE_FIELD_NUMBER: _ClassVar[int] + SPRATINGBELOW_FIELD_NUMBER: _ClassVar[int] + MATURITYDATEABOVE_FIELD_NUMBER: _ClassVar[int] + MATURITYDATEBELOW_FIELD_NUMBER: _ClassVar[int] + COUPONRATEABOVE_FIELD_NUMBER: _ClassVar[int] + COUPONRATEBELOW_FIELD_NUMBER: _ClassVar[int] + EXCLUDECONVERTIBLE_FIELD_NUMBER: _ClassVar[int] + AVERAGEOPTIONVOLUMEABOVE_FIELD_NUMBER: _ClassVar[int] + SCANNERSETTINGPAIRS_FIELD_NUMBER: _ClassVar[int] + STOCKTYPEFILTER_FIELD_NUMBER: _ClassVar[int] + SCANNERSUBSCRIPTIONFILTEROPTIONS_FIELD_NUMBER: _ClassVar[int] + SCANNERSUBSCRIPTIONOPTIONS_FIELD_NUMBER: _ClassVar[int] + numberOfRows: int + instrument: str + locationCode: str + scanCode: str + abovePrice: float + belowPrice: float + aboveVolume: int + marketCapAbove: float + marketCapBelow: float + moodyRatingAbove: str + moodyRatingBelow: str + spRatingAbove: str + spRatingBelow: str + maturityDateAbove: str + maturityDateBelow: str + couponRateAbove: float + couponRateBelow: float + excludeConvertible: bool + averageOptionVolumeAbove: int + scannerSettingPairs: str + stockTypeFilter: str + scannerSubscriptionFilterOptions: _containers.ScalarMap[str, str] + scannerSubscriptionOptions: _containers.ScalarMap[str, str] + def __init__(self, numberOfRows: _Optional[int] = ..., instrument: _Optional[str] = ..., locationCode: _Optional[str] = ..., scanCode: _Optional[str] = ..., abovePrice: _Optional[float] = ..., belowPrice: _Optional[float] = ..., aboveVolume: _Optional[int] = ..., marketCapAbove: _Optional[float] = ..., marketCapBelow: _Optional[float] = ..., moodyRatingAbove: _Optional[str] = ..., moodyRatingBelow: _Optional[str] = ..., spRatingAbove: _Optional[str] = ..., spRatingBelow: _Optional[str] = ..., maturityDateAbove: _Optional[str] = ..., maturityDateBelow: _Optional[str] = ..., couponRateAbove: _Optional[float] = ..., couponRateBelow: _Optional[float] = ..., excludeConvertible: bool = ..., averageOptionVolumeAbove: _Optional[int] = ..., scannerSettingPairs: _Optional[str] = ..., stockTypeFilter: _Optional[str] = ..., scannerSubscriptionFilterOptions: _Optional[_Mapping[str, str]] = ..., scannerSubscriptionOptions: _Optional[_Mapping[str, str]] = ...) -> None: ... diff --git a/ib_async/protobuf/SecDefOptParameterEnd_pb2.py b/ib_async/protobuf/SecDefOptParameterEnd_pb2.py new file mode 100644 index 00000000..c97b06dc --- /dev/null +++ b/ib_async/protobuf/SecDefOptParameterEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SecDefOptParameterEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SecDefOptParameterEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bSecDefOptParameterEnd.proto\x12\x08protobuf\"5\n\x15SecDefOptParameterEnd\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBE\n\x16\x63om.ib.client.protobufB\x1aSecDefOptParameterEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SecDefOptParameterEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032SecDefOptParameterEndProto\252\002\016IBApi.protobuf' + _globals['_SECDEFOPTPARAMETEREND']._serialized_start=41 + _globals['_SECDEFOPTPARAMETEREND']._serialized_end=94 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SecDefOptParameterEnd_pb2.pyi b/ib_async/protobuf/SecDefOptParameterEnd_pb2.pyi new file mode 100644 index 00000000..1e9632d8 --- /dev/null +++ b/ib_async/protobuf/SecDefOptParameterEnd_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class SecDefOptParameterEnd(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/SecDefOptParameter_pb2.py b/ib_async/protobuf/SecDefOptParameter_pb2.py new file mode 100644 index 00000000..72a77b61 --- /dev/null +++ b/ib_async/protobuf/SecDefOptParameter_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SecDefOptParameter.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SecDefOptParameter.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18SecDefOptParameter.proto\x12\x08protobuf\"\x82\x02\n\x12SecDefOptParameter\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08\x65xchange\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0funderlyingConId\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12\x19\n\x0ctradingClass\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nmultiplier\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x13\n\x0b\x65xpirations\x18\x06 \x03(\t\x12\x0f\n\x07strikes\x18\x07 \x03(\x01\x42\x08\n\x06_reqIdB\x0b\n\t_exchangeB\x12\n\x10_underlyingConIdB\x0f\n\r_tradingClassB\r\n\x0b_multiplierBB\n\x16\x63om.ib.client.protobufB\x17SecDefOptParameterProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SecDefOptParameter_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027SecDefOptParameterProto\252\002\016IBApi.protobuf' + _globals['_SECDEFOPTPARAMETER']._serialized_start=39 + _globals['_SECDEFOPTPARAMETER']._serialized_end=297 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SecDefOptParameter_pb2.pyi b/ib_async/protobuf/SecDefOptParameter_pb2.pyi new file mode 100644 index 00000000..c24dbb5a --- /dev/null +++ b/ib_async/protobuf/SecDefOptParameter_pb2.pyi @@ -0,0 +1,25 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class SecDefOptParameter(_message.Message): + __slots__ = ("reqId", "exchange", "underlyingConId", "tradingClass", "multiplier", "expirations", "strikes") + REQID_FIELD_NUMBER: _ClassVar[int] + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + UNDERLYINGCONID_FIELD_NUMBER: _ClassVar[int] + TRADINGCLASS_FIELD_NUMBER: _ClassVar[int] + MULTIPLIER_FIELD_NUMBER: _ClassVar[int] + EXPIRATIONS_FIELD_NUMBER: _ClassVar[int] + STRIKES_FIELD_NUMBER: _ClassVar[int] + reqId: int + exchange: str + underlyingConId: int + tradingClass: str + multiplier: str + expirations: _containers.RepeatedScalarFieldContainer[str] + strikes: _containers.RepeatedScalarFieldContainer[float] + def __init__(self, reqId: _Optional[int] = ..., exchange: _Optional[str] = ..., underlyingConId: _Optional[int] = ..., tradingClass: _Optional[str] = ..., multiplier: _Optional[str] = ..., expirations: _Optional[_Iterable[str]] = ..., strikes: _Optional[_Iterable[float]] = ...) -> None: ... diff --git a/ib_async/protobuf/SecDefOptParamsRequest_pb2.py b/ib_async/protobuf/SecDefOptParamsRequest_pb2.py new file mode 100644 index 00000000..6ce18c0a --- /dev/null +++ b/ib_async/protobuf/SecDefOptParamsRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SecDefOptParamsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SecDefOptParamsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cSecDefOptParamsRequest.proto\x12\x08protobuf\"\x82\x02\n\x16SecDefOptParamsRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x1d\n\x10underlyingSymbol\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0e\x66utFopExchange\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1e\n\x11underlyingSecType\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x1c\n\x0funderlyingConId\x18\x05 \x01(\x05H\x04\x88\x01\x01\x42\x08\n\x06_reqIdB\x13\n\x11_underlyingSymbolB\x11\n\x0f_futFopExchangeB\x14\n\x12_underlyingSecTypeB\x12\n\x10_underlyingConIdBF\n\x16\x63om.ib.client.protobufB\x1bSecDefOptParamsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SecDefOptParamsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\033SecDefOptParamsRequestProto\252\002\016IBApi.protobuf' + _globals['_SECDEFOPTPARAMSREQUEST']._serialized_start=43 + _globals['_SECDEFOPTPARAMSREQUEST']._serialized_end=301 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SecDefOptParamsRequest_pb2.pyi b/ib_async/protobuf/SecDefOptParamsRequest_pb2.pyi new file mode 100644 index 00000000..4c3ec2ac --- /dev/null +++ b/ib_async/protobuf/SecDefOptParamsRequest_pb2.pyi @@ -0,0 +1,19 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class SecDefOptParamsRequest(_message.Message): + __slots__ = ("reqId", "underlyingSymbol", "futFopExchange", "underlyingSecType", "underlyingConId") + REQID_FIELD_NUMBER: _ClassVar[int] + UNDERLYINGSYMBOL_FIELD_NUMBER: _ClassVar[int] + FUTFOPEXCHANGE_FIELD_NUMBER: _ClassVar[int] + UNDERLYINGSECTYPE_FIELD_NUMBER: _ClassVar[int] + UNDERLYINGCONID_FIELD_NUMBER: _ClassVar[int] + reqId: int + underlyingSymbol: str + futFopExchange: str + underlyingSecType: str + underlyingConId: int + def __init__(self, reqId: _Optional[int] = ..., underlyingSymbol: _Optional[str] = ..., futFopExchange: _Optional[str] = ..., underlyingSecType: _Optional[str] = ..., underlyingConId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/SetServerLogLevelRequest_pb2.py b/ib_async/protobuf/SetServerLogLevelRequest_pb2.py new file mode 100644 index 00000000..4c4bac1d --- /dev/null +++ b/ib_async/protobuf/SetServerLogLevelRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SetServerLogLevelRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SetServerLogLevelRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eSetServerLogLevelRequest.proto\x12\x08protobuf\">\n\x18SetServerLogLevelRequest\x12\x15\n\x08logLevel\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x0b\n\t_logLevelBH\n\x16\x63om.ib.client.protobufB\x1dSetServerLogLevelRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SetServerLogLevelRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\035SetServerLogLevelRequestProto\252\002\016IBApi.protobuf' + _globals['_SETSERVERLOGLEVELREQUEST']._serialized_start=44 + _globals['_SETSERVERLOGLEVELREQUEST']._serialized_end=106 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SetServerLogLevelRequest_pb2.pyi b/ib_async/protobuf/SetServerLogLevelRequest_pb2.pyi new file mode 100644 index 00000000..67d0fe3f --- /dev/null +++ b/ib_async/protobuf/SetServerLogLevelRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class SetServerLogLevelRequest(_message.Message): + __slots__ = ("logLevel",) + LOGLEVEL_FIELD_NUMBER: _ClassVar[int] + logLevel: int + def __init__(self, logLevel: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/SmartComponent_pb2.py b/ib_async/protobuf/SmartComponent_pb2.py new file mode 100644 index 00000000..ba343333 --- /dev/null +++ b/ib_async/protobuf/SmartComponent_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SmartComponent.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SmartComponent.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14SmartComponent.proto\x12\x08protobuf\"\x8a\x01\n\x0eSmartComponent\x12\x16\n\tbitNumber\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08\x65xchange\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0e\x65xchangeLetter\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x0c\n\n_bitNumberB\x0b\n\t_exchangeB\x11\n\x0f_exchangeLetterB>\n\x16\x63om.ib.client.protobufB\x13SmartComponentProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SmartComponent_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023SmartComponentProto\252\002\016IBApi.protobuf' + _globals['_SMARTCOMPONENT']._serialized_start=35 + _globals['_SMARTCOMPONENT']._serialized_end=173 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SmartComponent_pb2.pyi b/ib_async/protobuf/SmartComponent_pb2.pyi new file mode 100644 index 00000000..33fcd79d --- /dev/null +++ b/ib_async/protobuf/SmartComponent_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class SmartComponent(_message.Message): + __slots__ = ("bitNumber", "exchange", "exchangeLetter") + BITNUMBER_FIELD_NUMBER: _ClassVar[int] + EXCHANGE_FIELD_NUMBER: _ClassVar[int] + EXCHANGELETTER_FIELD_NUMBER: _ClassVar[int] + bitNumber: int + exchange: str + exchangeLetter: str + def __init__(self, bitNumber: _Optional[int] = ..., exchange: _Optional[str] = ..., exchangeLetter: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/SmartComponentsRequest_pb2.py b/ib_async/protobuf/SmartComponentsRequest_pb2.py new file mode 100644 index 00000000..d9560b15 --- /dev/null +++ b/ib_async/protobuf/SmartComponentsRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SmartComponentsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SmartComponentsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cSmartComponentsRequest.proto\x12\x08protobuf\"`\n\x16SmartComponentsRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x18\n\x0b\x62\x62oExchange\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x0e\n\x0c_bboExchangeBF\n\x16\x63om.ib.client.protobufB\x1bSmartComponentsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SmartComponentsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\033SmartComponentsRequestProto\252\002\016IBApi.protobuf' + _globals['_SMARTCOMPONENTSREQUEST']._serialized_start=42 + _globals['_SMARTCOMPONENTSREQUEST']._serialized_end=138 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SmartComponentsRequest_pb2.pyi b/ib_async/protobuf/SmartComponentsRequest_pb2.pyi new file mode 100644 index 00000000..9e2f83f4 --- /dev/null +++ b/ib_async/protobuf/SmartComponentsRequest_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class SmartComponentsRequest(_message.Message): + __slots__ = ("reqId", "bboExchange") + REQID_FIELD_NUMBER: _ClassVar[int] + BBOEXCHANGE_FIELD_NUMBER: _ClassVar[int] + reqId: int + bboExchange: str + def __init__(self, reqId: _Optional[int] = ..., bboExchange: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/SmartComponents_pb2.py b/ib_async/protobuf/SmartComponents_pb2.py new file mode 100644 index 00000000..d97e0eb3 --- /dev/null +++ b/ib_async/protobuf/SmartComponents_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SmartComponents.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SmartComponents.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import SmartComponent_pb2 as SmartComponent__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15SmartComponents.proto\x12\x08protobuf\x1a\x14SmartComponent.proto\"b\n\x0fSmartComponents\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x31\n\x0fsmartComponents\x18\x02 \x03(\x0b\x32\x18.protobuf.SmartComponentB\x08\n\x06_reqIdB?\n\x16\x63om.ib.client.protobufB\x14SmartComponentsProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SmartComponents_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024SmartComponentsProto\252\002\016IBApi.protobuf' + _globals['_SMARTCOMPONENTS']._serialized_start=57 + _globals['_SMARTCOMPONENTS']._serialized_end=155 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SmartComponents_pb2.pyi b/ib_async/protobuf/SmartComponents_pb2.pyi new file mode 100644 index 00000000..d269e201 --- /dev/null +++ b/ib_async/protobuf/SmartComponents_pb2.pyi @@ -0,0 +1,16 @@ +import SmartComponent_pb2 as _SmartComponent_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class SmartComponents(_message.Message): + __slots__ = ("reqId", "smartComponents") + REQID_FIELD_NUMBER: _ClassVar[int] + SMARTCOMPONENTS_FIELD_NUMBER: _ClassVar[int] + reqId: int + smartComponents: _containers.RepeatedCompositeFieldContainer[_SmartComponent_pb2.SmartComponent] + def __init__(self, reqId: _Optional[int] = ..., smartComponents: _Optional[_Iterable[_Union[_SmartComponent_pb2.SmartComponent, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/SoftDollarTier_pb2.py b/ib_async/protobuf/SoftDollarTier_pb2.py new file mode 100644 index 00000000..174be03e --- /dev/null +++ b/ib_async/protobuf/SoftDollarTier_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SoftDollarTier.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SoftDollarTier.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14SoftDollarTier.proto\x12\x08protobuf\"t\n\x0eSoftDollarTier\x12\x11\n\x04name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05value\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x64isplayName\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x07\n\x05_nameB\x08\n\x06_valueB\x0e\n\x0c_displayNameB>\n\x16\x63om.ib.client.protobufB\x13SoftDollarTierProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SoftDollarTier_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023SoftDollarTierProto\252\002\016IBApi.protobuf' + _globals['_SOFTDOLLARTIER']._serialized_start=34 + _globals['_SOFTDOLLARTIER']._serialized_end=150 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SoftDollarTier_pb2.pyi b/ib_async/protobuf/SoftDollarTier_pb2.pyi new file mode 100644 index 00000000..2fb60bd1 --- /dev/null +++ b/ib_async/protobuf/SoftDollarTier_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class SoftDollarTier(_message.Message): + __slots__ = ("name", "value", "displayName") + NAME_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + DISPLAYNAME_FIELD_NUMBER: _ClassVar[int] + name: str + value: str + displayName: str + def __init__(self, name: _Optional[str] = ..., value: _Optional[str] = ..., displayName: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/SoftDollarTiersRequest_pb2.py b/ib_async/protobuf/SoftDollarTiersRequest_pb2.py new file mode 100644 index 00000000..0b83163f --- /dev/null +++ b/ib_async/protobuf/SoftDollarTiersRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SoftDollarTiersRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SoftDollarTiersRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cSoftDollarTiersRequest.proto\x12\x08protobuf\"6\n\x16SoftDollarTiersRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBF\n\x16\x63om.ib.client.protobufB\x1bSoftDollarTiersRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SoftDollarTiersRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\033SoftDollarTiersRequestProto\252\002\016IBApi.protobuf' + _globals['_SOFTDOLLARTIERSREQUEST']._serialized_start=42 + _globals['_SOFTDOLLARTIERSREQUEST']._serialized_end=96 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SoftDollarTiersRequest_pb2.pyi b/ib_async/protobuf/SoftDollarTiersRequest_pb2.pyi new file mode 100644 index 00000000..b5d9c42f --- /dev/null +++ b/ib_async/protobuf/SoftDollarTiersRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class SoftDollarTiersRequest(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/SoftDollarTiers_pb2.py b/ib_async/protobuf/SoftDollarTiers_pb2.py new file mode 100644 index 00000000..b686d009 --- /dev/null +++ b/ib_async/protobuf/SoftDollarTiers_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SoftDollarTiers.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SoftDollarTiers.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import SoftDollarTier_pb2 as SoftDollarTier__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15SoftDollarTiers.proto\x12\x08protobuf\x1a\x14SoftDollarTier.proto\"b\n\x0fSoftDollarTiers\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x31\n\x0fsoftDollarTiers\x18\x02 \x03(\x0b\x32\x18.protobuf.SoftDollarTierB\x08\n\x06_reqIdB?\n\x16\x63om.ib.client.protobufB\x14SoftDollarTiersProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SoftDollarTiers_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024SoftDollarTiersProto\252\002\016IBApi.protobuf' + _globals['_SOFTDOLLARTIERS']._serialized_start=57 + _globals['_SOFTDOLLARTIERS']._serialized_end=155 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SoftDollarTiers_pb2.pyi b/ib_async/protobuf/SoftDollarTiers_pb2.pyi new file mode 100644 index 00000000..a10d38de --- /dev/null +++ b/ib_async/protobuf/SoftDollarTiers_pb2.pyi @@ -0,0 +1,16 @@ +import SoftDollarTier_pb2 as _SoftDollarTier_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class SoftDollarTiers(_message.Message): + __slots__ = ("reqId", "softDollarTiers") + REQID_FIELD_NUMBER: _ClassVar[int] + SOFTDOLLARTIERS_FIELD_NUMBER: _ClassVar[int] + reqId: int + softDollarTiers: _containers.RepeatedCompositeFieldContainer[_SoftDollarTier_pb2.SoftDollarTier] + def __init__(self, reqId: _Optional[int] = ..., softDollarTiers: _Optional[_Iterable[_Union[_SoftDollarTier_pb2.SoftDollarTier, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/StartApiRequest_pb2.py b/ib_async/protobuf/StartApiRequest_pb2.py new file mode 100644 index 00000000..5392d0fd --- /dev/null +++ b/ib_async/protobuf/StartApiRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: StartApiRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'StartApiRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15StartApiRequest.proto\x12\x08protobuf\"q\n\x0fStartApiRequest\x12\x15\n\x08\x63lientId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12!\n\x14optionalCapabilities\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x0b\n\t_clientIdB\x17\n\x15_optionalCapabilitiesB?\n\x16\x63om.ib.client.protobufB\x14StartApiRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'StartApiRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024StartApiRequestProto\252\002\016IBApi.protobuf' + _globals['_STARTAPIREQUEST']._serialized_start=35 + _globals['_STARTAPIREQUEST']._serialized_end=148 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/StartApiRequest_pb2.pyi b/ib_async/protobuf/StartApiRequest_pb2.pyi new file mode 100644 index 00000000..48503130 --- /dev/null +++ b/ib_async/protobuf/StartApiRequest_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class StartApiRequest(_message.Message): + __slots__ = ("clientId", "optionalCapabilities") + CLIENTID_FIELD_NUMBER: _ClassVar[int] + OPTIONALCAPABILITIES_FIELD_NUMBER: _ClassVar[int] + clientId: int + optionalCapabilities: str + def __init__(self, clientId: _Optional[int] = ..., optionalCapabilities: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/SubscribeToGroupEventsRequest_pb2.py b/ib_async/protobuf/SubscribeToGroupEventsRequest_pb2.py new file mode 100644 index 00000000..c61d0072 --- /dev/null +++ b/ib_async/protobuf/SubscribeToGroupEventsRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SubscribeToGroupEventsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SubscribeToGroupEventsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#SubscribeToGroupEventsRequest.proto\x12\x08protobuf\"_\n\x1dSubscribeToGroupEventsRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07groupId\x18\x02 \x01(\x05H\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_groupIdBM\n\x16\x63om.ib.client.protobufB\"SubscribeToGroupEventsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SubscribeToGroupEventsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\"SubscribeToGroupEventsRequestProto\252\002\016IBApi.protobuf' + _globals['_SUBSCRIBETOGROUPEVENTSREQUEST']._serialized_start=49 + _globals['_SUBSCRIBETOGROUPEVENTSREQUEST']._serialized_end=144 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SubscribeToGroupEventsRequest_pb2.pyi b/ib_async/protobuf/SubscribeToGroupEventsRequest_pb2.pyi new file mode 100644 index 00000000..8b9d2490 --- /dev/null +++ b/ib_async/protobuf/SubscribeToGroupEventsRequest_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class SubscribeToGroupEventsRequest(_message.Message): + __slots__ = ("reqId", "groupId") + REQID_FIELD_NUMBER: _ClassVar[int] + GROUPID_FIELD_NUMBER: _ClassVar[int] + reqId: int + groupId: int + def __init__(self, reqId: _Optional[int] = ..., groupId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/SymbolSamples_pb2.py b/ib_async/protobuf/SymbolSamples_pb2.py new file mode 100644 index 00000000..62e8ea28 --- /dev/null +++ b/ib_async/protobuf/SymbolSamples_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: SymbolSamples.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'SymbolSamples.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import ContractDescription_pb2 as ContractDescription__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13SymbolSamples.proto\x12\x08protobuf\x1a\x19\x43ontractDescription.proto\"j\n\rSymbolSamples\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12;\n\x14\x63ontractDescriptions\x18\x02 \x03(\x0b\x32\x1d.protobuf.ContractDescriptionB\x08\n\x06_reqIdB=\n\x16\x63om.ib.client.protobufB\x12SymbolSamplesProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SymbolSamples_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\022SymbolSamplesProto\252\002\016IBApi.protobuf' + _globals['_SYMBOLSAMPLES']._serialized_start=60 + _globals['_SYMBOLSAMPLES']._serialized_end=166 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/SymbolSamples_pb2.pyi b/ib_async/protobuf/SymbolSamples_pb2.pyi new file mode 100644 index 00000000..8d07c685 --- /dev/null +++ b/ib_async/protobuf/SymbolSamples_pb2.pyi @@ -0,0 +1,16 @@ +import ContractDescription_pb2 as _ContractDescription_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class SymbolSamples(_message.Message): + __slots__ = ("reqId", "contractDescriptions") + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACTDESCRIPTIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + contractDescriptions: _containers.RepeatedCompositeFieldContainer[_ContractDescription_pb2.ContractDescription] + def __init__(self, reqId: _Optional[int] = ..., contractDescriptions: _Optional[_Iterable[_Union[_ContractDescription_pb2.ContractDescription, _Mapping]]] = ...) -> None: ... diff --git a/ib_async/protobuf/TickAttribBidAsk_pb2.py b/ib_async/protobuf/TickAttribBidAsk_pb2.py new file mode 100644 index 00000000..32f906cf --- /dev/null +++ b/ib_async/protobuf/TickAttribBidAsk_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickAttribBidAsk.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickAttribBidAsk.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16TickAttribBidAsk.proto\x12\x08protobuf\"d\n\x10TickAttribBidAsk\x12\x17\n\nbidPastLow\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x18\n\x0b\x61skPastHigh\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\r\n\x0b_bidPastLowB\x0e\n\x0c_askPastHighB@\n\x16\x63om.ib.client.protobufB\x15TickAttribBidAskProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickAttribBidAsk_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025TickAttribBidAskProto\252\002\016IBApi.protobuf' + _globals['_TICKATTRIBBIDASK']._serialized_start=36 + _globals['_TICKATTRIBBIDASK']._serialized_end=136 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickAttribBidAsk_pb2.pyi b/ib_async/protobuf/TickAttribBidAsk_pb2.pyi new file mode 100644 index 00000000..44631505 --- /dev/null +++ b/ib_async/protobuf/TickAttribBidAsk_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickAttribBidAsk(_message.Message): + __slots__ = ("bidPastLow", "askPastHigh") + BIDPASTLOW_FIELD_NUMBER: _ClassVar[int] + ASKPASTHIGH_FIELD_NUMBER: _ClassVar[int] + bidPastLow: bool + askPastHigh: bool + def __init__(self, bidPastLow: bool = ..., askPastHigh: bool = ...) -> None: ... diff --git a/ib_async/protobuf/TickAttribLast_pb2.py b/ib_async/protobuf/TickAttribLast_pb2.py new file mode 100644 index 00000000..2c7c772c --- /dev/null +++ b/ib_async/protobuf/TickAttribLast_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickAttribLast.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickAttribLast.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14TickAttribLast.proto\x12\x08protobuf\"^\n\x0eTickAttribLast\x12\x16\n\tpastLimit\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x17\n\nunreported\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\x0c\n\n_pastLimitB\r\n\x0b_unreportedB>\n\x16\x63om.ib.client.protobufB\x13TickAttribLastProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickAttribLast_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023TickAttribLastProto\252\002\016IBApi.protobuf' + _globals['_TICKATTRIBLAST']._serialized_start=34 + _globals['_TICKATTRIBLAST']._serialized_end=128 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickAttribLast_pb2.pyi b/ib_async/protobuf/TickAttribLast_pb2.pyi new file mode 100644 index 00000000..0e01899e --- /dev/null +++ b/ib_async/protobuf/TickAttribLast_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickAttribLast(_message.Message): + __slots__ = ("pastLimit", "unreported") + PASTLIMIT_FIELD_NUMBER: _ClassVar[int] + UNREPORTED_FIELD_NUMBER: _ClassVar[int] + pastLimit: bool + unreported: bool + def __init__(self, pastLimit: bool = ..., unreported: bool = ...) -> None: ... diff --git a/ib_async/protobuf/TickByTickData_pb2.py b/ib_async/protobuf/TickByTickData_pb2.py new file mode 100644 index 00000000..0cb90c20 --- /dev/null +++ b/ib_async/protobuf/TickByTickData_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickByTickData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickByTickData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import HistoricalTickLast_pb2 as HistoricalTickLast__pb2 +from . import HistoricalTickBidAsk_pb2 as HistoricalTickBidAsk__pb2 +from . import HistoricalTick_pb2 as HistoricalTick__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14TickByTickData.proto\x12\x08protobuf\x1a\x18HistoricalTickLast.proto\x1a\x1aHistoricalTickBidAsk.proto\x1a\x14HistoricalTick.proto\"\x92\x02\n\x0eTickByTickData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x01\x88\x01\x01\x12\x15\n\x08tickType\x18\x02 \x01(\x05H\x02\x88\x01\x01\x12:\n\x12historicalTickLast\x18\x03 \x01(\x0b\x32\x1c.protobuf.HistoricalTickLastH\x00\x12>\n\x14historicalTickBidAsk\x18\x04 \x01(\x0b\x32\x1e.protobuf.HistoricalTickBidAskH\x00\x12:\n\x16historicalTickMidPoint\x18\x05 \x01(\x0b\x32\x18.protobuf.HistoricalTickH\x00\x42\x06\n\x04tickB\x08\n\x06_reqIdB\x0b\n\t_tickTypeB>\n\x16\x63om.ib.client.protobufB\x13TickByTickDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickByTickData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\023TickByTickDataProto\252\002\016IBApi.protobuf' + _globals['_TICKBYTICKDATA']._serialized_start=111 + _globals['_TICKBYTICKDATA']._serialized_end=385 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickByTickData_pb2.pyi b/ib_async/protobuf/TickByTickData_pb2.pyi new file mode 100644 index 00000000..ae7325d1 --- /dev/null +++ b/ib_async/protobuf/TickByTickData_pb2.pyi @@ -0,0 +1,23 @@ +import HistoricalTickLast_pb2 as _HistoricalTickLast_pb2 +import HistoricalTickBidAsk_pb2 as _HistoricalTickBidAsk_pb2 +import HistoricalTick_pb2 as _HistoricalTick_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickByTickData(_message.Message): + __slots__ = ("reqId", "tickType", "historicalTickLast", "historicalTickBidAsk", "historicalTickMidPoint") + REQID_FIELD_NUMBER: _ClassVar[int] + TICKTYPE_FIELD_NUMBER: _ClassVar[int] + HISTORICALTICKLAST_FIELD_NUMBER: _ClassVar[int] + HISTORICALTICKBIDASK_FIELD_NUMBER: _ClassVar[int] + HISTORICALTICKMIDPOINT_FIELD_NUMBER: _ClassVar[int] + reqId: int + tickType: int + historicalTickLast: _HistoricalTickLast_pb2.HistoricalTickLast + historicalTickBidAsk: _HistoricalTickBidAsk_pb2.HistoricalTickBidAsk + historicalTickMidPoint: _HistoricalTick_pb2.HistoricalTick + def __init__(self, reqId: _Optional[int] = ..., tickType: _Optional[int] = ..., historicalTickLast: _Optional[_Union[_HistoricalTickLast_pb2.HistoricalTickLast, _Mapping]] = ..., historicalTickBidAsk: _Optional[_Union[_HistoricalTickBidAsk_pb2.HistoricalTickBidAsk, _Mapping]] = ..., historicalTickMidPoint: _Optional[_Union[_HistoricalTick_pb2.HistoricalTick, _Mapping]] = ...) -> None: ... diff --git a/ib_async/protobuf/TickByTickRequest_pb2.py b/ib_async/protobuf/TickByTickRequest_pb2.py new file mode 100644 index 00000000..dbf8efba --- /dev/null +++ b/ib_async/protobuf/TickByTickRequest_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickByTickRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickByTickRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import Contract_pb2 as Contract__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17TickByTickRequest.proto\x12\x08protobuf\x1a\x0e\x43ontract.proto\"\xe3\x01\n\x11TickByTickRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12)\n\x08\x63ontract\x18\x02 \x01(\x0b\x32\x12.protobuf.ContractH\x01\x88\x01\x01\x12\x15\n\x08tickType\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rnumberOfTicks\x18\x04 \x01(\x05H\x03\x88\x01\x01\x12\x17\n\nignoreSize\x18\x05 \x01(\x08H\x04\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_contractB\x0b\n\t_tickTypeB\x10\n\x0e_numberOfTicksB\r\n\x0b_ignoreSizeBA\n\x16\x63om.ib.client.protobufB\x16TickByTickRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickByTickRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\026TickByTickRequestProto\252\002\016IBApi.protobuf' + _globals['_TICKBYTICKREQUEST']._serialized_start=54 + _globals['_TICKBYTICKREQUEST']._serialized_end=281 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickByTickRequest_pb2.pyi b/ib_async/protobuf/TickByTickRequest_pb2.pyi new file mode 100644 index 00000000..0bcc2010 --- /dev/null +++ b/ib_async/protobuf/TickByTickRequest_pb2.pyi @@ -0,0 +1,21 @@ +import Contract_pb2 as _Contract_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickByTickRequest(_message.Message): + __slots__ = ("reqId", "contract", "tickType", "numberOfTicks", "ignoreSize") + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACT_FIELD_NUMBER: _ClassVar[int] + TICKTYPE_FIELD_NUMBER: _ClassVar[int] + NUMBEROFTICKS_FIELD_NUMBER: _ClassVar[int] + IGNORESIZE_FIELD_NUMBER: _ClassVar[int] + reqId: int + contract: _Contract_pb2.Contract + tickType: str + numberOfTicks: int + ignoreSize: bool + def __init__(self, reqId: _Optional[int] = ..., contract: _Optional[_Union[_Contract_pb2.Contract, _Mapping]] = ..., tickType: _Optional[str] = ..., numberOfTicks: _Optional[int] = ..., ignoreSize: bool = ...) -> None: ... diff --git a/ib_async/protobuf/TickGeneric_pb2.py b/ib_async/protobuf/TickGeneric_pb2.py new file mode 100644 index 00000000..79c6cbbb --- /dev/null +++ b/ib_async/protobuf/TickGeneric_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickGeneric.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickGeneric.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11TickGeneric.proto\x12\x08protobuf\"m\n\x0bTickGeneric\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08tickType\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x12\n\x05value\x18\x03 \x01(\x01H\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_tickTypeB\x08\n\x06_valueB;\n\x16\x63om.ib.client.protobufB\x10TickGenericProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickGeneric_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020TickGenericProto\252\002\016IBApi.protobuf' + _globals['_TICKGENERIC']._serialized_start=31 + _globals['_TICKGENERIC']._serialized_end=140 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickGeneric_pb2.pyi b/ib_async/protobuf/TickGeneric_pb2.pyi new file mode 100644 index 00000000..ea8a5cde --- /dev/null +++ b/ib_async/protobuf/TickGeneric_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickGeneric(_message.Message): + __slots__ = ("reqId", "tickType", "value") + REQID_FIELD_NUMBER: _ClassVar[int] + TICKTYPE_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + reqId: int + tickType: int + value: float + def __init__(self, reqId: _Optional[int] = ..., tickType: _Optional[int] = ..., value: _Optional[float] = ...) -> None: ... diff --git a/ib_async/protobuf/TickNews_pb2.py b/ib_async/protobuf/TickNews_pb2.py new file mode 100644 index 00000000..0457cb5f --- /dev/null +++ b/ib_async/protobuf/TickNews_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickNews.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickNews.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eTickNews.proto\x12\x08protobuf\"\xea\x01\n\x08TickNews\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x16\n\ttimestamp\x18\x02 \x01(\x03H\x01\x88\x01\x01\x12\x19\n\x0cproviderCode\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tarticleId\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x15\n\x08headline\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x16\n\textraData\x18\x06 \x01(\tH\x05\x88\x01\x01\x42\x08\n\x06_reqIdB\x0c\n\n_timestampB\x0f\n\r_providerCodeB\x0c\n\n_articleIdB\x0b\n\t_headlineB\x0c\n\n_extraDataB8\n\x16\x63om.ib.client.protobufB\rTickNewsProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickNews_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\rTickNewsProto\252\002\016IBApi.protobuf' + _globals['_TICKNEWS']._serialized_start=29 + _globals['_TICKNEWS']._serialized_end=263 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickNews_pb2.pyi b/ib_async/protobuf/TickNews_pb2.pyi new file mode 100644 index 00000000..a943f018 --- /dev/null +++ b/ib_async/protobuf/TickNews_pb2.pyi @@ -0,0 +1,21 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickNews(_message.Message): + __slots__ = ("reqId", "timestamp", "providerCode", "articleId", "headline", "extraData") + REQID_FIELD_NUMBER: _ClassVar[int] + TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + PROVIDERCODE_FIELD_NUMBER: _ClassVar[int] + ARTICLEID_FIELD_NUMBER: _ClassVar[int] + HEADLINE_FIELD_NUMBER: _ClassVar[int] + EXTRADATA_FIELD_NUMBER: _ClassVar[int] + reqId: int + timestamp: int + providerCode: str + articleId: str + headline: str + extraData: str + def __init__(self, reqId: _Optional[int] = ..., timestamp: _Optional[int] = ..., providerCode: _Optional[str] = ..., articleId: _Optional[str] = ..., headline: _Optional[str] = ..., extraData: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/TickOptionComputation_pb2.py b/ib_async/protobuf/TickOptionComputation_pb2.py new file mode 100644 index 00000000..7fd4e751 --- /dev/null +++ b/ib_async/protobuf/TickOptionComputation_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickOptionComputation.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickOptionComputation.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bTickOptionComputation.proto\x12\x08protobuf\"\x8f\x03\n\x15TickOptionComputation\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08tickType\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x17\n\ntickAttrib\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12\x17\n\nimpliedVol\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x12\n\x05\x64\x65lta\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x15\n\x08optPrice\x18\x06 \x01(\x01H\x05\x88\x01\x01\x12\x17\n\npvDividend\x18\x07 \x01(\x01H\x06\x88\x01\x01\x12\x12\n\x05gamma\x18\x08 \x01(\x01H\x07\x88\x01\x01\x12\x11\n\x04vega\x18\t \x01(\x01H\x08\x88\x01\x01\x12\x12\n\x05theta\x18\n \x01(\x01H\t\x88\x01\x01\x12\x15\n\x08undPrice\x18\x0b \x01(\x01H\n\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_tickTypeB\r\n\x0b_tickAttribB\r\n\x0b_impliedVolB\x08\n\x06_deltaB\x0b\n\t_optPriceB\r\n\x0b_pvDividendB\x08\n\x06_gammaB\x07\n\x05_vegaB\x08\n\x06_thetaB\x0b\n\t_undPriceBE\n\x16\x63om.ib.client.protobufB\x1aTickOptionComputationProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickOptionComputation_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\032TickOptionComputationProto\252\002\016IBApi.protobuf' + _globals['_TICKOPTIONCOMPUTATION']._serialized_start=42 + _globals['_TICKOPTIONCOMPUTATION']._serialized_end=441 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickOptionComputation_pb2.pyi b/ib_async/protobuf/TickOptionComputation_pb2.pyi new file mode 100644 index 00000000..44f2978f --- /dev/null +++ b/ib_async/protobuf/TickOptionComputation_pb2.pyi @@ -0,0 +1,31 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickOptionComputation(_message.Message): + __slots__ = ("reqId", "tickType", "tickAttrib", "impliedVol", "delta", "optPrice", "pvDividend", "gamma", "vega", "theta", "undPrice") + REQID_FIELD_NUMBER: _ClassVar[int] + TICKTYPE_FIELD_NUMBER: _ClassVar[int] + TICKATTRIB_FIELD_NUMBER: _ClassVar[int] + IMPLIEDVOL_FIELD_NUMBER: _ClassVar[int] + DELTA_FIELD_NUMBER: _ClassVar[int] + OPTPRICE_FIELD_NUMBER: _ClassVar[int] + PVDIVIDEND_FIELD_NUMBER: _ClassVar[int] + GAMMA_FIELD_NUMBER: _ClassVar[int] + VEGA_FIELD_NUMBER: _ClassVar[int] + THETA_FIELD_NUMBER: _ClassVar[int] + UNDPRICE_FIELD_NUMBER: _ClassVar[int] + reqId: int + tickType: int + tickAttrib: int + impliedVol: float + delta: float + optPrice: float + pvDividend: float + gamma: float + vega: float + theta: float + undPrice: float + def __init__(self, reqId: _Optional[int] = ..., tickType: _Optional[int] = ..., tickAttrib: _Optional[int] = ..., impliedVol: _Optional[float] = ..., delta: _Optional[float] = ..., optPrice: _Optional[float] = ..., pvDividend: _Optional[float] = ..., gamma: _Optional[float] = ..., vega: _Optional[float] = ..., theta: _Optional[float] = ..., undPrice: _Optional[float] = ...) -> None: ... diff --git a/ib_async/protobuf/TickPrice_pb2.py b/ib_async/protobuf/TickPrice_pb2.py new file mode 100644 index 00000000..5705f7b1 --- /dev/null +++ b/ib_async/protobuf/TickPrice_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickPrice.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickPrice.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fTickPrice.proto\x12\x08protobuf\"\xab\x01\n\tTickPrice\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08tickType\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x12\n\x05price\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x11\n\x04size\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x15\n\x08\x61ttrMask\x18\x05 \x01(\x05H\x04\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_tickTypeB\x08\n\x06_priceB\x07\n\x05_sizeB\x0b\n\t_attrMaskB9\n\x16\x63om.ib.client.protobufB\x0eTickPriceProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickPrice_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\016TickPriceProto\252\002\016IBApi.protobuf' + _globals['_TICKPRICE']._serialized_start=30 + _globals['_TICKPRICE']._serialized_end=201 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickPrice_pb2.pyi b/ib_async/protobuf/TickPrice_pb2.pyi new file mode 100644 index 00000000..2313de7b --- /dev/null +++ b/ib_async/protobuf/TickPrice_pb2.pyi @@ -0,0 +1,19 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickPrice(_message.Message): + __slots__ = ("reqId", "tickType", "price", "size", "attrMask") + REQID_FIELD_NUMBER: _ClassVar[int] + TICKTYPE_FIELD_NUMBER: _ClassVar[int] + PRICE_FIELD_NUMBER: _ClassVar[int] + SIZE_FIELD_NUMBER: _ClassVar[int] + ATTRMASK_FIELD_NUMBER: _ClassVar[int] + reqId: int + tickType: int + price: float + size: str + attrMask: int + def __init__(self, reqId: _Optional[int] = ..., tickType: _Optional[int] = ..., price: _Optional[float] = ..., size: _Optional[str] = ..., attrMask: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/TickReqParams_pb2.py b/ib_async/protobuf/TickReqParams_pb2.py new file mode 100644 index 00000000..b5284c6b --- /dev/null +++ b/ib_async/protobuf/TickReqParams_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickReqParams.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickReqParams.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13TickReqParams.proto\x12\x08protobuf\"\xb3\x01\n\rTickReqParams\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x14\n\x07minTick\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x62\x62oExchange\x18\x03 \x01(\tH\x02\x88\x01\x01\x12 \n\x13snapshotPermissions\x18\x04 \x01(\x05H\x03\x88\x01\x01\x42\x08\n\x06_reqIdB\n\n\x08_minTickB\x0e\n\x0c_bboExchangeB\x16\n\x14_snapshotPermissionsB=\n\x16\x63om.ib.client.protobufB\x12TickReqParamsProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickReqParams_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\022TickReqParamsProto\252\002\016IBApi.protobuf' + _globals['_TICKREQPARAMS']._serialized_start=34 + _globals['_TICKREQPARAMS']._serialized_end=213 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickReqParams_pb2.pyi b/ib_async/protobuf/TickReqParams_pb2.pyi new file mode 100644 index 00000000..2ce28d9a --- /dev/null +++ b/ib_async/protobuf/TickReqParams_pb2.pyi @@ -0,0 +1,17 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickReqParams(_message.Message): + __slots__ = ("reqId", "minTick", "bboExchange", "snapshotPermissions") + REQID_FIELD_NUMBER: _ClassVar[int] + MINTICK_FIELD_NUMBER: _ClassVar[int] + BBOEXCHANGE_FIELD_NUMBER: _ClassVar[int] + SNAPSHOTPERMISSIONS_FIELD_NUMBER: _ClassVar[int] + reqId: int + minTick: str + bboExchange: str + snapshotPermissions: int + def __init__(self, reqId: _Optional[int] = ..., minTick: _Optional[str] = ..., bboExchange: _Optional[str] = ..., snapshotPermissions: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/TickSize_pb2.py b/ib_async/protobuf/TickSize_pb2.py new file mode 100644 index 00000000..9d345d3f --- /dev/null +++ b/ib_async/protobuf/TickSize_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickSize.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickSize.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eTickSize.proto\x12\x08protobuf\"h\n\x08TickSize\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08tickType\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x11\n\x04size\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_tickTypeB\x07\n\x05_sizeB8\n\x16\x63om.ib.client.protobufB\rTickSizeProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickSize_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\rTickSizeProto\252\002\016IBApi.protobuf' + _globals['_TICKSIZE']._serialized_start=28 + _globals['_TICKSIZE']._serialized_end=132 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickSize_pb2.pyi b/ib_async/protobuf/TickSize_pb2.pyi new file mode 100644 index 00000000..6b6b3c17 --- /dev/null +++ b/ib_async/protobuf/TickSize_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickSize(_message.Message): + __slots__ = ("reqId", "tickType", "size") + REQID_FIELD_NUMBER: _ClassVar[int] + TICKTYPE_FIELD_NUMBER: _ClassVar[int] + SIZE_FIELD_NUMBER: _ClassVar[int] + reqId: int + tickType: int + size: str + def __init__(self, reqId: _Optional[int] = ..., tickType: _Optional[int] = ..., size: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/TickSnapshotEnd_pb2.py b/ib_async/protobuf/TickSnapshotEnd_pb2.py new file mode 100644 index 00000000..5b53c06e --- /dev/null +++ b/ib_async/protobuf/TickSnapshotEnd_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickSnapshotEnd.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickSnapshotEnd.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15TickSnapshotEnd.proto\x12\x08protobuf\"/\n\x0fTickSnapshotEnd\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdB?\n\x16\x63om.ib.client.protobufB\x14TickSnapshotEndProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickSnapshotEnd_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024TickSnapshotEndProto\252\002\016IBApi.protobuf' + _globals['_TICKSNAPSHOTEND']._serialized_start=35 + _globals['_TICKSNAPSHOTEND']._serialized_end=82 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickSnapshotEnd_pb2.pyi b/ib_async/protobuf/TickSnapshotEnd_pb2.pyi new file mode 100644 index 00000000..6dfac3a4 --- /dev/null +++ b/ib_async/protobuf/TickSnapshotEnd_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickSnapshotEnd(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/TickString_pb2.py b/ib_async/protobuf/TickString_pb2.py new file mode 100644 index 00000000..9cbbc0e0 --- /dev/null +++ b/ib_async/protobuf/TickString_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: TickString.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'TickString.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10TickString.proto\x12\x08protobuf\"l\n\nTickString\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08tickType\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x12\n\x05value\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_tickTypeB\x08\n\x06_valueB:\n\x16\x63om.ib.client.protobufB\x0fTickStringProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TickString_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\017TickStringProto\252\002\016IBApi.protobuf' + _globals['_TICKSTRING']._serialized_start=30 + _globals['_TICKSTRING']._serialized_end=138 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/TickString_pb2.pyi b/ib_async/protobuf/TickString_pb2.pyi new file mode 100644 index 00000000..b30c79e8 --- /dev/null +++ b/ib_async/protobuf/TickString_pb2.pyi @@ -0,0 +1,15 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TickString(_message.Message): + __slots__ = ("reqId", "tickType", "value") + REQID_FIELD_NUMBER: _ClassVar[int] + TICKTYPE_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + reqId: int + tickType: int + value: str + def __init__(self, reqId: _Optional[int] = ..., tickType: _Optional[int] = ..., value: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/UnsubscribeFromGroupEventsRequest_pb2.py b/ib_async/protobuf/UnsubscribeFromGroupEventsRequest_pb2.py new file mode 100644 index 00000000..e8089d6c --- /dev/null +++ b/ib_async/protobuf/UnsubscribeFromGroupEventsRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: UnsubscribeFromGroupEventsRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'UnsubscribeFromGroupEventsRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'UnsubscribeFromGroupEventsRequest.proto\x12\x08protobuf\"A\n!UnsubscribeFromGroupEventsRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBQ\n\x16\x63om.ib.client.protobufB&UnsubscribeFromGroupEventsRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'UnsubscribeFromGroupEventsRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB&UnsubscribeFromGroupEventsRequestProto\252\002\016IBApi.protobuf' + _globals['_UNSUBSCRIBEFROMGROUPEVENTSREQUEST']._serialized_start=53 + _globals['_UNSUBSCRIBEFROMGROUPEVENTSREQUEST']._serialized_end=118 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/UnsubscribeFromGroupEventsRequest_pb2.pyi b/ib_async/protobuf/UnsubscribeFromGroupEventsRequest_pb2.pyi new file mode 100644 index 00000000..26646aec --- /dev/null +++ b/ib_async/protobuf/UnsubscribeFromGroupEventsRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class UnsubscribeFromGroupEventsRequest(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/UpdateDisplayGroupRequest_pb2.py b/ib_async/protobuf/UpdateDisplayGroupRequest_pb2.py new file mode 100644 index 00000000..942fdbd8 --- /dev/null +++ b/ib_async/protobuf/UpdateDisplayGroupRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: UpdateDisplayGroupRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'UpdateDisplayGroupRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fUpdateDisplayGroupRequest.proto\x12\x08protobuf\"e\n\x19UpdateDisplayGroupRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x19\n\x0c\x63ontractInfo\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x0f\n\r_contractInfoBI\n\x16\x63om.ib.client.protobufB\x1eUpdateDisplayGroupRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'UpdateDisplayGroupRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\036UpdateDisplayGroupRequestProto\252\002\016IBApi.protobuf' + _globals['_UPDATEDISPLAYGROUPREQUEST']._serialized_start=45 + _globals['_UPDATEDISPLAYGROUPREQUEST']._serialized_end=146 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/UpdateDisplayGroupRequest_pb2.pyi b/ib_async/protobuf/UpdateDisplayGroupRequest_pb2.pyi new file mode 100644 index 00000000..89fc1f97 --- /dev/null +++ b/ib_async/protobuf/UpdateDisplayGroupRequest_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class UpdateDisplayGroupRequest(_message.Message): + __slots__ = ("reqId", "contractInfo") + REQID_FIELD_NUMBER: _ClassVar[int] + CONTRACTINFO_FIELD_NUMBER: _ClassVar[int] + reqId: int + contractInfo: str + def __init__(self, reqId: _Optional[int] = ..., contractInfo: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/UserInfoRequest_pb2.py b/ib_async/protobuf/UserInfoRequest_pb2.py new file mode 100644 index 00000000..c6563bb0 --- /dev/null +++ b/ib_async/protobuf/UserInfoRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: UserInfoRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'UserInfoRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15UserInfoRequest.proto\x12\x08protobuf\"/\n\x0fUserInfoRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdB?\n\x16\x63om.ib.client.protobufB\x14UserInfoRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'UserInfoRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024UserInfoRequestProto\252\002\016IBApi.protobuf' + _globals['_USERINFOREQUEST']._serialized_start=35 + _globals['_USERINFOREQUEST']._serialized_end=82 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/UserInfoRequest_pb2.pyi b/ib_async/protobuf/UserInfoRequest_pb2.pyi new file mode 100644 index 00000000..f2fe5acb --- /dev/null +++ b/ib_async/protobuf/UserInfoRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class UserInfoRequest(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/UserInfo_pb2.py b/ib_async/protobuf/UserInfo_pb2.py new file mode 100644 index 00000000..1583bf4b --- /dev/null +++ b/ib_async/protobuf/UserInfo_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: UserInfo.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'UserInfo.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eUserInfo.proto\x12\x08protobuf\"Z\n\x08UserInfo\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x1c\n\x0fwhiteBrandingId\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x12\n\x10_whiteBrandingIdB8\n\x16\x63om.ib.client.protobufB\rUserInfoProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'UserInfo_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\rUserInfoProto\252\002\016IBApi.protobuf' + _globals['_USERINFO']._serialized_start=28 + _globals['_USERINFO']._serialized_end=118 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/UserInfo_pb2.pyi b/ib_async/protobuf/UserInfo_pb2.pyi new file mode 100644 index 00000000..94698a21 --- /dev/null +++ b/ib_async/protobuf/UserInfo_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class UserInfo(_message.Message): + __slots__ = ("reqId", "whiteBrandingId") + REQID_FIELD_NUMBER: _ClassVar[int] + WHITEBRANDINGID_FIELD_NUMBER: _ClassVar[int] + reqId: int + whiteBrandingId: str + def __init__(self, reqId: _Optional[int] = ..., whiteBrandingId: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/VerifyCompleted_pb2.py b/ib_async/protobuf/VerifyCompleted_pb2.py new file mode 100644 index 00000000..484c6d1e --- /dev/null +++ b/ib_async/protobuf/VerifyCompleted_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: VerifyCompleted.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'VerifyCompleted.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15VerifyCompleted.proto\x12\x08protobuf\"c\n\x0fVerifyCompleted\x12\x19\n\x0cisSuccessful\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x16\n\terrorText\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x0f\n\r_isSuccessfulB\x0c\n\n_errorTextB?\n\x16\x63om.ib.client.protobufB\x14VerifyCompletedProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'VerifyCompleted_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\024VerifyCompletedProto\252\002\016IBApi.protobuf' + _globals['_VERIFYCOMPLETED']._serialized_start=35 + _globals['_VERIFYCOMPLETED']._serialized_end=134 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/VerifyCompleted_pb2.pyi b/ib_async/protobuf/VerifyCompleted_pb2.pyi new file mode 100644 index 00000000..5ce2ed2d --- /dev/null +++ b/ib_async/protobuf/VerifyCompleted_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class VerifyCompleted(_message.Message): + __slots__ = ("isSuccessful", "errorText") + ISSUCCESSFUL_FIELD_NUMBER: _ClassVar[int] + ERRORTEXT_FIELD_NUMBER: _ClassVar[int] + isSuccessful: bool + errorText: str + def __init__(self, isSuccessful: bool = ..., errorText: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/VerifyMessageApi_pb2.py b/ib_async/protobuf/VerifyMessageApi_pb2.py new file mode 100644 index 00000000..9e8fc056 --- /dev/null +++ b/ib_async/protobuf/VerifyMessageApi_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: VerifyMessageApi.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'VerifyMessageApi.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16VerifyMessageApi.proto\x12\x08protobuf\"4\n\x10VerifyMessageApi\x12\x14\n\x07\x61piData\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_apiDataB@\n\x16\x63om.ib.client.protobufB\x15VerifyMessageApiProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'VerifyMessageApi_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\025VerifyMessageApiProto\252\002\016IBApi.protobuf' + _globals['_VERIFYMESSAGEAPI']._serialized_start=36 + _globals['_VERIFYMESSAGEAPI']._serialized_end=88 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/VerifyMessageApi_pb2.pyi b/ib_async/protobuf/VerifyMessageApi_pb2.pyi new file mode 100644 index 00000000..1834856c --- /dev/null +++ b/ib_async/protobuf/VerifyMessageApi_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class VerifyMessageApi(_message.Message): + __slots__ = ("apiData",) + APIDATA_FIELD_NUMBER: _ClassVar[int] + apiData: str + def __init__(self, apiData: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/VerifyMessageRequest_pb2.py b/ib_async/protobuf/VerifyMessageRequest_pb2.py new file mode 100644 index 00000000..390d7df1 --- /dev/null +++ b/ib_async/protobuf/VerifyMessageRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: VerifyMessageRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'VerifyMessageRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aVerifyMessageRequest.proto\x12\x08protobuf\"8\n\x14VerifyMessageRequest\x12\x14\n\x07\x61piData\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_apiDataBD\n\x16\x63om.ib.client.protobufB\x19VerifyMessageRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'VerifyMessageRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\031VerifyMessageRequestProto\252\002\016IBApi.protobuf' + _globals['_VERIFYMESSAGEREQUEST']._serialized_start=40 + _globals['_VERIFYMESSAGEREQUEST']._serialized_end=96 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/VerifyMessageRequest_pb2.pyi b/ib_async/protobuf/VerifyMessageRequest_pb2.pyi new file mode 100644 index 00000000..800143ec --- /dev/null +++ b/ib_async/protobuf/VerifyMessageRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class VerifyMessageRequest(_message.Message): + __slots__ = ("apiData",) + APIDATA_FIELD_NUMBER: _ClassVar[int] + apiData: str + def __init__(self, apiData: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/VerifyRequest_pb2.py b/ib_async/protobuf/VerifyRequest_pb2.py new file mode 100644 index 00000000..a06e3c6d --- /dev/null +++ b/ib_async/protobuf/VerifyRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: VerifyRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'VerifyRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13VerifyRequest.proto\x12\x08protobuf\"Y\n\rVerifyRequest\x12\x14\n\x07\x61piName\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\napiVersion\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\n\n\x08_apiNameB\r\n\x0b_apiVersionB=\n\x16\x63om.ib.client.protobufB\x12VerifyRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'VerifyRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\022VerifyRequestProto\252\002\016IBApi.protobuf' + _globals['_VERIFYREQUEST']._serialized_start=33 + _globals['_VERIFYREQUEST']._serialized_end=122 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/VerifyRequest_pb2.pyi b/ib_async/protobuf/VerifyRequest_pb2.pyi new file mode 100644 index 00000000..f083646e --- /dev/null +++ b/ib_async/protobuf/VerifyRequest_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class VerifyRequest(_message.Message): + __slots__ = ("apiName", "apiVersion") + APINAME_FIELD_NUMBER: _ClassVar[int] + APIVERSION_FIELD_NUMBER: _ClassVar[int] + apiName: str + apiVersion: str + def __init__(self, apiName: _Optional[str] = ..., apiVersion: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/WshEventDataRequest_pb2.py b/ib_async/protobuf/WshEventDataRequest_pb2.py new file mode 100644 index 00000000..82438ecc --- /dev/null +++ b/ib_async/protobuf/WshEventDataRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: WshEventDataRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'WshEventDataRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19WshEventDataRequest.proto\x12\x08protobuf\"\xef\x02\n\x13WshEventDataRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x12\n\x05\x63onId\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x13\n\x06\x66ilter\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rfillWatchlist\x18\x04 \x01(\x08H\x03\x88\x01\x01\x12\x1a\n\rfillPortfolio\x18\x05 \x01(\x08H\x04\x88\x01\x01\x12\x1c\n\x0f\x66illCompetitors\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x16\n\tstartDate\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x14\n\x07\x65ndDate\x18\x08 \x01(\tH\x07\x88\x01\x01\x12\x17\n\ntotalLimit\x18\t \x01(\x05H\x08\x88\x01\x01\x42\x08\n\x06_reqIdB\x08\n\x06_conIdB\t\n\x07_filterB\x10\n\x0e_fillWatchlistB\x10\n\x0e_fillPortfolioB\x12\n\x10_fillCompetitorsB\x0c\n\n_startDateB\n\n\x08_endDateB\r\n\x0b_totalLimitBC\n\x16\x63om.ib.client.protobufB\x18WshEventDataRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'WshEventDataRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\030WshEventDataRequestProto\252\002\016IBApi.protobuf' + _globals['_WSHEVENTDATAREQUEST']._serialized_start=40 + _globals['_WSHEVENTDATAREQUEST']._serialized_end=407 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/WshEventDataRequest_pb2.pyi b/ib_async/protobuf/WshEventDataRequest_pb2.pyi new file mode 100644 index 00000000..06d8c0c8 --- /dev/null +++ b/ib_async/protobuf/WshEventDataRequest_pb2.pyi @@ -0,0 +1,27 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class WshEventDataRequest(_message.Message): + __slots__ = ("reqId", "conId", "filter", "fillWatchlist", "fillPortfolio", "fillCompetitors", "startDate", "endDate", "totalLimit") + REQID_FIELD_NUMBER: _ClassVar[int] + CONID_FIELD_NUMBER: _ClassVar[int] + FILTER_FIELD_NUMBER: _ClassVar[int] + FILLWATCHLIST_FIELD_NUMBER: _ClassVar[int] + FILLPORTFOLIO_FIELD_NUMBER: _ClassVar[int] + FILLCOMPETITORS_FIELD_NUMBER: _ClassVar[int] + STARTDATE_FIELD_NUMBER: _ClassVar[int] + ENDDATE_FIELD_NUMBER: _ClassVar[int] + TOTALLIMIT_FIELD_NUMBER: _ClassVar[int] + reqId: int + conId: int + filter: str + fillWatchlist: bool + fillPortfolio: bool + fillCompetitors: bool + startDate: str + endDate: str + totalLimit: int + def __init__(self, reqId: _Optional[int] = ..., conId: _Optional[int] = ..., filter: _Optional[str] = ..., fillWatchlist: bool = ..., fillPortfolio: bool = ..., fillCompetitors: bool = ..., startDate: _Optional[str] = ..., endDate: _Optional[str] = ..., totalLimit: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/WshEventData_pb2.py b/ib_async/protobuf/WshEventData_pb2.py new file mode 100644 index 00000000..978bd772 --- /dev/null +++ b/ib_async/protobuf/WshEventData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: WshEventData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'WshEventData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12WshEventData.proto\x12\x08protobuf\"P\n\x0cWshEventData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08\x64\x61taJson\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_dataJsonB<\n\x16\x63om.ib.client.protobufB\x11WshEventDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'WshEventData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\021WshEventDataProto\252\002\016IBApi.protobuf' + _globals['_WSHEVENTDATA']._serialized_start=32 + _globals['_WSHEVENTDATA']._serialized_end=112 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/WshEventData_pb2.pyi b/ib_async/protobuf/WshEventData_pb2.pyi new file mode 100644 index 00000000..c9a4c270 --- /dev/null +++ b/ib_async/protobuf/WshEventData_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class WshEventData(_message.Message): + __slots__ = ("reqId", "dataJson") + REQID_FIELD_NUMBER: _ClassVar[int] + DATAJSON_FIELD_NUMBER: _ClassVar[int] + reqId: int + dataJson: str + def __init__(self, reqId: _Optional[int] = ..., dataJson: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/WshMetaDataRequest_pb2.py b/ib_async/protobuf/WshMetaDataRequest_pb2.py new file mode 100644 index 00000000..63e01e38 --- /dev/null +++ b/ib_async/protobuf/WshMetaDataRequest_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: WshMetaDataRequest.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'WshMetaDataRequest.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18WshMetaDataRequest.proto\x12\x08protobuf\"2\n\x12WshMetaDataRequest\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x42\x08\n\x06_reqIdBB\n\x16\x63om.ib.client.protobufB\x17WshMetaDataRequestProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'WshMetaDataRequest_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\027WshMetaDataRequestProto\252\002\016IBApi.protobuf' + _globals['_WSHMETADATAREQUEST']._serialized_start=38 + _globals['_WSHMETADATAREQUEST']._serialized_end=88 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/WshMetaDataRequest_pb2.pyi b/ib_async/protobuf/WshMetaDataRequest_pb2.pyi new file mode 100644 index 00000000..8aaf6731 --- /dev/null +++ b/ib_async/protobuf/WshMetaDataRequest_pb2.pyi @@ -0,0 +1,11 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class WshMetaDataRequest(_message.Message): + __slots__ = ("reqId",) + REQID_FIELD_NUMBER: _ClassVar[int] + reqId: int + def __init__(self, reqId: _Optional[int] = ...) -> None: ... diff --git a/ib_async/protobuf/WshMetaData_pb2.py b/ib_async/protobuf/WshMetaData_pb2.py new file mode 100644 index 00000000..97e64e7a --- /dev/null +++ b/ib_async/protobuf/WshMetaData_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: WshMetaData.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'WshMetaData.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11WshMetaData.proto\x12\x08protobuf\"O\n\x0bWshMetaData\x12\x12\n\x05reqId\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08\x64\x61taJson\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_reqIdB\x0b\n\t_dataJsonB;\n\x16\x63om.ib.client.protobufB\x10WshMetaDataProto\xaa\x02\x0eIBApi.protobufb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'WshMetaData_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\026com.ib.client.protobufB\020WshMetaDataProto\252\002\016IBApi.protobuf' + _globals['_WSHMETADATA']._serialized_start=31 + _globals['_WSHMETADATA']._serialized_end=110 +# @@protoc_insertion_point(module_scope) diff --git a/ib_async/protobuf/WshMetaData_pb2.pyi b/ib_async/protobuf/WshMetaData_pb2.pyi new file mode 100644 index 00000000..8f2b1198 --- /dev/null +++ b/ib_async/protobuf/WshMetaData_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class WshMetaData(_message.Message): + __slots__ = ("reqId", "dataJson") + REQID_FIELD_NUMBER: _ClassVar[int] + DATAJSON_FIELD_NUMBER: _ClassVar[int] + reqId: int + dataJson: str + def __init__(self, reqId: _Optional[int] = ..., dataJson: _Optional[str] = ...) -> None: ... diff --git a/ib_async/protobuf/__init__.py b/ib_async/protobuf/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ib_async/protobuf_converters/account_converters.py b/ib_async/protobuf_converters/account_converters.py new file mode 100644 index 00000000..530aab1a --- /dev/null +++ b/ib_async/protobuf_converters/account_converters.py @@ -0,0 +1,163 @@ +# ib_async/protobuf_converters/account_converters.py + +from ..objects import AccountValue, PortfolioItem, Position +from ..protobuf.AccountDataRequest_pb2 import ( + AccountDataRequest as AccountDataRequestProto, +) +from ..protobuf.AccountUpdatesMultiRequest_pb2 import ( + AccountUpdatesMultiRequest as AccountUpdatesMultiRequestProto, +) +from ..protobuf.AccountSummary_pb2 import AccountSummary as AccountSummaryProto +from ..protobuf.AccountValue_pb2 import AccountValue as AccountValueProto +from ..protobuf.AccountUpdateMulti_pb2 import ( + AccountUpdateMulti as AccountUpdateMultiProto, +) +from ..protobuf.CancelAccountUpdatesMulti_pb2 import ( + CancelAccountUpdatesMulti as CancelAccountUpdatesMultiProto, +) +from ..protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto +from ..protobuf.Position_pb2 import Position as PositionProto +from .contract_converters import createContract + + +def createPosition(positionProto: PositionProto) -> Position: + """ + Converts a Position protobuf message to an ib_async Position object. + """ + contract = createContract(positionProto.contract) + position = Position( + account=positionProto.account, + contract=contract, + position=float(positionProto.position) if positionProto.position else 0.0, + avgCost=positionProto.avgCost, + ) + return position + + +def createAccountDataRequestProto( + subscribe: bool, acctCode: str +) -> AccountDataRequestProto: + """ + Creates an AccountDataRequest protobuf message. + """ + accountDataRequestProto = AccountDataRequestProto() + accountDataRequestProto.subscribe = subscribe + accountDataRequestProto.acctCode = acctCode + return accountDataRequestProto + +def createAccountMultiRequestProto(reqId: int, account: str, modelCode: str, ledgerAndNLV: bool) -> AccountUpdatesMultiRequestProto: + """ + Creates an AccountUpdatesMultiRequest protobuf message. + """ + accountUpdatesMultiRequestProto = AccountUpdatesMultiRequestProto() + accountUpdatesMultiRequestProto.reqId = reqId + if account: + accountUpdatesMultiRequestProto.account = account + if modelCode: + accountUpdatesMultiRequestProto.modelCode = modelCode + if ledgerAndNLV: + accountUpdatesMultiRequestProto.ledgerAndNLV = ledgerAndNLV + return accountUpdatesMultiRequestProto + +def createCancelAccMultiRequestProto(reqId: int) -> CancelAccountUpdatesMultiProto: + cancelAccountUpdatesMultiProto = CancelAccountUpdatesMultiProto() + cancelAccountUpdatesMultiProto.reqId = reqId + return cancelAccountUpdatesMultiProto + + +def createAccountValueFromUpdateMulti(accountValueProto: AccountUpdateMultiProto) -> AccountValue: + """ + Converts an AccountUpdateMulti protobuf message to an ib_async AccountValue object. + """ + if accountValueProto.HasField("account"): + _account = accountValueProto.account + else: + _account = "" + if accountValueProto.HasField("key"): + _tag = accountValueProto.key + else: + _tag = "" + if accountValueProto.HasField("value"): + _value = accountValueProto.value + else: + _value = "" + if accountValueProto.HasField("currency"): + _currency = accountValueProto.currency + else: + _currency = "" + return AccountValue( + account=_account, + tag=_tag, + value=_value, + currency=_currency, + modelCode="", + ) + + +def createAccountValue(accountValueProto: AccountValueProto) -> AccountValue: + if accountValueProto.HasField("accountName"): + _account = accountValueProto.accountName + else: + _account = "" + if accountValueProto.HasField("key"): + _tag = accountValueProto.key + else: + _tag = "" + if accountValueProto.HasField("value"): + _value = accountValueProto.value + else: + _value = "" + if accountValueProto.HasField("currency"): + _currency = accountValueProto.currency + else: + _currency = "" + + return AccountValue( + account=_account, + tag=_tag, + value=_value, + currency=_currency, + modelCode="", + ) + + +def createAccountSummary(accountSummaryProto: AccountSummaryProto) -> AccountValue: + if accountSummaryProto.HasField("account"): + _account = accountSummaryProto.account + else: + _account = "" + if accountSummaryProto.HasField("tag"): + _tag = accountSummaryProto.tag + else: + _tag = "" + if accountSummaryProto.HasField("value"): + _value = accountSummaryProto.value + else: + _value = "" + if accountSummaryProto.HasField("currency"): + _currency = accountSummaryProto.currency + else: + _currency = "" + + return AccountValue( + account=_account, + tag=_tag, + value=_value, + currency=_currency, + modelCode="", + ) + + +def createPortfolioItem(portfolioValueProto: PortfolioValueProto) -> PortfolioItem: + return PortfolioItem( + contract=createContract(portfolioValueProto.contract), + position=float(portfolioValueProto.position) + if portfolioValueProto.position + else 0.0, + marketPrice=portfolioValueProto.marketPrice, + marketValue=portfolioValueProto.marketValue, + averageCost=portfolioValueProto.averageCost, + unrealizedPNL=portfolioValueProto.unrealizedPNL, + realizedPNL=portfolioValueProto.realizedPNL, + account=portfolioValueProto.accountName, + ) diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py new file mode 100644 index 00000000..17cd41c8 --- /dev/null +++ b/ib_async/protobuf_converters/contract_converters.py @@ -0,0 +1,529 @@ +"""Contract converters""" + +from decimal import Decimal + +from ..contract import ( + ComboLeg, + Contract, + ContractDescription, + ContractDetails, + DeltaNeutralContract, + FundAssetType, + FundDistributionPolicyIndicator, +) +from ..objects import IneligibilityReason, OptionChain +from ..order import Order, OrderComboLeg +from ..protobuf.AccountDataRequest_pb2 import ( + AccountDataRequest as AccountDataRequestProto, +) +from ..protobuf.ComboLeg_pb2 import ComboLeg as ComboLegProto +from ..protobuf.Contract_pb2 import Contract as ContractProto +from ..protobuf.ContractData_pb2 import ContractData as ContractDataProto +from ..protobuf.ContractDescription_pb2 import ( + ContractDescription as ContractDescriptionProto, +) +from ..protobuf.ContractDetails_pb2 import ContractDetails as ContractDetailsProto +from ..protobuf.DeltaNeutralContract_pb2 import ( + DeltaNeutralContract as DeltaNeutralContractProto, +) +from ..protobuf.IneligibilityReason_pb2 import ( + IneligibilityReason as IneligibilityReasonProto, +) +from ..protobuf.MarketRuleRequest_pb2 import MarketRuleRequest as MarketRuleRequestProto +from ..protobuf.MatchingSymbolsRequest_pb2 import ( + MatchingSymbolsRequest as MatchingSymbolsRequestProto, +) +from ..protobuf.SecDefOptParameter_pb2 import ( + SecDefOptParameter as SecDefOptParameterProto, +) +from ..protobuf.SecDefOptParamsRequest_pb2 import ( + SecDefOptParamsRequest as SecDefOptParamsRequestProto, +) +from ..util import ( + UNSET_DOUBLE, + floatMaxString, + getEnumTypeFromString, +) + + +def createSecDefOptParamsRequestProto( + reqId: int, + underlyingSymbol: str, + futFopExchange: str, + underlyingSecType: str, + underlyingConId: int, +) -> SecDefOptParamsRequestProto: + """Create a SecDefOptParamsRequest protobuf message. + + Args: + reqId (int): The request ID. + underlyingSymbol (str): The underlying symbol. + futFopExchange (str): The future/option exchange. + underlyingSecType (str): The underlying security type. + underlyingConId (int): The underlying contract ID. + + Returns: + SecDefOptParamsRequestProto: The created SecDefOptParamsRequest protobuf + message. + """ + secDefOptParamsRequestProto = SecDefOptParamsRequestProto() + if reqId is not None: + secDefOptParamsRequestProto.reqId = reqId + if underlyingSymbol is not None: + secDefOptParamsRequestProto.underlyingSymbol = underlyingSymbol + if futFopExchange is not None: + secDefOptParamsRequestProto.futFopExchange = futFopExchange + if underlyingSecType is not None: + secDefOptParamsRequestProto.underlyingSecType = underlyingSecType + if underlyingConId is not None: + secDefOptParamsRequestProto.underlyingConId = underlyingConId + return secDefOptParamsRequestProto + + +def createOptionChain(secDefOptParameterProto: SecDefOptParameterProto) -> OptionChain: + """Create an OptionChain object from a protobuf message. + + Args: + secDefOptParameterProto (SecDefOptParameterProto): The protobuf message. + + Returns: + OptionChain: The created OptionChain object. + """ + if secDefOptParameterProto.exchange is not None: + exchange = secDefOptParameterProto.exchange + if secDefOptParameterProto.underlyingConId is not None: + underlyingConId = secDefOptParameterProto.underlyingConId + if secDefOptParameterProto.tradingClass is not None: + tradingClass = secDefOptParameterProto.tradingClass + if secDefOptParameterProto.multiplier is not None: + multiplier = secDefOptParameterProto.multiplier + if secDefOptParameterProto.expirations is not None: + expirations = list(secDefOptParameterProto.expirations) + if secDefOptParameterProto.strikes is not None: + strikes = list(secDefOptParameterProto.strikes) + optionChain = OptionChain( + exchange=exchange, + underlyingConId=underlyingConId, + tradingClass=tradingClass, + multiplier=multiplier, + expirations=expirations, + strikes=strikes, + ) + return optionChain + + +def createContract(contractProto: ContractProto) -> Contract: + contract = Contract() + if contractProto.HasField("conId"): + contract.conId = contractProto.conId + if contractProto.HasField("symbol"): + contract.symbol = contractProto.symbol + if contractProto.HasField("secType"): + contract.secType = contractProto.secType + if contractProto.HasField("lastTradeDateOrContractMonth"): + contract.lastTradeDateOrContractMonth = ( + contractProto.lastTradeDateOrContractMonth + ) + if contractProto.HasField("strike"): + contract.strike = contractProto.strike + if contractProto.HasField("right"): + contract.right = contractProto.right + if contractProto.HasField("multiplier"): + contract.multiplier = floatMaxString(contractProto.multiplier) + if contractProto.HasField("exchange"): + contract.exchange = contractProto.exchange + if contractProto.HasField("currency"): + contract.currency = contractProto.currency + if contractProto.HasField("localSymbol"): + contract.localSymbol = contractProto.localSymbol + if contractProto.HasField("tradingClass"): + contract.tradingClass = contractProto.tradingClass + if contractProto.HasField("comboLegsDescrip"): + contract.comboLegsDescrip = contractProto.comboLegsDescrip + + comboLegs = createComboLegs(contractProto) + if comboLegs is not None and comboLegs: + contract.comboLegs = comboLegs + + deltaNeutralContract = createDeltaNeutralContract(contractProto) + if deltaNeutralContract is not None: + contract.deltaNeutralContract = deltaNeutralContract + + if contractProto.HasField("lastTradeDate"): + contract.lastTradeDate = contractProto.lastTradeDate + if contractProto.HasField("primaryExch"): + contract.primaryExchange = contractProto.primaryExch + if contractProto.HasField("issuerId"): + contract.issuerId = contractProto.issuerId + if contractProto.HasField("description"): + contract.description = contractProto.description + + return contract + + +def createComboLegs(contractProto: ContractProto) -> list[ComboLeg]: + comboLegs = [] + comboLegProtoList = contractProto.comboLegs + if comboLegProtoList: + for comboLegProto in comboLegProtoList: + comboLeg = ComboLeg() + if comboLegProto.HasField("conId"): + comboLeg.conId = comboLegProto.conId + if comboLegProto.HasField("ratio"): + comboLeg.ratio = comboLegProto.ratio + if comboLegProto.HasField("action"): + comboLeg.action = comboLegProto.action + if comboLegProto.HasField("exchange"): + comboLeg.exchange = comboLegProto.exchange + if comboLegProto.HasField("openClose"): + comboLeg.openClose = comboLegProto.openClose + if comboLegProto.HasField("shortSalesSlot"): + comboLeg.shortSalesSlot = comboLegProto.shortSalesSlot + if comboLegProto.HasField("designatedLocation"): + comboLeg.designatedLocation = comboLegProto.designatedLocation + if comboLegProto.HasField("exemptCode"): + comboLeg.exemptCode = comboLegProto.exemptCode + comboLegs.append(comboLeg) + + return comboLegs + + +def createDeltaNeutralContract(contractProto: ContractProto) -> DeltaNeutralContract: + deltaNeutralContract = DeltaNeutralContract() + if contractProto.HasField("deltaNeutralContract"): + deltaNeutralContractProto = DeltaNeutralContractProto() + deltaNeutralContractProto.CopyFrom(contractProto.deltaNeutralContract) + if deltaNeutralContractProto is not None: + deltaNeutralContract = DeltaNeutralContract() + if deltaNeutralContractProto.HasField("conId"): + deltaNeutralContract.conId = deltaNeutralContractProto.conId + if deltaNeutralContractProto.HasField("delta"): + deltaNeutralContract.delta = deltaNeutralContractProto.delta + if deltaNeutralContractProto.HasField("price"): + deltaNeutralContract.price = deltaNeutralContractProto.price + + return deltaNeutralContract + + +def decodeIneligibilityReasonList( + contractDetailsProto: ContractDetailsProto, +) -> list[IneligibilityReason]: + ineligibilityReasonList = [] + ineligibilityReasonProtoList = contractDetailsProto.ineligibilityReasonList + if ineligibilityReasonProtoList: + for ineligibilityReasonProto in ineligibilityReasonProtoList: + ineligibilityReason = IneligibilityReason() + if ineligibilityReasonProto.HasField("id"): + ineligibilityReason.id_ = ineligibilityReasonProto.id + if ineligibilityReasonProto.HasField("description"): + ineligibilityReason.description = ineligibilityReasonProto.description + ineligibilityReasonList.append(ineligibilityReason) + return ineligibilityReasonList + + +def setLastTradeDate( + lastTradeDateOrContractMonth: str, contract: ContractDetails, isBond: bool +): + if lastTradeDateOrContractMonth is not None: + if "-" in lastTradeDateOrContractMonth: + split = lastTradeDateOrContractMonth.split("-") + else: + split = lastTradeDateOrContractMonth.split() + + if len(split) > 0: + if isBond: + contract.maturity = split[0] + else: + contract.contract.lastTradeDateOrContractMonth = split[0] + + if len(split) > 1: + contract.lastTradeTime = split[1] + + if isBond and len(split) > 2: + contract.timeZoneId = split[2] + + +def createContractDetails( + msg: ContractDataProto, isBond: bool = False +) -> ContractDetails: + """Create a ContractDetails object from a protobuf message. + Args: + msg (ContractDataProto): The protobuf message. + Returns: + ContractDetails: The created ContractDetails object. + """ + contractDetails = ContractDetails() + contractDetails.contract = createContract(msg.contract) + details = msg.contractDetails + + contractDetails.marketName = details.marketName + contractDetails.minTick = float(details.minTick) if details.minTick else 0.0 + contractDetails.orderTypes = details.orderTypes + contractDetails.validExchanges = details.validExchanges + contractDetails.priceMagnifier = details.priceMagnifier + contractDetails.underConId = details.underConId + contractDetails.longName = details.longName + contractDetails.contractMonth = details.contractMonth + contractDetails.industry = details.industry + contractDetails.category = details.category + contractDetails.subcategory = details.subcategory + contractDetails.timeZoneId = details.timeZoneId + contractDetails.tradingHours = details.tradingHours + contractDetails.liquidHours = details.liquidHours + contractDetails.evRule = details.evRule + contractDetails.evMultiplier = details.evMultiplier + # contractDetails.secIdList = details.secIdList + # This is a map in proto, list of TagValue in ib_async + contractDetails.aggGroup = details.aggGroup + contractDetails.underSymbol = details.underSymbol + contractDetails.underSecType = details.underSecType + contractDetails.marketRuleIds = details.marketRuleIds + contractDetails.realExpirationDate = details.realExpirationDate + contractDetails.stockType = details.stockType + contractDetails.minSize = float(details.minSize) if details.minSize else 0.0 + contractDetails.sizeIncrement = ( + float(details.sizeIncrement) if details.sizeIncrement else 0.0 + ) + contractDetails.suggestedSizeIncrement = ( + float(details.suggestedSizeIncrement) if details.suggestedSizeIncrement else 0.0 + ) + contractDetails.cusip = details.cusip + contractDetails.ratings = details.ratings + contractDetails.descAppend = details.descAppend + contractDetails.bondType = details.bondType + contractDetails.couponType = details.couponType + contractDetails.callable = details.callable + contractDetails.putable = details.puttable + contractDetails.coupon = details.coupon + contractDetails.convertible = details.convertible + contractDetails.issueDate = details.issueDate + contractDetails.nextOptionDate = details.nextOptionDate + contractDetails.nextOptionType = details.nextOptionType + contractDetails.nextOptionPartial = details.nextOptionPartial + + if details.HasField("fundName"): + contractDetails.fundName = details.fundName + if details.HasField("fundFamily"): + contractDetails.fundFamily = details.fundFamily + if details.HasField("fundType"): + contractDetails.fundType = details.fundType + if details.HasField("fundFrontLoad"): + contractDetails.fundFrontLoad = details.fundFrontLoad + if details.HasField("fundBackLoad"): + contractDetails.fundBackLoad = details.fundBackLoad + if details.HasField("fundBackLoadTimeInterval"): + contractDetails.fundBackLoadTimeInterval = details.fundBackLoadTimeInterval + if details.HasField("fundManagementFee"): + contractDetails.fundManagementFee = details.fundManagementFee + if details.HasField("fundClosed"): + contractDetails.fundClosed = details.fundClosed + if details.HasField("fundClosedForNewInvestors"): + contractDetails.fundClosedForNewInvestors = details.fundClosedForNewInvestors + if details.HasField("fundClosedForNewMoney"): + contractDetails.fundClosedForNewMoney = details.fundClosedForNewMoney + if details.HasField("fundNotifyAmount"): + contractDetails.fundNotifyAmount = details.fundNotifyAmount + if details.HasField("fundMinimumInitialPurchase"): + contractDetails.fundMinimumInitialPurchase = details.fundMinimumInitialPurchase + if details.HasField("fundMinimumSubsequentPurchase"): + contractDetails.fundSubsequentMinimumPurchase = ( + details.fundMinimumSubsequentPurchase + ) + if details.HasField("fundBlueSkyStates"): + contractDetails.fundBlueSkyStates = details.fundBlueSkyStates + if details.HasField("fundBlueSkyTerritories"): + contractDetails.fundBlueSkyTerritories = details.fundBlueSkyTerritories + + if details.HasField("fundDistributionPolicyIndicator"): + contractDetails.fundDistributionPolicyIndicator = getEnumTypeFromString( + FundDistributionPolicyIndicator, details.fundDistributionPolicyIndicator + ) + if details.HasField("fundAssetType"): + contractDetails.fundAssetType = getEnumTypeFromString( + FundAssetType, details.fundAssetType + ) + + ineligibilityReasonList = decodeIneligibilityReasonList(details) + if ineligibilityReasonList is not None and ineligibilityReasonList: + contractDetails.ineligibilityReasonList = ineligibilityReasonList + + if details.HasField("eventContract1"): + contractDetails.eventContract1 = details.eventContract1 + if details.HasField("eventContractDescription1"): + contractDetails.eventContractDescription1 = details.eventContractDescription1 + if details.HasField("eventContractDescription2"): + contractDetails.eventContractDescription2 = details.eventContractDescription2 + + setLastTradeDate( + contractDetails.contract.lastTradeDateOrContractMonth, contractDetails, isBond + ) + + return contractDetails + + +def createContractDescription( + contractDescriptionProto: ContractDescriptionProto, +) -> ContractDescription: + """Create a ContractDescription object from a protobuf message. + + Args: + contractDescriptionProto (ContractDescriptionProto): The protobuf message. + + Returns: + ContractDescription: The created ContractDescription object. + """ + contractDescription = ContractDescription() + contractDescription.contract = createContract(contractDescriptionProto.contract) + contractDescription.derivativeSecTypes.extend( + contractDescriptionProto.derivativeSecTypes + ) + return contractDescription + + +def createMatchingSymbolsRequestProto( + reqId: int, pattern: str +) -> MatchingSymbolsRequestProto: + """Create a MatchingSymbolsRequest protobuf message. + + Args: + reqId (int): The request ID. + pattern (str): The pattern to match. + + Returns: + MatchingSymbolsRequestProto: The created MatchingSymbolsRequest protobuf + message. + """ + matchingSymbolsRequestProto = MatchingSymbolsRequestProto() + matchingSymbolsRequestProto.reqId = reqId + matchingSymbolsRequestProto.pattern = pattern + return matchingSymbolsRequestProto + + +def createMarketRuleRequestProto(marketRuleId: int) -> MarketRuleRequestProto: + """Create a MarketRuleRequest protobuf message. + + Args: + marketRuleId (int): The ID of the market rule. + + Returns: + MarketRuleRequestProto: The created MarketRuleRequest protobuf message. + """ + marketRuleRequestProto = MarketRuleRequestProto() + marketRuleRequestProto.marketRuleId = marketRuleId + return marketRuleRequestProto + + +def createContractProto(contract: Contract, order: Order | None) -> ContractProto: + """Create a Contract protobuf message. + + Args: + contract (Contract): The contract. + + Returns: + ContractProto: The created Contract protobuf message. + """ + contractProto = ContractProto() + if contract.conId: + contractProto.conId = contract.conId + if contract.symbol: + contractProto.symbol = contract.symbol + if contract.secType: + contractProto.secType = contract.secType + if contract.lastTradeDateOrContractMonth: + contractProto.lastTradeDateOrContractMonth = ( + contract.lastTradeDateOrContractMonth + ) + if contract.strike: + contractProto.strike = contract.strike + if contract.right: + contractProto.right = contract.right + if contract.multiplier: + contractProto.multiplier = ( + float(contract.multiplier) if contract.multiplier else 0.0 + ) + if contract.exchange: + contractProto.exchange = contract.exchange + if contract.primaryExchange: + contractProto.primaryExch = contract.primaryExchange + if contract.currency: + contractProto.currency = contract.currency + if contract.localSymbol: + contractProto.localSymbol = contract.localSymbol + if contract.tradingClass: + contractProto.tradingClass = contract.tradingClass + if contract.includeExpired: + contractProto.includeExpired = contract.includeExpired + if contract.secIdType: + contractProto.secIdType = contract.secIdType + if contract.secId: + contractProto.secId = contract.secId + if contract.description: + contractProto.description = contract.description + if contract.issuerId: + contractProto.issuerId = contract.issuerId + + comboLegProtoList = createComboLegProtoList(contract, order) + if comboLegProtoList: + contractProto.comboLegs.extend(comboLegProtoList) + + deltaNeutralContractProto = createDeltaNeutralContractProto(contract) + if deltaNeutralContractProto is not None: + contractProto.deltaNeutralContract.CopyFrom(deltaNeutralContractProto) + + return contractProto + + +def createDeltaNeutralContractProto( + contract: Contract, +) -> DeltaNeutralContractProto | None: + deltaNeutralContractProto = None + if contract.deltaNeutralContract is not None: + deltaNeutralContract = contract.deltaNeutralContract + deltaNeutralContractProto = DeltaNeutralContractProto() + if deltaNeutralContract.conId is not None: + deltaNeutralContractProto.conId = deltaNeutralContract.conId + if deltaNeutralContract.delta is not None: + deltaNeutralContractProto.delta = deltaNeutralContract.delta + if deltaNeutralContract.price is not None: + deltaNeutralContractProto.price = deltaNeutralContract.price + return deltaNeutralContractProto + + +def createComboLegProtoList( + contract: Contract, order: Order | None +) -> list[ComboLegProto]: + comboLegs = contract.comboLegs + orderComboLegs = order.orderComboLegs if order else None + comboLegProtoList = [] + if comboLegs: + for i, comboLeg in enumerate(comboLegs): + perLegPrice = UNSET_DOUBLE + if orderComboLegs and i < len(orderComboLegs): + perLegPrice = orderComboLegs[i].price + comboLegProto = createComboLegProto(comboLeg, perLegPrice) + if comboLegProto is not None: + comboLegProtoList.append(comboLegProto) + return comboLegProtoList + + +def createComboLegProto(comboLeg: ComboLeg, perLegPrice: float) -> ComboLegProto: + comboLegProto = ComboLegProto() + if comboLeg.conId is not None: + comboLegProto.conId = comboLeg.conId + if comboLeg.ratio is not None: + comboLegProto.ratio = comboLeg.ratio + if comboLeg.action is not None: + comboLegProto.action = comboLeg.action + if comboLegProto.exchange is not None: + comboLegProto.exchange = comboLeg.exchange + if comboLegProto.openClose is not None: + comboLegProto.openClose = comboLeg.openClose + if comboLegProto.shortSalesSlot is not None: + comboLegProto.shortSalesSlot = comboLeg.shortSaleSlot + if comboLegProto.designatedLocation is not None: + comboLegProto.designatedLocation = comboLeg.designatedLocation + if comboLegProto.exemptCode is not None: + comboLegProto.exemptCode = comboLeg.exemptCode + if comboLegProto.perLegPrice is not None: + comboLegProto.perLegPrice = perLegPrice + return comboLegProto diff --git a/ib_async/protobuf_converters/historical_data_converters.py b/ib_async/protobuf_converters/historical_data_converters.py new file mode 100644 index 00000000..6fec6516 --- /dev/null +++ b/ib_async/protobuf_converters/historical_data_converters.py @@ -0,0 +1,102 @@ +"""Historical data protobuf converters""" + +from ib_async.objects import BarData +from ..util import isValidIntValue, parseIBDatetime + +from ..contract import Contract, TagValue +from ..protobuf.HeadTimestampRequest_pb2 import ( + HeadTimestampRequest as HeadTimestampRequestProto, +) +from ..protobuf.HistoricalDataRequest_pb2 import ( + HistoricalDataRequest as HistoricalDataRequestProto, +) +from ..protobuf.HistoricalDataBar_pb2 import ( + HistoricalDataBar as HistoricalDataBarProto, +) +from .contract_converters import createContractProto + + +def createHeadTimestampRequestProto( + reqId: int, contract: Contract, whatToShow: str, useRTH: bool, formatDate: int +) -> HeadTimestampRequestProto: + headTimestampRequestProto = HeadTimestampRequestProto() + if isValidIntValue(reqId): + headTimestampRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + if contractProto is not None: + headTimestampRequestProto.contract.CopyFrom(contractProto) + if whatToShow: + headTimestampRequestProto.whatToShow = whatToShow + if useRTH: + headTimestampRequestProto.useRTH = useRTH + if isValidIntValue(formatDate): + headTimestampRequestProto.formatDate = formatDate + return headTimestampRequestProto + + +def fillTagValueList(tagValueList: list[TagValue], orderProtoMap: dict): + if tagValueList is not None and tagValueList: + for tagValue in tagValueList: + orderProtoMap[tagValue.tag] = tagValue.value + + +def createHistoricalDataRequestProto( + reqId: int, + contract: Contract, + endDateTime: str, + duration: str, + barSizeSetting: str, + whatToShow: str, + useRTH: bool, + formatDate: int, + keepUpToDate: bool, + chartOptionsList: list[TagValue], +) -> HistoricalDataRequestProto: + historicalDataRequestProto = HistoricalDataRequestProto() + if isValidIntValue(reqId): + historicalDataRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + if contractProto is not None: + historicalDataRequestProto.contract.CopyFrom(contractProto) + if endDateTime: + historicalDataRequestProto.endDateTime = endDateTime + if duration: + historicalDataRequestProto.duration = duration + if barSizeSetting: + historicalDataRequestProto.barSizeSetting = barSizeSetting + if whatToShow: + historicalDataRequestProto.whatToShow = whatToShow + if useRTH: + historicalDataRequestProto.useRTH = useRTH + if isValidIntValue(formatDate): + historicalDataRequestProto.formatDate = formatDate + if keepUpToDate: + historicalDataRequestProto.keepUpToDate = keepUpToDate + fillTagValueList(chartOptionsList, historicalDataRequestProto.chartOptions) + return historicalDataRequestProto + + +def createBarDataList( + historicalDataBarsProto: list[HistoricalDataBarProto], +) -> list[BarData]: + bars = [] + for barProto in historicalDataBarsProto: + bar = BarData() + if barProto.HasField("date"): + bar.date = parseIBDatetime(barProto.date) + if barProto.HasField("open"): + bar.open = barProto.open + if barProto.HasField("high"): + bar.high = barProto.high + if barProto.HasField("low"): + bar.low = barProto.low + if barProto.HasField("close"): + bar.close = barProto.close + if barProto.HasField("volume"): + bar.volume = float(barProto.volume) + if barProto.HasField("WAP"): + bar.average = float(barProto.WAP) + if barProto.HasField("barCount"): + bar.barCount = barProto.barCount + bars.append(bar) + return bars diff --git a/ib_async/protobuf_converters/trade_converter.py b/ib_async/protobuf_converters/trade_converter.py new file mode 100644 index 00000000..2ece6159 --- /dev/null +++ b/ib_async/protobuf_converters/trade_converter.py @@ -0,0 +1,807 @@ +""" +Converters for trade-related Protobuf messages. +""" + +from decimal import Decimal + +from ib_async.contract import ComboLeg, Contract, DeltaNeutralContract +from ib_async.objects import ( + CommissionReport, + Execution, + ExecutionFilter, + Fill, + OptionExerciseType, + SoftDollarTier, + TagValue, +) +from ib_async.order import ( + ExecutionCondition, + MarginCondition, + Order, + OrderAllocation, + OrderComboLeg, + OrderCondition, + OrderState, + OrderStatus, + PercentChangeCondition, + PriceCondition, + TimeCondition, + Trade, + VolumeCondition, +) +from ib_async.util import ( + UNSET_INTEGER, + decimalMaxString, + getEnumTypeFromString, + isValidIntValue, + parseIBDatetime, +) + +from ..protobuf.CommissionAndFeesReport_pb2 import ( + CommissionAndFeesReport as CommissionReportProto, +) +from ..protobuf.Contract_pb2 import Contract as ContractProto +from ..protobuf.Execution_pb2 import Execution as ExecutionProto +from ..protobuf.ExecutionDetails_pb2 import ( + ExecutionDetails as ExecutionDetailsProto, +) +from ..protobuf.ExecutionFilter_pb2 import ExecutionFilter as ExecutionFilterProto +from ..protobuf.ExecutionRequest_pb2 import ExecutionRequest as ExecutionRequestProto +from ..protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto +from ..protobuf.Order_pb2 import Order as OrderProto +from ..protobuf.OrderAllocation_pb2 import ( + OrderAllocation as OrderAllocationProto, +) +from ..protobuf.OrderCondition_pb2 import OrderCondition as OrderConditionProto +from ..protobuf.OrderState_pb2 import OrderState as OrderStateProto +from ..protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto +from ..protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto +from ..protobuf_converters.contract_converters import createContract + + +def createOrderComboLegs(contractProto: ContractProto) -> list[OrderComboLeg]: + orderComboLegs = [] + comboLegProtoList = contractProto.comboLegs + if comboLegProtoList: + for comboLegProto in comboLegProtoList: + orderComboLeg = OrderComboLeg() + if comboLegProto.HasField("perLegPrice"): + orderComboLeg.price = comboLegProto.perLegPrice + orderComboLegs.append(orderComboLeg) + + return orderComboLegs + +def createOrder( + orderId: int, contractProto: ContractProto, orderProto: OrderProto +) -> Order: + order = Order() + if isValidIntValue(orderId): + order.orderId = orderId + if orderProto.HasField("orderId"): + order.orderId = orderProto.orderId + if orderProto.HasField("action"): + order.action = orderProto.action + if orderProto.HasField("totalQuantity"): + order.totalQuantity = orderProto.totalQuantity + if orderProto.HasField("orderType"): + order.orderType = orderProto.orderType + if orderProto.HasField("lmtPrice"): + order.lmtPrice = orderProto.lmtPrice + if orderProto.HasField("auxPrice"): + order.auxPrice = orderProto.auxPrice + if orderProto.HasField("tif"): + order.tif = orderProto.tif + if orderProto.HasField("ocaGroup"): + order.ocaGroup = orderProto.ocaGroup + if orderProto.HasField("account"): + order.account = orderProto.account + if orderProto.HasField("openClose"): + order.openClose = orderProto.openClose + if orderProto.HasField("origin"): + order.origin = orderProto.origin + if orderProto.HasField("orderRef"): + order.orderRef = orderProto.orderRef + if orderProto.HasField("clientId"): + order.clientId = orderProto.clientId + if orderProto.HasField("permId"): + order.permId = orderProto.permId + if orderProto.HasField("outsideRth"): + order.outsideRth = orderProto.outsideRth + if orderProto.HasField("hidden"): + order.hidden = orderProto.hidden + if orderProto.HasField("discretionaryAmt"): + order.discretionaryAmt = orderProto.discretionaryAmt + if orderProto.HasField("goodAfterTime"): + order.goodAfterTime = orderProto.goodAfterTime + if orderProto.HasField("faGroup"): + order.faGroup = orderProto.faGroup + if orderProto.HasField("faMethod"): + order.faMethod = orderProto.faMethod + if orderProto.HasField("faPercentage"): + order.faPercentage = orderProto.faPercentage + if orderProto.HasField("modelCode"): + order.modelCode = orderProto.modelCode + if orderProto.HasField("goodTillDate"): + order.goodTillDate = orderProto.goodTillDate + if orderProto.HasField("rule80A"): + order.rule80A = orderProto.rule80A + if orderProto.HasField("percentOffset"): + order.percentOffset = orderProto.percentOffset + if orderProto.HasField("settlingFirm"): + order.settlingFirm = orderProto.settlingFirm + if orderProto.HasField("shortSaleSlot"): + order.shortSaleSlot = orderProto.shortSaleSlot + if orderProto.HasField("designatedLocation"): + order.designatedLocation = orderProto.designatedLocation + if orderProto.HasField("exemptCode"): + order.exemptCode = orderProto.exemptCode + if orderProto.HasField("startingPrice"): + order.startingPrice = orderProto.startingPrice + if orderProto.HasField("stockRefPrice"): + order.stockRefPrice = orderProto.stockRefPrice + if orderProto.HasField("delta"): + order.delta = orderProto.delta + if orderProto.HasField("stockRangeLower"): + order.stockRangeLower = orderProto.stockRangeLower + if orderProto.HasField("stockRangeUpper"): + order.stockRangeUpper = orderProto.stockRangeUpper + if orderProto.HasField("displaySize"): + order.displaySize = orderProto.displaySize + if orderProto.HasField("blockOrder"): + order.blockOrder = orderProto.blockOrder + if orderProto.HasField("sweepToFill"): + order.sweepToFill = orderProto.sweepToFill + if orderProto.HasField("allOrNone"): + order.allOrNone = orderProto.allOrNone + if orderProto.HasField("minQty"): + order.minQty = orderProto.minQty + if orderProto.HasField("ocaType"): + order.ocaType = orderProto.ocaType + if orderProto.HasField("parentId"): + order.parentId = orderProto.parentId + if orderProto.HasField("triggerMethod"): + order.triggerMethod = orderProto.triggerMethod + if orderProto.HasField("volatility"): + order.volatility = orderProto.volatility + if orderProto.HasField("volatilityType"): + order.volatilityType = orderProto.volatilityType + if orderProto.HasField("deltaNeutralOrderType"): + order.deltaNeutralOrderType = orderProto.deltaNeutralOrderType + if orderProto.HasField("deltaNeutralAuxPrice"): + order.deltaNeutralAuxPrice = orderProto.deltaNeutralAuxPrice + if orderProto.HasField("deltaNeutralConId"): + order.deltaNeutralConId = orderProto.deltaNeutralConId + if orderProto.HasField("deltaNeutralSettlingFirm"): + order.deltaNeutralSettlingFirm = orderProto.deltaNeutralSettlingFirm + if orderProto.HasField("deltaNeutralClearingAccount"): + order.deltaNeutralClearingAccount = orderProto.deltaNeutralClearingAccount + if orderProto.HasField("deltaNeutralClearingIntent"): + order.deltaNeutralClearingIntent = orderProto.deltaNeutralClearingIntent + if orderProto.HasField("deltaNeutralOpenClose"): + order.deltaNeutralOpenClose = orderProto.deltaNeutralOpenClose + if orderProto.HasField("deltaNeutralShortSale"): + order.deltaNeutralShortSale = orderProto.deltaNeutralShortSale + if orderProto.HasField("deltaNeutralShortSaleSlot"): + order.deltaNeutralShortSaleSlot = orderProto.deltaNeutralShortSaleSlot + if orderProto.HasField("deltaNeutralDesignatedLocation"): + order.deltaNeutralDesignatedLocation = orderProto.deltaNeutralDesignatedLocation + if orderProto.HasField("continuousUpdate"): + order.continuousUpdate = orderProto.continuousUpdate + if orderProto.HasField("referencePriceType"): + order.referencePriceType = orderProto.referencePriceType + if orderProto.HasField("trailStopPrice"): + order.trailStopPrice = orderProto.trailStopPrice + if orderProto.HasField("trailingPercent"): + order.trailingPercent = orderProto.trailingPercent + + orderComboLegs = createOrderComboLegs(contractProto) + if orderComboLegs is not None and orderComboLegs: + order.orderComboLegs = orderComboLegs + + order.smartComboRoutingParams = createTagValueList( + orderProto.smartComboRoutingParams + ) + + if orderProto.HasField("scaleInitLevelSize"): + order.scaleInitLevelSize = orderProto.scaleInitLevelSize + if orderProto.HasField("scaleSubsLevelSize"): + order.scaleSubsLevelSize = orderProto.scaleSubsLevelSize + if orderProto.HasField("scalePriceIncrement"): + order.scalePriceIncrement = orderProto.scalePriceIncrement + if orderProto.HasField("scalePriceAdjustValue"): + order.scalePriceAdjustValue = orderProto.scalePriceAdjustValue + if orderProto.HasField("scalePriceAdjustInterval"): + order.scalePriceAdjustInterval = orderProto.scalePriceAdjustInterval + if orderProto.HasField("scaleProfitOffset"): + order.scaleProfitOffset = orderProto.scaleProfitOffset + if orderProto.HasField("scaleAutoReset"): + order.scaleAutoReset = orderProto.scaleAutoReset + if orderProto.HasField("scaleInitPosition"): + order.scaleInitPosition = orderProto.scaleInitPosition + if orderProto.HasField("scaleInitFillQty"): + order.scaleInitFillQty = orderProto.scaleInitFillQty + if orderProto.HasField("scaleRandomPercent"): + order.scaleRandomPercent = orderProto.scaleRandomPercent + if orderProto.HasField("hedgeType"): + order.hedgeType = orderProto.hedgeType + if ( + orderProto.HasField("hedgeType") + and orderProto.HasField("hedgeParam") + and orderProto.hedgeType + ): + order.hedgeParam = orderProto.hedgeParam + if orderProto.HasField("optOutSmartRouting"): + order.optOutSmartRouting = orderProto.optOutSmartRouting + if orderProto.HasField("clearingAccount"): + order.clearingAccount = orderProto.clearingAccount + if orderProto.HasField("clearingIntent"): + order.clearingIntent = orderProto.clearingIntent + if orderProto.HasField("notHeld"): + order.notHeld = orderProto.notHeld + + if orderProto.HasField("algoStrategy"): + order.algoStrategy = orderProto.algoStrategy + order.algoParams = createTagValueList(orderProto.algoParams) + + if orderProto.HasField("solicited"): + order.solicited = orderProto.solicited + if orderProto.HasField("whatIf"): + order.whatIf = orderProto.whatIf + if orderProto.HasField("randomizeSize"): + order.randomizeSize = orderProto.randomizeSize + if orderProto.HasField("randomizePrice"): + order.randomizePrice = orderProto.randomizePrice + if orderProto.HasField("referenceContractId"): + order.referenceContractId = orderProto.referenceContractId + if orderProto.HasField("isPeggedChangeAmountDecrease"): + order.isPeggedChangeAmountDecrease = orderProto.isPeggedChangeAmountDecrease + if orderProto.HasField("peggedChangeAmount"): + order.peggedChangeAmount = orderProto.peggedChangeAmount + if orderProto.HasField("referenceChangeAmount"): + order.referenceChangeAmount = orderProto.referenceChangeAmount + if orderProto.HasField("referenceExchangeId"): + order.referenceExchangeId = orderProto.referenceExchangeId + + conditions = createOrderConditions(orderProto) + if conditions is not None and conditions: + order.conditions = conditions + if orderProto.HasField("conditionsIgnoreRth"): + order.conditionsIgnoreRth = orderProto.conditionsIgnoreRth + if orderProto.HasField("conditionsCancelOrder"): + order.conditionsCancelOrder = orderProto.conditionsCancelOrder + + if orderProto.HasField("adjustedOrderType"): + order.adjustedOrderType = orderProto.adjustedOrderType + if orderProto.HasField("triggerPrice"): + order.triggerPrice = orderProto.triggerPrice + if orderProto.HasField("lmtPriceOffset"): + order.lmtPriceOffset = orderProto.lmtPriceOffset + if orderProto.HasField("adjustedStopPrice"): + order.adjustedStopPrice = orderProto.adjustedStopPrice + if orderProto.HasField("adjustedStopLimitPrice"): + order.adjustedStopLimitPrice = orderProto.adjustedStopLimitPrice + if orderProto.HasField("adjustedTrailingAmount"): + order.adjustedTrailingAmount = orderProto.adjustedTrailingAmount + if orderProto.HasField("adjustableTrailingUnit"): + order.adjustableTrailingUnit = orderProto.adjustableTrailingUnit + + softDollarTier = createSoftDollarTierFromOrder(orderProto) + if softDollarTier is not None: + order.softDollarTier = softDollarTier + + if orderProto.HasField("cashQty"): + order.cashQty = orderProto.cashQty + if orderProto.HasField("dontUseAutoPriceForHedge"): + order.dontUseAutoPriceForHedge = orderProto.dontUseAutoPriceForHedge + if orderProto.HasField("isOmsContainer"): + order.isOmsContainer = orderProto.isOmsContainer + if orderProto.HasField("discretionaryUpToLimitPrice"): + order.discretionaryUpToLimitPrice = orderProto.discretionaryUpToLimitPrice + if orderProto.HasField("usePriceMgmtAlgo"): + order.usePriceMgmtAlgo = orderProto.usePriceMgmtAlgo + if orderProto.HasField("duration"): + order.duration = orderProto.duration + if orderProto.HasField("postToAts"): + order.postToAts = orderProto.postToAts + if orderProto.HasField("autoCancelParent"): + order.autoCancelParent = orderProto.autoCancelParent + if orderProto.HasField("minTradeQty"): + order.minTradeQty = orderProto.minTradeQty + if orderProto.HasField("minCompeteSize"): + order.minCompeteSize = orderProto.minCompeteSize + if orderProto.HasField("competeAgainstBestOffset"): + order.competeAgainstBestOffset = orderProto.competeAgainstBestOffset + if orderProto.HasField("midOffsetAtWhole"): + order.midOffsetAtWhole = orderProto.midOffsetAtWhole + if orderProto.HasField("midOffsetAtHalf"): + order.midOffsetAtHalf = orderProto.midOffsetAtHalf + if orderProto.HasField("customerAccount"): + order.customerAccount = orderProto.customerAccount + if orderProto.HasField("professionalCustomer"): + order.professionalCustomer = orderProto.professionalCustomer + if orderProto.HasField("bondAccruedInterest"): + order.bondAccruedInterest = orderProto.bondAccruedInterest + if orderProto.HasField("includeOvernight"): + order.includeOvernight = orderProto.includeOvernight + if orderProto.HasField("extOperator"): + order.extOperator = orderProto.extOperator + if orderProto.HasField("manualOrderIndicator"): + order.manualOrderIndicator = orderProto.manualOrderIndicator + if orderProto.HasField("submitter"): + order.submitter = orderProto.submitter + if orderProto.HasField("imbalanceOnly"): + order.imbalanceOnly = orderProto.imbalanceOnly + if orderProto.HasField("autoCancelDate"): + order.autoCancelDate = orderProto.autoCancelDate + if orderProto.HasField("filledQuantity"): + order.filledQuantity = Decimal(orderProto.filledQuantity) + if orderProto.HasField("refFuturesConId"): + order.refFuturesConId = orderProto.refFuturesConId + if orderProto.HasField("shareholder"): + order.shareholder = orderProto.shareholder + if orderProto.HasField("routeMarketableToBbo"): + order.routeMarketableToBbo = orderProto.routeMarketableToBbo + if orderProto.HasField("parentPermId"): + order.parentPermId = orderProto.parentPermId + + return order + + +def createOrderConditions(orderProto: OrderProto) -> list[OrderCondition]: + orderConditions: list[OrderCondition] = [] + orderConditionsProtoList = [] + if orderProto.conditions is not None: + orderConditionsProtoList = orderProto.conditions + + if orderConditionsProtoList: + for orderConditionProto in orderConditionsProtoList: + conditionType = ( + orderConditionProto.type if orderConditionProto.HasField("type") else 0 + ) + + condition = None + if PriceCondition.condType == conditionType: + condition: PriceCondition = createPriceCondition(orderConditionProto) + elif TimeCondition.condType == conditionType: + condition: TimeCondition = createTimeCondition(orderConditionProto) + elif MarginCondition.condType == conditionType: + condition: MarginCondition = createMarginCondition(orderConditionProto) + elif ExecutionCondition.condType == conditionType: + condition: ExecutionCondition = createExecutionCondition( + orderConditionProto + ) + elif VolumeCondition.condType == conditionType: + condition: VolumeCondition = createVolumeCondition(orderConditionProto) + elif PercentChangeCondition.condType == conditionType: + condition: PercentChangeCondition = createPercentChangeCondition( + orderConditionProto + ) + + if condition is not None: + orderConditions.append(condition) + + return orderConditions + + +def setConditionFields( + orderConditionProto: OrderConditionProto, orderCondition: OrderCondition +): + if orderConditionProto.HasField("isConjunctionConnection"): + orderCondition.conjunction = ( + "a" if orderConditionProto.isConjunctionConnection else "o" + ) + + +def setOperatorConditionFields( + orderConditionProto: OrderConditionProto, operatorCondition: PriceCondition +): + setConditionFields(orderConditionProto, operatorCondition) + if orderConditionProto.HasField("isMore"): + operatorCondition.isMore = orderConditionProto.isMore + + +def setContractConditionFields( + orderConditionProto: OrderConditionProto, contractCondition: PriceCondition +): + setOperatorConditionFields(orderConditionProto, contractCondition) + if orderConditionProto.HasField("conId"): + contractCondition.conId = orderConditionProto.conId + if orderConditionProto.HasField("exchange"): + contractCondition.exch = orderConditionProto.exchange + + +def createPriceCondition(orderConditionProto: OrderConditionProto) -> PriceCondition: + priceCondition = PriceCondition() + setContractConditionFields(orderConditionProto, priceCondition) + if orderConditionProto.HasField("price"): + priceCondition.price = orderConditionProto.price + if orderConditionProto.HasField("triggerMethod"): + priceCondition.triggerMethod = orderConditionProto.triggerMethod + return priceCondition + + +def createTimeCondition(orderConditionProto: OrderConditionProto) -> TimeCondition: + timeCondition = TimeCondition() + setOperatorConditionFields(orderConditionProto, timeCondition) + if orderConditionProto.HasField("time"): + timeCondition.time = orderConditionProto.time + return timeCondition + + +def createMarginCondition(orderConditionProto: OrderConditionProto) -> MarginCondition: + marginCondition = MarginCondition() + setOperatorConditionFields(orderConditionProto, marginCondition) + if orderConditionProto.HasField("percent"): + marginCondition.percent = orderConditionProto.percent + return marginCondition + + +def createExecutionCondition( + orderConditionProto: OrderConditionProto, +) -> ExecutionCondition: + executionCondition = ExecutionCondition() + setConditionFields(orderConditionProto, executionCondition) + if orderConditionProto.HasField("secType"): + executionCondition.secType = orderConditionProto.secType + if orderConditionProto.HasField("exchange"): + executionCondition.exch = orderConditionProto.exchange + if orderConditionProto.HasField("symbol"): + executionCondition.symbol = orderConditionProto.symbol + return executionCondition + + +def createVolumeCondition(orderConditionProto: OrderConditionProto) -> VolumeCondition: + volumeCondition = VolumeCondition() + setContractConditionFields(orderConditionProto, volumeCondition) + if orderConditionProto.HasField("volume"): + volumeCondition.volume = orderConditionProto.volume + return volumeCondition + + +def createPercentChangeCondition( + orderConditionProto: OrderConditionProto, +) -> PercentChangeCondition: + percentChangeCondition = PercentChangeCondition() + setContractConditionFields(orderConditionProto, percentChangeCondition) + if orderConditionProto.HasField("changePercent"): + percentChangeCondition.changePercent = orderConditionProto.changePercent + return percentChangeCondition + + +def createSoftDollarTierFromOrder(orderProto: OrderProto) -> SoftDollarTier | None: + softDollarTierProto = None + if orderProto.softDollarTier is not None: + softDollarTierProto = orderProto.softDollarTier + return ( + createSoftDollarTier(softDollarTierProto) + if softDollarTierProto is not None + else None + ) + + +def createSoftDollarTier(softDollarTierProto: SoftDollarTierProto) -> SoftDollarTier: + name = "" + value = "" + displayName = "" + softDollarTier = None + if softDollarTierProto is not None: + if softDollarTierProto.HasField("name"): + name = softDollarTierProto.name + if softDollarTierProto.HasField("value"): + value = softDollarTierProto.value + if softDollarTierProto.HasField("displayName"): + displayName = softDollarTierProto.displayName + softDollarTier = SoftDollarTier(name, value, displayName) + + return softDollarTier + + +def createTagValueList(protoMap: dict[str, str]) -> list[TagValue]: + tagValueList = [] + if protoMap is not None and protoMap: + for tag, value in protoMap.items(): + tagValue = TagValue(tag,value) + tagValueList.append(tagValue) + return tagValueList + + +def createOrderState(orderStateProto: OrderStateProto) -> OrderState: + orderState = OrderState() + if orderStateProto.HasField("status"): + orderState.status = orderStateProto.status + if orderStateProto.HasField("initMarginBefore"): + orderState.initMarginBefore = orderStateProto.initMarginBefore + if orderStateProto.HasField("maintMarginBefore"): + orderState.maintMarginBefore = decimalMaxString( + orderStateProto.maintMarginBefore + ) + if orderStateProto.HasField("equityWithLoanBefore"): + orderState.equityWithLoanBefore = decimalMaxString( + orderStateProto.equityWithLoanBefore + ) + if orderStateProto.HasField("initMarginChange"): + orderState.initMarginChange = decimalMaxString(orderStateProto.initMarginChange) + if orderStateProto.HasField("maintMarginChange"): + orderState.maintMarginChange = decimalMaxString( + orderStateProto.maintMarginChange + ) + if orderStateProto.HasField("equityWithLoanChange"): + orderState.equityWithLoanChange = decimalMaxString( + orderStateProto.equityWithLoanChange + ) + if orderStateProto.HasField("initMarginAfter"): + orderState.initMarginAfter = decimalMaxString(orderStateProto.initMarginAfter) + if orderStateProto.HasField("maintMarginAfter"): + orderState.maintMarginAfter = decimalMaxString(orderStateProto.maintMarginAfter) + if orderStateProto.HasField("equityWithLoanAfter"): + orderState.equityWithLoanAfter = decimalMaxString( + orderStateProto.equityWithLoanAfter + ) + if orderStateProto.HasField("commissionAndFees"): + orderState.commissionAndFees = orderStateProto.commissionAndFees + if orderStateProto.HasField("minCommissionAndFees"): + orderState.minCommissionAndFees = orderStateProto.minCommissionAndFees + if orderStateProto.HasField("maxCommissionAndFees"): + orderState.maxCommissionAndFees = orderStateProto.maxCommissionAndFees + if orderStateProto.HasField("commissionAndFeesCurrency"): + orderState.commissionAndFeesCurrency = orderStateProto.commissionAndFeesCurrency + if orderStateProto.HasField("warningText"): + orderState.warningText = orderStateProto.warningText + if orderStateProto.HasField("marginCurrency"): + orderState.marginCurrency = orderStateProto.marginCurrency + if orderStateProto.HasField("initMarginBeforeOutsideRTH"): + orderState.initMarginBeforeOutsideRTH = ( + orderStateProto.initMarginBeforeOutsideRTH + ) + if orderStateProto.HasField("maintMarginBeforeOutsideRTH"): + orderState.maintMarginBeforeOutsideRTH = ( + orderStateProto.maintMarginBeforeOutsideRTH + ) + if orderStateProto.HasField("equityWithLoanBeforeOutsideRTH"): + orderState.equityWithLoanBeforeOutsideRTH = ( + orderStateProto.equityWithLoanBeforeOutsideRTH + ) + if orderStateProto.HasField("initMarginChangeOutsideRTH"): + orderState.initMarginChangeOutsideRTH = ( + orderStateProto.initMarginChangeOutsideRTH + ) + if orderStateProto.HasField("maintMarginChangeOutsideRTH"): + orderState.maintMarginChangeOutsideRTH = ( + orderStateProto.maintMarginChangeOutsideRTH + ) + if orderStateProto.HasField("equityWithLoanChangeOutsideRTH"): + orderState.equityWithLoanChangeOutsideRTH = ( + orderStateProto.equityWithLoanChangeOutsideRTH + ) + if orderStateProto.HasField("initMarginAfterOutsideRTH"): + orderState.initMarginAfterOutsideRTH = orderStateProto.initMarginAfterOutsideRTH + if orderStateProto.HasField("maintMarginAfterOutsideRTH"): + orderState.maintMarginAfterOutsideRTH = ( + orderStateProto.maintMarginAfterOutsideRTH + ) + if orderStateProto.HasField("equityWithLoanAfterOutsideRTH"): + orderState.equityWithLoanAfterOutsideRTH = ( + orderStateProto.equityWithLoanAfterOutsideRTH + ) + if orderStateProto.HasField("suggestedSize"): + orderState.suggestedSize = Decimal(orderStateProto.suggestedSize) + if orderStateProto.HasField("rejectReason"): + orderState.rejectReason = orderStateProto.rejectReason + + orderAllocations = createOrderAllocations(orderStateProto) + if orderAllocations is not None and orderAllocations: + orderState.orderAllocations = orderAllocations + + if orderStateProto.HasField("completedTime"): + orderState.completedTime = orderStateProto.completedTime + if orderStateProto.HasField("completedStatus"): + orderState.completedStatus = orderStateProto.completedStatus + + return orderState + + +def createOrderAllocations(orderStateProto: OrderStateProto) -> list[OrderAllocation]: + orderAllocations = [] + orderAllocationProtoList = [] + if orderStateProto.orderAllocations is not None: + orderAllocationProtoList = orderStateProto.orderAllocations + if orderAllocationProtoList: + for orderAllocationProto in orderAllocationProtoList: + orderAllocation = OrderAllocationProto() + if orderAllocationProto.HasField("account"): + orderAllocation.account = orderAllocationProto.account + if orderAllocationProto.HasField("position"): + orderAllocation.position = Decimal(orderAllocationProto.position) + if orderAllocationProto.HasField("positionDesired"): + orderAllocation.positionDesired = Decimal( + orderAllocationProto.positionDesired + ) + if orderAllocationProto.HasField("positionAfter"): + orderAllocation.positionAfter = Decimal( + orderAllocationProto.positionAfter + ) + if orderAllocationProto.HasField("desiredAllocQty"): + orderAllocation.desiredAllocQty = Decimal( + orderAllocationProto.desiredAllocQty + ) + if orderAllocationProto.HasField("allowedAllocQty"): + orderAllocation.allowedAllocQty = Decimal( + orderAllocationProto.allowedAllocQty + ) + if orderAllocationProto.HasField("isMonetary"): + orderAllocation.isMonetary = orderAllocationProto.isMonetary + orderAllocations.append(orderAllocation) + return orderAllocations + + +def createContractFromExecutionDetails( + exec_details_proto: ExecutionDetailsProto, +) -> Contract: + """ + Create a Contract object from a Protobuf ExecutionDetails message. + """ + return createContract(exec_details_proto.contract) + + +def createOrderStatus(orderStatusProto: OrderStatusProto) -> OrderStatus: + orderStatus = OrderStatus() + if orderStatusProto.HasField("orderId"): + orderStatus.orderId = orderStatusProto.orderId + if orderStatusProto.HasField("status"): + orderStatus.status = orderStatusProto.status + if orderStatusProto.HasField("filled"): + orderStatus.filled = Decimal(orderStatusProto.filled) + if orderStatusProto.HasField("remaining"): + orderStatus.remaining = Decimal(orderStatusProto.remaining) + if orderStatusProto.HasField("avgFillPrice"): + orderStatus.avgFillPrice = orderStatusProto.avgFillPrice + if orderStatusProto.HasField("permId"): + orderStatus.permId = orderStatusProto.permId + if orderStatusProto.HasField("parentId"): + orderStatus.parentId = orderStatusProto.parentId + if orderStatusProto.HasField("lastFillPrice"): + orderStatus.lastFillPrice = orderStatusProto.lastFillPrice + if orderStatusProto.HasField("clientId"): + orderStatus.clientId = orderStatusProto.clientId + if orderStatusProto.HasField("whyHeld"): + orderStatus.whyHeld = orderStatusProto.whyHeld + if orderStatusProto.HasField("mktCapPrice"): + orderStatus.mktCapPrice = orderStatusProto.mktCapPrice + return orderStatus + + +def createExecution(executionProto: ExecutionProto) -> Execution: + execution = Execution() + if executionProto.HasField("execId"): + execution.execId = executionProto.execId + if executionProto.HasField("time"): + execution.time = parseIBDatetime(executionProto.time) + if executionProto.HasField("acctNumber"): + execution.acctNumber = executionProto.acctNumber + if executionProto.HasField("exchange"): + execution.exchange = executionProto.exchange + if executionProto.HasField("side"): + execution.side = executionProto.side + if executionProto.HasField("shares"): + execution.shares = float(executionProto.shares) + if executionProto.HasField("price"): + execution.price = executionProto.price + if executionProto.HasField("permId"): + execution.permId = executionProto.permId + if executionProto.HasField("clientId"): + execution.clientId = executionProto.clientId + if executionProto.HasField("orderId"): + execution.orderId = executionProto.orderId + if executionProto.HasField("isLiquidation"): + execution.liquidation = 1 if executionProto.isLiquidation else 0 + if executionProto.HasField("cumQty"): + execution.cumQty = float(executionProto.cumQty) + if executionProto.HasField("avgPrice"): + execution.avgPrice = executionProto.avgPrice + if executionProto.HasField("orderRef"): + execution.orderRef = executionProto.orderRef + if executionProto.HasField("evRule"): + execution.evRule = executionProto.evRule + if executionProto.HasField("evMultiplier"): + execution.evMultiplier = executionProto.evMultiplier + if executionProto.HasField("modelCode"): + execution.modelCode = executionProto.modelCode + if executionProto.HasField("lastLiquidity"): + execution.lastLiquidity = executionProto.lastLiquidity + if executionProto.HasField("isPriceRevisionPending"): + execution.pendingPriceRevision = executionProto.isPriceRevisionPending + if executionProto.HasField("submitter"): + execution.submitter = executionProto.submitter + if executionProto.HasField("optExerciseOrLapseType"): + execution.optExerciseOrLapseType = getEnumTypeFromString( + OptionExerciseType, executionProto.optExerciseOrLapseType + ) + return execution + + +def createFill(execDetailsProto: ExecutionDetailsProto) -> Fill: + contract = createContract(execDetailsProto.contract) + execution = createExecution(execDetailsProto.execution) + return Fill( + contract, execution, CommissionReport(execId=execution.execId), execution.time + ) + + +def createTradeFromOpenOrder(openOrderProto: OpenOrderProto) -> "Trade": + from ib_async.order import Trade + + contract = createContract(openOrderProto.contract) + order = createOrder( + openOrderProto.order.orderId, openOrderProto.contract, openOrderProto.order + ) + orderStatus = createOrderStatus(openOrderProto.order) + return Trade(contract, order, orderStatus, [], []) + + +def createCommissionReport( + commissionReportProto: CommissionReportProto, +) -> CommissionReport: + commissionReport = CommissionReport() + commissionReport.execId = ( + commissionReportProto.execId if commissionReportProto.HasField("execId") else "" + ) + commissionReport.commission = ( + commissionReportProto.commissionAndFees + if commissionReportProto.HasField("commissionAndFees") + else 0.0 + ) + commissionReport.currency = ( + commissionReportProto.currency + if commissionReportProto.HasField("currency") + else "" + ) + commissionReport.realizedPNL = ( + commissionReportProto.realizedPNL + if commissionReportProto.HasField("realizedPNL") + else 0.0 + ) + commissionReport.yield_ = ( + commissionReportProto.bondYield + if commissionReportProto.HasField("bondYield") + else 0.0 + ) + commissionReport.yieldRedemptionDate = ( + int(commissionReportProto.yieldRedemptionDate) + if commissionReportProto.HasField("yieldRedemptionDate") + else 0 + ) + return commissionReport + + +def createExecutionRequestProto( + reqId: int, execFilter: ExecutionFilter +) -> ExecutionRequestProto: + """ + Create an ExecutionRequest protobuf message. + """ + # execution filter + executionFilterProto = ExecutionFilterProto() + if execFilter.clientId is not None: + executionFilterProto.clientId = execFilter.clientId + if execFilter.acctCode is not None: + executionFilterProto.acctCode = execFilter.acctCode + if execFilter.time is not None: + executionFilterProto.time = execFilter.time + if execFilter.symbol is not None: + executionFilterProto.symbol = execFilter.symbol + if execFilter.secType is not None: + executionFilterProto.secType = execFilter.secType + if execFilter.exchange is not None: + executionFilterProto.exchange = execFilter.exchange + if execFilter.side is not None: + executionFilterProto.side = execFilter.side + if execFilter.lastNDays != UNSET_INTEGER: + executionFilterProto.lastNDays = execFilter.lastNDays + if execFilter.specificDates is not None and execFilter.specificDates: + executionFilterProto.specificDates.extend(execFilter.specificDates) + + # execution request + executionRequestProto = ExecutionRequestProto() + executionRequestProto.reqId = reqId + executionRequestProto.executionFilter.CopyFrom(executionFilterProto) + return executionRequestProto diff --git a/ib_async/util.py b/ib_async/util.py index 8462c515..c1a71244 100644 --- a/ib_async/util.py +++ b/ib_async/util.py @@ -9,6 +9,7 @@ import sys import time from dataclasses import fields, is_dataclass +from decimal import Decimal from typing import ( Any, AsyncIterator, @@ -38,6 +39,7 @@ EPOCH: Final = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc) UNSET_INTEGER: Final = 2**31 - 1 UNSET_DOUBLE: Final = sys.float_info.max +UNSET_DECIMAL = Decimal(2**127 - 1) Time_t: TypeAlias = dt.time | dt.datetime @@ -598,3 +600,30 @@ def parseIBDatetime(s: str) -> Union[dt.date, dt.datetime]: t = dt.datetime.strptime(ss, "%Y%m%d%H:%M:%S") return t + + +def decimalMaxString(val: Decimal): + val = Decimal(val) + return f"{val:f}" if val != UNSET_DECIMAL else "" + + +def floatMaxString(val: float): + if val is None: + return "" + return ( + f"{val:.8f}".rstrip("0").rstrip(".").rstrip(",") if val != UNSET_DOUBLE else "" + ) + + +def getEnumTypeFromString(cls, stringIn): + for item in cls: + if item.value[0] == stringIn: + return item + return listOfValues(cls)[0] + + +def listOfValues(cls): + return list(map(lambda c: c, cls)) + +def isValidIntValue(val: int) -> bool: + return val != UNSET_INTEGER diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 1ce3b012..4a305d32 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -10,6 +10,8 @@ from datetime import datetime from typing import Any, cast, Final, Optional, TYPE_CHECKING, TypeAlias, Union +import eventkit as ev + from ib_async.contract import ( Contract, ContractDescription, @@ -182,7 +184,7 @@ class Wrapper: portfolio: dict[str, dict[int, PortfolioItem]] = field(init=False) """ account -> conId -> PortfolioItem """ - positions: dict[str, dict[int, Position]] = field(init=False) + positions: defaultdict[str, dict[int, Position]] = field(init=False) """ account -> conId -> Position """ trades: dict[OrderKeyType, Trade] = field(init=False) @@ -190,6 +192,8 @@ class Wrapper: permId2Trade: dict[int, Trade] = field(init=False) """ permId -> Trade """ + _isReady: bool = field(init=False, default=False) + """ wrapper initial status state """ fills: dict[str, Fill] = field(init=False) """ execId -> Fill """ @@ -259,6 +263,7 @@ def __post_init__(self): self.defaultTimezone = self.defaults.timezone self.defaultEmptyPrice = self.defaults.emptyPrice self.defaultEmptySize = self.defaults.emptySize + self.response_bus = ev.Event("Response bus") self.reset() @@ -269,6 +274,7 @@ def reset(self): self.positions = defaultdict(dict) self.trades = {} self.permId2Trade = {} + self._isReady = False self.fills = {} self.newsTicks = [] self.msgId2NewsBulletin = {} @@ -420,112 +426,72 @@ def _setTimer(self, delay: float = 0): # wrapper methods - def connectAck(self): - pass - def nextValidId(self, reqId: int): - pass + self.ib.client.updateReqId(reqId) + self.ib.client._hasReqId = True def managedAccounts(self, accountsList: str): - self.accounts = [a for a in accountsList.split(",") if a] + self.accounts = ( + accountsList.split(",") if isinstance(accountsList, str) else accountsList + ) + self.ib.client._accounts = self.accounts def updateAccountTime(self, timestamp: str): - pass + self.accountTime = timestamp - def updateAccountValue(self, tag: str, val: str, currency: str, account: str): - key = (account, tag, currency, "") - acctVal = AccountValue(account, tag, val, currency, "") - self.accountValues[key] = acctVal - self.ib.accountValueEvent.emit(acctVal) + def updateAccountValue(self, value: AccountValue): + key = (value.account, value.tag, value.currency, "") + self.accountValues[key] = value + if self._isReady: + self.ib.accountValueEvent.emit(value) def accountDownloadEnd(self, _account: str): # sent after updateAccountValue and updatePortfolio both finished - self._endReq("accountValues") + self.response_bus.emit("accountValues", None) - def accountUpdateMulti( - self, - reqId: int, - account: str, - modelCode: str, - tag: str, - val: str, - currency: str, - ): - key = (account, tag, currency, modelCode) - acctVal = AccountValue(account, tag, val, currency, modelCode) - self.accountValues[key] = acctVal - self.ib.accountValueEvent.emit(acctVal) + def accountUpdateMulti(self, reqId: int, value: AccountValue): + key = (value.account, value.tag, value.currency, value.modelCode) + self.accountValues[key] = value + if self._isReady: + self.ib.accountValueEvent.emit(value) + self.response_bus.emit(reqId, value) def accountUpdateMultiEnd(self, reqId: int): - self._endReq(reqId) + self.response_bus.emit(reqId, None) - def accountSummary( - self, _reqId: int, account: str, tag: str, value: str, currency: str - ): - key = (account, tag, currency) - acctVal = AccountValue(account, tag, value, currency, "") - self.acctSummary[key] = acctVal - self.ib.accountSummaryEvent.emit(acctVal) + def accountSummary(self, reqId: int, accountValue: AccountValue): + key = (accountValue.account, accountValue.tag, accountValue.currency) + self.acctSummary[key] = accountValue + if self._isReady: + self.ib.accountValueEvent.emit(accountValue) + self.response_bus.emit(reqId, accountValue) def accountSummaryEnd(self, reqId: int): - self._endReq(reqId) - - def updatePortfolio( - self, - contract: Contract, - posSize: float, - marketPrice: float, - marketValue: float, - averageCost: float, - unrealizedPNL: float, - realizedPNL: float, - account: str, - ): - contract = Contract.recreate(contract) - portfItem = PortfolioItem( - contract, - posSize, - marketPrice, - marketValue, - averageCost, - unrealizedPNL, - realizedPNL, - account, - ) - portfolioItems = self.portfolio[account] + self.response_bus.emit(reqId, None) - if posSize == 0: - portfolioItems.pop(contract.conId, None) + def updatePortfolio(self, portfolioItem: PortfolioItem): + account_portfolio = self.portfolio[portfolioItem.account] + if portfolioItem.position == 0: + account_portfolio.pop(portfolioItem.contract.conId, None) else: - portfolioItems[contract.conId] = portfItem + account_portfolio[portfolioItem.contract.conId] = portfolioItem - self._logger.info(f"updatePortfolio: {portfItem}") - self.ib.updatePortfolioEvent.emit(portfItem) + if self._isReady: + self.ib.updatePortfolioEvent.emit(portfolioItem) - def position( - self, account: str, contract: Contract, posSize: float, avgCost: float - ): - contract = Contract.recreate(contract) - position = Position(account, contract, posSize, avgCost) - positions = self.positions[account] - - # if this updates position to 0 quantity, remove the position - if posSize == 0: - positions.pop(contract.conId, None) + def position(self, position: Position): + account_positions = self.positions[position.account] + if position.position == 0: + account_positions.pop(position.contract.conId, None) else: - # else, add or replace the position in-place - positions[contract.conId] = position - - self._logger.info(f"position: {position}") - results = self._results.get("positions") - - if results is not None: - results.append(position) + account_positions[position.contract.conId] = position - self.ib.positionEvent.emit(position) + if self._isReady: + self.ib.positionEvent.emit(position) + self.response_bus.emit("position", position) def positionEnd(self): - self._endReq("positions") + self.response_bus.emit("position", None) def positionMulti( self, @@ -573,184 +539,93 @@ def pnlSingle( pnlSingle.value = value self.ib.pnlSingleEvent.emit(pnlSingle) - def openOrder( - self, orderId: int, contract: Contract, order: Order, orderState: OrderState - ): - """ - This wrapper is called to: - - * feed in open orders at startup; - * feed in open orders or order updates from other clients and TWS - if clientId=master id; - * feed in manual orders and order updates from TWS if clientId=0; - * handle openOrders and allOpenOrders responses. - """ - if order.whatIf: - # response to whatIfOrder - if float(orderState.initMarginChange) != UNSET_DOUBLE: - self._endReq(order.orderId, orderState) + def openOrder(self, trade: Trade): + if trade.order.whatIf: + self.openOrderEnd() + return else: - key = self.orderKey(order.clientId, order.orderId, order.permId) - trade = self.trades.get(key) - if trade: - trade.order.permId = order.permId - trade.order.totalQuantity = order.totalQuantity - trade.order.lmtPrice = order.lmtPrice - trade.order.auxPrice = order.auxPrice - trade.order.orderType = order.orderType - trade.order.orderRef = order.orderRef - else: - # ignore '?' values in the order - order = Order( - **{k: v for k, v in dataclassAsDict(order).items() if v != "?"} - ) - contract = Contract.recreate(contract) - orderStatus = OrderStatus(orderId=orderId, status=orderState.status) - trade = Trade(contract, order, orderStatus, [], []) + key = self.orderKey( + trade.order.clientId, trade.order.orderId, trade.order.permId + ) + existing_trade = self.trades.get(key) + if existing_trade: + # trade is already known, update it + existing_trade.order.permId = trade.order.permId + existing_trade.order.totalQuantity = trade.order.totalQuantity + existing_trade.order.lmtPrice = trade.order.lmtPrice + existing_trade.order.auxPrice = trade.order.auxPrice + existing_trade.order.orderType = trade.order.orderType + existing_trade.order.orderRef = trade.order.orderRef + else: # new trade self.trades[key] = trade self._logger.info(f"openOrder: {trade}") - self.permId2Trade.setdefault(order.permId, trade) - results = self._results.get("openOrders") - - if results is None: - self.ib.openOrderEvent.emit(trade) - else: - # response to reqOpenOrders or reqAllOpenOrders - results.append(trade) - - # make sure that the client issues order ids larger than any - # order id encountered (even from other clients) to avoid - # "Duplicate order id" error - self.ib.client.updateReqId(orderId + 1) + self.permId2Trade.setdefault(trade.order.permId, trade) + if self._isReady: + self.ib.newOrderEvent.emit(trade) + self.ib.client.updateReqId(trade.order.orderId + 1) + self.response_bus.emit("openOrders", trade) def openOrderEnd(self): - self._endReq("openOrders") + self.response_bus.emit("openOrders", None) - def completedOrder(self, contract: Contract, order: Order, orderState: OrderState): + def completedOrder( + self, contract: Contract, order: Order, orderStatus: OrderStatus + ): contract = Contract.recreate(contract) - orderStatus = OrderStatus(orderId=order.orderId, status=orderState.status) trade = Trade(contract, order, orderStatus, [], []) - self._results["completedOrders"].append(trade) if order.permId not in self.permId2Trade: self.trades[order.permId] = trade self.permId2Trade[order.permId] = trade + self._logger.debug(f"completedOrders: {trade}") + self.response_bus.emit("completedOrders", trade) def completedOrdersEnd(self): - self._endReq("completedOrders") + self.response_bus.emit("completedOrders", None) - def orderStatus( - self, - orderId: int, - status: str, - filled: float, - remaining: float, - avgFillPrice: float, - permId: int, - parentId: int, - lastFillPrice: float, - clientId: int, - whyHeld: str, - mktCapPrice: float = 0.0, - ): - key = self.orderKey(clientId, orderId, permId) - trade = self.trades.get(key) - if trade: - msg: Optional[str] - oldStatus = trade.orderStatus.status - new = dict( - status=status, - filled=filled, - remaining=remaining, - avgFillPrice=avgFillPrice, - permId=permId, - parentId=parentId, - lastFillPrice=lastFillPrice, - clientId=clientId, - whyHeld=whyHeld, - mktCapPrice=mktCapPrice, - ) - curr = dataclassAsDict(trade.orderStatus) - isChanged = curr != {**curr, **new} - - if isChanged: - dataclassUpdate(trade.orderStatus, **new) - msg = "" - elif ( - status == "Submitted" - and trade.log - and trade.log[-1].message == "Modify" - ): - # order modifications are acknowledged - msg = "Modified" - else: - msg = None - - if msg is not None: - logEntry = TradeLogEntry(self.lastTime, status, msg) - trade.log.append(logEntry) - self._logger.info(f"orderStatus: {trade}") - self.ib.orderStatusEvent.emit(trade) - trade.statusEvent.emit(trade) - if status != oldStatus: - if status == OrderStatus.Filled: - trade.filledEvent.emit(trade) - elif status == OrderStatus.Cancelled: - trade.cancelledEvent.emit(trade) - else: - self._logger.error( - "orderStatus: No order found for orderId %s and clientId %s", - orderId, - clientId, - ) - - def execDetails(self, reqId: int, contract: Contract, execution: Execution): - """ - This wrapper handles both live fills and responses to - reqExecutions. - """ - self._logger.info(f"execDetails {execution}") - if execution.orderId == UNSET_INTEGER: - # bug in TWS: executions of manual orders have unset value - execution.orderId = 0 - - trade = self.permId2Trade.get(execution.permId) + def orderStatus(self, orderStatus: OrderStatus): + permId = orderStatus.permId + trade = self.permId2Trade.get(permId) if not trade: - key = self.orderKey(execution.clientId, execution.orderId, execution.permId) - trade = self.trades.get(key) - - # TODO: debug why spread contracts aren't fully detailed here. They have no legs in execDetails, but they do in orderStatus? - if trade and contract == trade.contract: - contract = trade.contract - else: - contract = Contract.recreate(contract) - - execId = execution.execId - isLive = reqId not in self._futures - time = self.lastTime if isLive else execution.time - fill = Fill(contract, execution, CommissionReport(), time) - if execId not in self.fills: - # first time we see this execution so add it - self.fills[execId] = fill - if trade: - trade.fills.append(fill) - logEntry = TradeLogEntry( - time, - trade.orderStatus.status, - f"Fill {execution.shares}@{execution.price}", - ) - trade.log.append(logEntry) - if isLive: - self._logger.info(f"execDetails: {fill}") - self.ib.execDetailsEvent.emit(trade, fill) - trade.fillEvent(trade, fill) - - if not isLive: - self._results[reqId].append(fill) + # trade is not found, create a placeholder + trade = Trade(Contract(), Order(permId=permId), orderStatus, [], []) + self.permId2Trade[permId] = trade + + trade.orderStatus = orderStatus + logEntry = TradeLogEntry(self.lastTime, orderStatus.status) + trade.log.append(logEntry) + self._logger.info(f"orderStatus: {trade}") + + if self._isReady: + trade.statusEvent.emit(trade) + self.ib.orderStatusEvent.emit(trade) + + if orderStatus.status == OrderStatus.Cancelled: + trade.cancelledEvent.emit(trade) + elif orderStatus.status == OrderStatus.Filled: + trade.filledEvent.emit(trade) + + def execDetails(self, reqId: int, fill: Fill): + permId = fill.execution.permId + trade = self.permId2Trade.get(permId) + if not trade: + # trade is not found, create a placeholder + trade = Trade(fill.contract, Order(permId=permId), OrderStatus(), [], []) + self.permId2Trade[permId] = trade + + self.fills[fill.execution.execId] = fill + trade.fills.append(fill) + if self._isReady: + self.ib.execDetailsEvent.emit(trade, fill) + trade.fillEvent.emit(trade, fill) + if fill.commissionReport: + self.ib.commissionReportEvent.emit(trade, fill, fill.commissionReport) + trade.commissionReportEvent.emit(trade, fill, fill.commissionReport) + self.response_bus.emit(reqId, fill) def execDetailsEnd(self, reqId: int): - self._endReq(reqId) + self.response_bus.emit(reqId, None) def commissionReport(self, commissionReport: CommissionReport): if commissionReport.yield_ == UNSET_DOUBLE: @@ -758,41 +633,45 @@ def commissionReport(self, commissionReport: CommissionReport): if commissionReport.realizedPNL == UNSET_DOUBLE: commissionReport.realizedPNL = 0.0 + fill: Fill | None = self.fills.get(commissionReport.execId) - fill = self.fills.get(commissionReport.execId) - if fill: - report = dataclassUpdate(fill.commissionReport, commissionReport) - self._logger.info(f"commissionReport: {report}") - trade = self.permId2Trade.get(fill.execution.permId) - if trade: - self.ib.commissionReportEvent.emit(trade, fill, report) - trade.commissionReportEvent.emit(trade, fill, report) - else: - # this is not a live execution and the order was filled - # before this connection started - pass - else: + if not fill: # commission report is not for this client + return + + trade = self.permId2Trade.get(fill.execution.permId) + if not trade: + return + + report = dataclassUpdate(fill.commissionReport, commissionReport) + + self._logger.info(f"commissionReport: {report}") + if self._isReady and trade: + self.ib.commissionReportEvent.emit(trade, fill, commissionReport) + trade.commissionReportEvent.emit(trade, fill, commissionReport) + else: + # this is not a live execution and the order was filled + # before this connection started pass def orderBound(self, reqId: int, apiClientId: int, apiOrderId: int): pass def contractDetails(self, reqId: int, contractDetails: ContractDetails): - self._results[reqId].append(contractDetails) + self.response_bus.emit(reqId, contractDetails) bondContractDetails = contractDetails def contractDetailsEnd(self, reqId: int): - self._endReq(reqId) + self.response_bus.emit(reqId, None) def symbolSamples( self, reqId: int, contractDescriptions: list[ContractDescription] ): - self._endReq(reqId, contractDescriptions) + self.response_bus.emit(reqId, contractDescriptions) def marketRule(self, marketRuleId: int, priceIncrements: list[PriceIncrement]): - self._endReq(f"marketRule-{marketRuleId}", priceIncrements) + self.response_bus.emit(f"marketRule-{marketRuleId}", priceIncrements) def marketDataType(self, reqId: int, marketDataId: int): ticker = self.reqId2Ticker.get(reqId) @@ -819,14 +698,12 @@ def realtimeBar( self.ib.barUpdateEvent.emit(bars, True) bars.updateEvent.emit(bars, True) - def historicalData(self, reqId: int, bar: BarData): - results = self._results.get(reqId) - if results is not None: - bar.date = parseIBDatetime(bar.date) # type: ignore - results.append(bar) + def historicalData(self, reqId: int, bars: list[BarData]): + if bars is not None: + self.response_bus.emit(reqId, bars) def historicalDataEnd(self, reqId, _start: str, _end: str): - self._endReq(reqId) + self.response_bus.emit(reqId, None) def historicalDataUpdate(self, reqId: int, bar: BarData): bars = self.reqId2Subscriber.get(reqId) @@ -852,9 +729,9 @@ def historicalDataUpdate(self, reqId: int, bar: BarData): def headTimestamp(self, reqId: int, headTimestamp: str): try: dt = parseIBDatetime(headTimestamp) - self._endReq(reqId, dt) + self.response_bus.emit(reqId, dt) except ValueError as exc: - self._endReq(reqId, exc, False) + self.response_bus.emit(reqId, exc) def historicalTicks(self, reqId: int, ticks: list[HistoricalTick], done: bool): result = self._results.get(reqId) @@ -1453,7 +1330,11 @@ def receiveFA(self, _faDataType: int, faXmlData: str): def currentTime(self, time: int): dt = datetime.fromtimestamp(time, self.defaultTimezone) - self._endReq("currentTime", dt) + self.response_bus.emit("currentTime", dt) + + def currentTimeMili(self, time: int): + dt = datetime.fromtimestamp(time / 1000, self.defaultTimezone) + self.response_bus.emit("currentTimeMili", dt) def tickEFP( self, @@ -1498,14 +1379,23 @@ def familyCodes(self, familyCodes: list[FamilyCode]): pass def error( - self, reqId: int, errorCode: int, errorString: str, advancedOrderRejectJson: str + self, + reqId: int, + errorCode: int, + errorString: str, + advancedOrderRejectJson: str = "", ): + self._logger.debug("IBKR API reqID: %s error: %s", reqId, errorString) # https://interactivebrokers.github.io/tws-api/message_codes.html # https://ibkrcampus.com/campus/ibkr-api-page/twsapi-doc/#api-error-codes - isRequest = reqId in self._futures + isRequest = 0 < reqId <= self.ib.client._reqIdSeq or ( + isinstance(reqId, str) and reqId.startswith("marketRule") + ) + trade = None - # reqId is a local orderId, but is delivered as -1 if this is a non-order-related error + # reqId is a local orderId, but is delivered as -1 if this is a + # non-order-related error if reqId != -1: trade = self.trades.get((self.clientId, reqId)) @@ -1523,7 +1413,8 @@ def error( # Note: error 321 means error validing, but if the message is the result of a MODIFY, the order _is still live_ and we must not delete it. # TODO: investigate if error 321 happens on _new_ order placement with incorrect parameters too, then we should probably delete the order. - # Previously this was included as a Warning condition, but 202 is literally "Order Canceled" error status, so now it is an order-delete error: + # Previously this was included as a Warning condition, but 202 is literally + # "Order Canceled" error status, so now it is an order-delete error: # 202 - Order cancelled - Reason: warningCodes = frozenset({105, 110, 165, 321, 329, 399, 404, 434, 492, 10167}) @@ -1559,16 +1450,28 @@ def error( trade.statusEvent.emit(trade) else: # else, this is a non-trade-related warning message + if isRequest: + # It's a new eventkit-based request. Emit the error on the + # response_bus. The pipeline is responsible for raising it. + if self.ib.RaiseRequestErrors: + error = RequestError(reqId, errorCode, errorString) + self.response_bus.emit(reqId, error) + else: + # a None will be interpreted as an empty result + self._logger.error("is request %s, %s", reqId,msg) + self.response_bus.emit(reqId, None) self._logger.info(msg) else: self._logger.error(msg) if isRequest: - # the request failed + # It's a new eventkit-based request. Emit the error on the + # response_bus. The pipeline is responsible for raising it. if self.ib.RaiseRequestErrors: error = RequestError(reqId, errorCode, errorString) - self._endReq(reqId, error, success=False) + self.response_bus.emit(reqId, error) else: - self._endReq(reqId) + # a None will be interpreted as an empty result + self.response_bus.emit(reqId, None) elif trade: # something is wrong with the order, cancel it if advancedOrderRejectJson: @@ -1577,7 +1480,8 @@ def error( # Errors can mean two things: # - new order is REJECTED # - existing order is server-canceled (DAY orders, margin problems) - # - modification to *existing* order just has an update error, but the order is STILL LIVE + # - modification to *existing* order just has an update error, but the + # order is STILL LIVE if not trade.isDone(): status = trade.orderStatus.status = OrderStatus.Cancelled logEntry = TradeLogEntry(self.lastTime, status, msg, errorCode) diff --git a/pyproject.toml b/pyproject.toml index e22bd8f2..9aa87987 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ maintainers = ["Matt Stancliff "] license = "BSD" readme = "README.md" repository = "https://github.com/ib-api-reloaded/ib_async" +packages = [{include = "ib_async"}] include = ["ib_async/py.typed"] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -26,6 +27,7 @@ python = ">=3.10" aeventkit = "^2.1.0" # aeventkit = { path = "../eventkit", develop = true } nest_asyncio = "*" +protobuf = "*" [tool.poetry.urls] "Bug Tracker" = "https://github.com/ib-api-reloaded/ib_async/issues" @@ -40,6 +42,8 @@ pytest = ">=8.0" pytest-asyncio = ">=0.23" pandas = "^2.2.1" ruff = "^0.11.13" +grpcio-tools = "^1.75.1" +pytest-timeout = "*" [tool.poetry.group.docs] @@ -57,8 +61,15 @@ check_untyped_defs = true [tool.pytest.ini_options] -asyncio_mode="auto" - +testpaths = ["tests"] +log_cli = true +log_date_format = "%Y-%m-%S %H:%M:%S" +minversion = "6.0" +filterwarnings = "ignore" +asyncio_mode = "strict" +asyncio_default_fixture_loop_scope = "function" +asyncio_default_test_loop_scope = "function" +timeout = 10 [build-system] requires = ["poetry-core"] diff --git a/scripts/fix_proto_imports.py b/scripts/fix_proto_imports.py new file mode 100644 index 00000000..c687af7c --- /dev/null +++ b/scripts/fix_proto_imports.py @@ -0,0 +1,48 @@ +""" +Post-generation script for Protobuf Python files. + +This script is designed to be run after generating Python code from .proto +files using the Protocol Buffer compiler (protoc). Its primary purpose is to +fix the import statements within the generated `*_pb2.py` files. + +The `protoc` compiler generates absolute import statements, such as: +`import Contract_pb2 as Contract__pb2` + +These absolute imports can cause issues when the generated files are part of a +Python package. This script converts them into relative imports by prepending +`from . `, like so: +`from . import Contract_pb2 as Contract__pb2` + +This ensures that the generated modules can correctly import their dependencies +within the same package. +""" +import os +import glob + + +def fix_imports(directory: str): + """ + Finds all `*_pb2.py` files in a directory and converts their + Protobuf-related imports to be relative. + + Args: + directory: The path to the directory containing the generated + `*_pb2.py` files. + """ + for filepath in glob.glob(os.path.join(directory, "*_pb2.py")): + with open(filepath, 'r+') as f: + lines = f.readlines() + f.seek(0) + new_lines = [] + for line in lines: + if line.startswith('import') and '_pb2' in line: + new_lines.append('from . ' + line) + else: + new_lines.append(line) + f.writelines(new_lines) + f.truncate() + + +if __name__ == '__main__': + # The target directory where the generated protobuf files are located. + fix_imports('ib_async/protobuf') \ No newline at end of file From fc67a99e1c8929f3aa5bd2f3a54b95db4eeb4639 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:48:05 +0100 Subject: [PATCH 02/48] Refactor ticker handling and improve tick data processing - Added logging for ticker data reception and processing. - Introduced new methods in Ticker class for handling various tick data types (price, size, string, generic, computation). - Updated Wrapper class to emit events for tick data using a bus system. - Removed redundant tick type mappings and replaced them with more structured handling. - Enhanced error handling for malformed tick data. - Improved readability and maintainability of the code by organizing tick data processing logic. --- ib_async/client.py | 61 ++- ib_async/connection.py | 3 +- ib_async/contract.py | 28 +- ib_async/decoder.py | 155 ++++++- ib_async/ib.py | 83 ++-- ib_async/message.py | 7 +- ib_async/objects.py | 258 ++++++++++- ib_async/order.py | 25 +- .../protobuf_converters/account_converters.py | 14 +- .../contract_converters.py | 18 +- .../historical_data_converters.py | 144 +++++- .../market_data_converters.py | 292 ++++++++++++ .../protobuf_converters/trade_converter.py | 5 +- ib_async/ticker.py | 404 +++++++++++++++- ib_async/util.py | 5 +- ib_async/wrapper.py | 430 +++--------------- 16 files changed, 1403 insertions(+), 529 deletions(-) create mode 100644 ib_async/protobuf_converters/market_data_converters.py diff --git a/ib_async/client.py b/ib_async/client.py index 1157b8bb..326738b0 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -72,9 +72,16 @@ ) from .protobuf_converters.historical_data_converters import ( createHeadTimestampRequestProto, + createHistogramDataRequestProto, createHistoricalDataRequestProto, + createHistoricalTicksRequestProto, ) from .protobuf_converters.trade_converter import createExecutionRequestProto +from .protobuf_converters.market_data_converters import ( + cancelMarketDataProto, + createMarketDataRequestProto, + createMarketDataTypeRequestProto, +) from .util import UNSET_DOUBLE, UNSET_INTEGER, dataclassAsTuple, getLoop, run @@ -407,25 +414,23 @@ def reqMktData( regulatorySnapshot, mktDataOptions, ): - fields = [1, 11, reqId, contract] - - if contract.secType == "BAG": - legs = contract.comboLegs or [] - fields += [len(legs)] - for leg in legs: - fields += [leg.conId, leg.ratio, leg.action, leg.exchange] - - dnc = contract.deltaNeutralContract - if dnc: - fields += [True, dnc.conId, dnc.delta, dnc.price] - else: - fields += [False] - - fields += [genericTickList, snapshot, regulatorySnapshot, mktDataOptions] - self.send(*fields) + mktDataRequestProto = createMarketDataRequestProto( + reqId, + contract, + genericTickList, + snapshot, + regulatorySnapshot, + mktDataOptions, + ) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_MKT_DATA), mktDataRequestProto + ) def cancelMktData(self, reqId): - self.send(2, 2, reqId) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.CANCEL_MKT_DATA), + cancelMarketDataProto(reqId), + ) def placeOrder(self, orderId, contract, order): version = self.serverVersion() @@ -1013,8 +1018,8 @@ def cancelHistoricalData(self, reqId): cancelRequest = CancelHistoricalDataProto() cancelRequest.reqId = reqId self.sendProto( - MessageId.to_protobuf(MessageId.OUT.CANCEL_HISTORICAL_DATA), - cancelRequest) + MessageId.to_protobuf(MessageId.OUT.CANCEL_HISTORICAL_DATA), cancelRequest + ) def reqCurrentTime(self): currentTimeRequestProto = CurrentTimeRequestProto() @@ -1099,7 +1104,10 @@ def reqGlobalCancel(self): self.send(58, 1) def reqMarketDataType(self, marketDataType): - self.send(59, 1, marketDataType) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_MARKET_DATA_TYPE), + createMarketDataTypeRequestProto(marketDataType=marketDataType), + ) def reqPositions(self): self.sendProto( @@ -1264,7 +1272,10 @@ def cancelHeadTimeStamp(self, reqId): ) def reqHistogramData(self, tickerId, contract, useRTH, timePeriod): - self.send(88, tickerId, contract, contract.includeExpired, useRTH, timePeriod) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_HISTOGRAM_DATA), + createHistogramDataRequestProto(tickerId, contract, useRTH, timePeriod), + ) def cancelHistogramData(self, tickerId): self.send(89, tickerId) @@ -1299,11 +1310,9 @@ def reqHistoricalTicks( ignoreSize, miscOptions, ): - self.send( - 96, + historicalTicksRequestProto = createHistoricalTicksRequestProto( reqId, contract, - contract.includeExpired, startDateTime, endDateTime, numberOfTicks, @@ -1312,6 +1321,10 @@ def reqHistoricalTicks( ignoreSize, miscOptions, ) + self.sendProto( + MessageId.to_protobuf(MessageId.OUT.REQ_HISTORICAL_TICKS), + historicalTicksRequestProto, + ) def reqTickByTickData(self, reqId, contract, tickType, numberOfTicks, ignoreSize): self.send(97, reqId, contract, tickType, numberOfTicks, ignoreSize) diff --git a/ib_async/connection.py b/ib_async/connection.py index dd991fc7..56c2dd6a 100644 --- a/ib_async/connection.py +++ b/ib_async/connection.py @@ -18,7 +18,8 @@ class Connection(asyncio.Protocol): Is emitted on socket disconnect, with an error message in case of error, or an empty string in case of a normal disconnect. """ - transport:asyncio.Transport|None + + transport: asyncio.Transport | None def __init__(self): self.hasData = Event("hasData") diff --git a/ib_async/contract.py b/ib_async/contract.py index 18a98efe..550e9a5e 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -377,7 +377,12 @@ def __init__( currency = currency or pair[3:] Contract.__init__( - self, secType="CASH", symbol=symbol, exchange=exchange, currency=currency, **kwargs + self, + secType="CASH", + symbol=symbol, + exchange=exchange, + currency=currency, + **kwargs, ) def __repr__(self): @@ -412,7 +417,12 @@ def __init__( currency: Underlying currency. """ Contract.__init__( - self, secType="IND", symbol=symbol, exchange=exchange, currency=currency, **kwargs + self, + secType="IND", + symbol=symbol, + exchange=exchange, + currency=currency, + **kwargs, ) @@ -429,7 +439,12 @@ def __init__( currency: Underlying currency. """ Contract.__init__( - self, secType="CFD", symbol=symbol, exchange=exchange, currency=currency, **kwargs + self, + secType="CFD", + symbol=symbol, + exchange=exchange, + currency=currency, + **kwargs, ) @@ -446,7 +461,12 @@ def __init__( currency: Underlying currency. """ Contract.__init__( - self, secType="CMDTY", symbol=symbol, exchange=exchange, currency=currency, **kwargs + self, + secType="CMDTY", + symbol=symbol, + exchange=exchange, + currency=currency, + **kwargs, ) diff --git a/ib_async/decoder.py b/ib_async/decoder.py index f0011a59..4c83a241 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -27,6 +27,7 @@ TagValue, TickAttribBidAsk, TickAttribLast, + TickType, ) from .order import OrderStatus from .protobuf.AccountDataEnd_pb2 import AccountDataEnd as AccountDataEndProto @@ -61,10 +62,19 @@ ExecutionDetailsEnd as ExecutionDetailsEndProto, ) from .protobuf.HeadTimestamp_pb2 import HeadTimestamp as HeadTimestampProto +from .protobuf.HistogramData_pb2 import HistogramData as HistogramDataProto from .protobuf.HistoricalData_pb2 import HistoricalData as HistoricalDataProto from .protobuf.HistoricalDataEnd_pb2 import HistoricalDataEnd as HistoricalDataEndProto +from .protobuf.HistoricalTicks_pb2 import HistoricalTicks as HistoricalTicksProto +from .protobuf.HistoricalTicksBidAsk_pb2 import ( + HistoricalTicksBidAsk as HistoricalTicksBidAskProto, +) +from .protobuf.HistoricalTicksLast_pb2 import ( + HistoricalTicksLast as HistoricalTicksLastProto, +) from .protobuf.ManagedAccounts_pb2 import ManagedAccounts as ManagedAccountsProto from .protobuf.MarketRule_pb2 import MarketRule as MarketRuleProto +from .protobuf.MarketDataType_pb2 import MarketDataType as MarketDataTypeProto from .protobuf.NextValidId_pb2 import NextValidId as NextValidIdProto from .protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto from .protobuf.OpenOrdersEnd_pb2 import OpenOrdersEnd as OpenOrderEndProto @@ -79,6 +89,15 @@ SecDefOptParameterEnd as SecDefOptParameterEndProto, ) from .protobuf.SymbolSamples_pb2 import SymbolSamples as SymbolSamplesProto +from .protobuf.TickGeneric_pb2 import TickGeneric as TickGenericProto +from .protobuf.TickOptionComputation_pb2 import ( + TickOptionComputation as TickOptionComputationProto, +) +from .protobuf.TickPrice_pb2 import TickPrice as TickPriceProto +from .protobuf.TickSize_pb2 import TickSize as TickSizeProto +from .protobuf.TickSnapshotEnd_pb2 import TickSnapshotEnd as TickSnapshotEndProto +from .protobuf.TickString_pb2 import TickString as TickStringProto +from .protobuf.TickReqParams_pb2 import TickReqParams as TickReqParamsProto from .protobuf_converters.account_converters import ( createAccountSummary, createAccountValue, @@ -91,18 +110,30 @@ createContractDetails, createOptionChain, ) +from .protobuf_converters.historical_data_converters import ( + createBarDataList, + decodeHistogramDataEntry, + decodeHistoricalTick, + decodeHistoricalTickBidAsk, + decodeHistoricalTickLast, +) +from .protobuf_converters.market_data_converters import ( + createTickOptionComputation, + createTickParams, + createTickPriceData, + createTickSizeData, + createTickGenericData, + createTickStringData, +) from .protobuf_converters.trade_converter import ( createCommissionReport, createContract, - createExecution, createFill, createOrder, - createOrderState, createOrderStatus, createTradeFromOpenOrder, ) -from .protobuf_converters.historical_data_converters import createBarDataList -from .util import UNSET_DOUBLE, ZoneInfo, parseIBDatetime +from .util import NO_VALID_ID, UNSET_DOUBLE, UNSET_INTEGER, ZoneInfo, parseIBDatetime from .wrapper import Wrapper @@ -180,6 +211,7 @@ class Decoder: AccountUpdateTimeProto, "updateAccountTimeProto", ), + MessageId.IN.MARKET_DATA_TYPE: (MarketDataTypeProto, "marketDataTypeProto"), MessageId.IN.COMMISSION_AND_FEES_REPORT: ( CommissionReportProto, "commissionReportProto", @@ -189,9 +221,32 @@ class Decoder: CurrentTimeInMillisProto, "currentTimeMiliProto", ), - MessageId.IN.HEAD_TIMESTAMP: (HeadTimestampProto,"headTimestampProto"), + MessageId.IN.HEAD_TIMESTAMP: (HeadTimestampProto, "headTimestampProto"), MessageId.IN.HISTORICAL_DATA: (HistoricalDataProto, "historicalDataProto"), - MessageId.IN.HISTORICAL_DATA_END: (HistoricalDataEndProto, "historicalDataProtoEnd"), + MessageId.IN.HISTORICAL_DATA_END: ( + HistoricalDataEndProto, + "historicalDataProtoEnd", + ), + MessageId.IN.HISTORICAL_TICKS: (HistoricalTicksProto, "historicalTicksProto"), + MessageId.IN.HISTORICAL_TICKS_BID_ASK: ( + HistoricalTicksBidAskProto, + "historicalTicksBidAskProto", + ), + MessageId.IN.HISTORICAL_TICKS_LAST: ( + HistoricalTicksLastProto, + "historicalTicksLastProto", + ), + MessageId.IN.HISTOGRAM_DATA: (HistogramDataProto, "histogramDataProto"), + MessageId.IN.TICK_REQ_PARAMS: (TickReqParamsProto, "tickReqParamsProto"), + MessageId.IN.TICK_PRICE: (TickPriceProto, "tickPriceProto"), + MessageId.IN.TICK_SIZE: (TickSizeProto, "tickSizeProto"), + MessageId.IN.TICK_GENERIC: (TickGenericProto, "tickGenericProto"), + MessageId.IN.TICK_STRING: (TickStringProto, "tickStringProto"), + MessageId.IN.TICK_OPTION_COMPUTATION: ( + TickOptionComputationProto, + "tickOptionComputationProto", + ), + MessageId.IN.TICK_SNAPSHOT_END: (TickSnapshotEndProto, "tickSnapshotEndProto"), } def __init__(self, wrapper: Wrapper, serverVersion: int): @@ -341,27 +396,93 @@ def currentTimeProto(self, msg: CurrentTimeProto): def currentTimeMiliProto(self, msg: CurrentTimeInMillisProto): self.wrapper.currentTimeMili(msg.currentTimeInMillis) - + def headTimestampProto(self, msg: HeadTimestampProto): self.wrapper.headTimestamp(msg.reqId, msg.headTimestamp) - def historicalDataProto(self, msg: HistoricalDataProto): bar_data = createBarDataList(msg.historicalDataBars) self.wrapper.historicalData(msg.reqId, bar_data) - + def historicalDataProtoEnd(self, msg: HistoricalDataEndProto): self.wrapper.historicalDataEnd(msg.reqId, msg.startDateStr, msg.endDateStr) - ##################### legacy methods ########################################## - - def priceSizeTick(self, fields): - _, _, reqId, tickType, price, size, _ = fields + def historicalTicksProto(self, msg: HistoricalTicksProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + isDone = msg.isDone if msg.HasField("isDone") else False + historicalTicks = [] + if msg.historicalTicks: + for historicalTickProto in msg.historicalTicks: + historicalTick = decodeHistoricalTick( + historicalTickProto, self.wrapper.defaults.timezone + ) + historicalTicks.append(historicalTick) + self.wrapper.historicalTicks(reqId, historicalTicks, isDone) + + def historicalTicksBidAskProto(self, msg: HistoricalTicksBidAskProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + isDone = msg.isDone if msg.HasField("isDone") else False + historicalTicksBidAsk: list[HistoricalTickBidAsk] = [] + if msg.historicalTicksBidAsk: + for historicalTickProto in msg.historicalTicksBidAsk: + historicalTickBidAsk = decodeHistoricalTickBidAsk( + historicalTickProto, self.wrapper.defaults.timezone + ) + historicalTicksBidAsk.append(historicalTickBidAsk) + self.wrapper.historicalTicksBidAsk(reqId, historicalTicksBidAsk, isDone) + + def historicalTicksLastProto(self, msg: HistoricalTicksLastProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + isDone = msg.isDone if msg.HasField("isDone") else False + historicalTicksLast: list[HistoricalTickLast] = [] + if msg.historicalTicksLast: + for historicalTickProto in msg.historicalTicksLast: + historicalTickLast = decodeHistoricalTickLast( + historicalTickProto, self.wrapper.defaults.timezone + ) + historicalTicksLast.append(historicalTickLast) + self.wrapper.historicalTicksLast(reqId, historicalTicksLast, isDone) + + def histogramDataProto(self, msg: HistogramDataProto): + histogram: list[HistogramData] = [] + if msg.histogramDataEntries: + for histogramDataEntryProto in msg.histogramDataEntries: + histogramEntry = decodeHistogramDataEntry(histogramDataEntryProto) + histogram.append(histogramEntry) + self.wrapper.histogramData(msg.reqId, histogram) + + def marketDataTypeProto(self, msg: MarketDataTypeProto): + self.wrapper.marketDataType(msg.reqId, msg.marketDataType) + + def tickReqParamsProto(self, msg: TickReqParamsProto): + tickParams = createTickParams(msg) + self.wrapper.tickReqParams(tickParams.reqId, tickParams) + + def tickPriceProto(self, msg: TickPriceProto): + tickPrice, tickSize = createTickPriceData(msg) + self.wrapper.priceSizeTick(tickPrice.reqId, tickPrice) + if tickSize.tickType != TickType.NOT_SET: + self.wrapper.tickSize(tickSize.reqId, tickSize) + + def tickSizeProto(self, msg: TickSizeProto): + tickSize = createTickSizeData(msg) + self.wrapper.tickSize(tickSize.reqId, tickSize) + + def tickGenericProto(self, msg: TickGenericProto): + tickGeneric = createTickGenericData(msg) + self.wrapper.tickGeneric(tickGeneric.reqId, tickGeneric) + + def tickStringProto(self, msg: TickStringProto): + tickString = createTickStringData(msg) + self.wrapper.tickString(tickString.reqId, tickString) + + def tickOptionComputationProto(self, msg: TickOptionComputationProto): + tick_computation = createTickOptionComputation(msg) + self.wrapper.tickOptionComputation( + tick_computation.reqId, tick_computation + ) - if price: - self.wrapper.priceSizeTick( - int(reqId), int(tickType), float(price), float(size or 0) - ) + ##################### legacy methods ########################################## def bondContractDetails(self, fields): cd = ContractDetails() diff --git a/ib_async/ib.py b/ib_async/ib.py index 42046b8e..ea5896b7 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -1,37 +1,30 @@ """High-level interface to Interactive Brokers.""" -from asyncio import TimeoutError +import asyncio import copy import datetime -import asyncio import logging import time -from enum import auto, Flag +from asyncio import TimeoutError +from enum import Flag, auto from typing import ( Any, + AsyncIterator, Awaitable, Callable, - cast, Iterator, List, Optional, TypeVar, Union, - AsyncIterator, + cast, ) -_T = TypeVar("_T") - from eventkit import Event import ib_async.util as util -from ib_async.wrapper import Wrapper, RequestError from ib_async.client import Client from ib_async.contract import Contract, ContractDescription, ContractDetails -from ib_async.protobuf.ContractDataRequest_pb2 import ( - ContractDataRequest as ContractDataRequestProto, -) - from ib_async.objects import ( AccountValue, BarDataList, @@ -73,7 +66,9 @@ Trade, ) from ib_async.ticker import Ticker -from ib_async.wrapper import Wrapper +from ib_async.wrapper import RequestError, Wrapper + +_T = TypeVar("_T") class StartupFetch(Flag): @@ -414,7 +409,7 @@ def disconnect(self) -> str | None: f"in {stats.numMsgSent} messages, " f"{util.formatSI(stats.numBytesRecv)}B received " f"in {stats.numMsgRecv} messages, " - f"session time {util.formatSI(stats.duration)}s." + f"session time {datetime.timedelta(seconds=stats.duration)}." ) self._logger.info(status) @@ -661,7 +656,7 @@ def executions(self) -> list[Execution]: """List of all executions from this session.""" return list(fill.execution for fill in self.wrapper.fills.values()) - def ticker(self, contract: Contract) -> Optional[Ticker]: + def ticker(self, contract: Contract) -> Ticker | None: """ Get ticker of the given contract. It must have been requested before with reqMktData with the same contract object. The ticker may not be @@ -670,11 +665,14 @@ def ticker(self, contract: Contract) -> Optional[Ticker]: Args: contract: Contract to get ticker for. """ - return self.wrapper.tickers.get(hash(contract)) + for ticker in self.wrapper.reqId2Ticker.values(): + if hash(ticker.contract) == hash(contract): + return ticker + return None def tickers(self) -> list[Ticker]: """Get a list of all tickers.""" - return list(self.wrapper.tickers.values()) + return list(self.wrapper.reqId2Ticker.values()) def pendingTickers(self) -> list[Ticker]: """Get a list of all tickers that have pending ticks or domTicks.""" @@ -2379,16 +2377,16 @@ def reqCompletedOrdersAsync(self, apiOnly: bool) -> Awaitable[list[Trade]]: ) def reqExecutionsAsync( - self, execFilter: Optional[ExecutionFilter] = None + self, execFilter: ExecutionFilter | None = None ) -> Awaitable[list[Fill]]: """Request a list of fills.""" reqId = self.client.getReqId() self.client.reqExecutions(reqId, execFilter or ExecutionFilter()) fills = ( self.wrapper.response_bus.filter( - lambda rId, e: rId == reqId and e is not None + lambda rId, e: rId == reqId ) - .takewhile(lambda rId, data: data is not None) + .takewhile(lambda r, data: data is not None) .pluck(1) .map(self._raise_if_error) .list() @@ -2569,7 +2567,6 @@ def reqHistoricalTicksAsync( miscOptions: list[TagValue] = [], ) -> Awaitable[List]: reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId, contract) start = util.formatIBDatetime(startDateTime) end = util.formatIBDatetime(endDateTime) self.client.reqHistoricalTicks( @@ -2583,7 +2580,12 @@ def reqHistoricalTicksAsync( ignoreSize, miscOptions, ) - return future + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .takewhile(lambda rId, data: data is not None) + .pluck(1) + .map(self._raise_if_error) + ) async def reqHeadTimeStampAsync( self, contract: Contract, whatToShow: str, useRTH: bool, formatDate: int = 1 @@ -2618,10 +2620,13 @@ def reqHistogramDataAsync( self, contract: Contract, useRTH: bool, period: str ) -> Awaitable[list[HistogramData]]: reqId = self.client.getReqId() - - future = self.wrapper.startReq(reqId, contract) self.client.reqHistogramData(reqId, contract, useRTH, period) - return future + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .takewhile(lambda rId, data: data is not None) + .pluck(1) + .map(self._raise_if_error) + ) def reqFundamentalDataAsync( self, @@ -2720,34 +2725,6 @@ def reqSecDefOptParamsAsync( .list() ) - def reqSecDefOptParams( - self, - underlyingSymbol: str, - futFopExchange: str, - underlyingSecType: str, - underlyingConId: int, - ) -> list[OptionChain]: - """ - Get the option chain. - - This method is blocking. - - https://interactivebrokers.github.io/tws-api/options.html - - Args: - underlyingSymbol: Symbol of underlier contract. - futFopExchange: Exchange (only for ``FuturesOption``, otherwise - leave blank). - underlyingSecType: The type of the underlying security, like - 'STK' or 'FUT'. - underlyingConId: conId of the underlying contract. - """ - return self._run( - self.reqSecDefOptParamsAsync( - underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId - ) - ) - def reqNewsProvidersAsync(self) -> Awaitable[list[NewsProvider]]: future = self.wrapper.startReq("newsProviders") self.client.reqNewsProviders() diff --git a/ib_async/message.py b/ib_async/message.py index 9c586745..ee910bfa 100644 --- a/ib_async/message.py +++ b/ib_async/message.py @@ -2,14 +2,13 @@ class MessageId: """ Encapsulates TWS API message ID constants and protocol-related logic. """ - + from dataclasses import dataclass - PROTOBUF_MSG_ID = 200 """Protobuf message ID offset""" - @dataclass(frozen=True,slots=True) + @dataclass(frozen=True, slots=True) class IN: """Incoming Message IDs (TWS -> Client)""" @@ -98,7 +97,7 @@ class IN: HISTORICAL_DATA_END = 108 CURRENT_TIME_IN_MILLIS = 109 - @dataclass(frozen=True,slots=True) + @dataclass(frozen=True, slots=True) class OUT: """Outgoing Message IDs (Client -> TWS)""" diff --git a/ib_async/objects.py b/ib_async/objects.py index e8cbfcb9..391ed987 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -5,7 +5,7 @@ from dataclasses import dataclass, field from datetime import date as date_, datetime, timezone, tzinfo from enum import Enum -from typing import Any, List, NamedTuple, Optional, Union +from typing import Any, List, NamedTuple, Optional, TypeAlias, Union from eventkit import Event @@ -14,6 +14,7 @@ nan = float("nan") + class OptionExerciseType(Enum): NoneItem = (-1, "None") Exercise = (1, "Exercise") @@ -25,6 +26,7 @@ class OptionExerciseType(Enum): Netting = (103, "Netting") AutoexerciseTrading = (200, "AutoexerciseTrading") + @dataclass class ScannerSubscription: numberOfRows: int = -1 @@ -81,8 +83,10 @@ class Execution: modelCode: str = "" lastLiquidity: int = 0 pendingPriceRevision: bool = False - submitter:str = "" - optExerciseOrLapseType:OptionExerciseType = OptionExerciseType.NoneItem + submitter: str = "" + optExerciseOrLapseType: OptionExerciseType = field( + default=OptionExerciseType.NoneItem + ) @dataclass @@ -104,8 +108,8 @@ class ExecutionFilter: secType: str = "" exchange: str = "" side: str = "" - lastNDays:int = UNSET_INTEGER - specificDates:list[int] = field(default_factory=list) + lastNDays: int = UNSET_INTEGER + specificDates: list[int] = field(default_factory=list) @dataclass @@ -133,26 +137,246 @@ class RealTimeBar: count: int = 0 -@dataclass +class TickType(Enum): + BID_SIZE = 0 + BID = 1 + ASK = 2 + ASK_SIZE = 3 + LAST = 4 + LAST_SIZE = 5 + HIGH = 6 + LOW = 7 + VOLUME = 8 + CLOSE = 9 + BID_OPTION_COMPUTATION = 10 + ASK_OPTION_COMPUTATION = 11 + LAST_OPTION_COMPUTATION = 12 + MODEL_OPTION = 13 + OPEN = 14 + LOW_13_WEEK = 15 + HIGH_13_WEEK = 16 + LOW_26_WEEK = 17 + HIGH_26_WEEK = 18 + LOW_52_WEEK = 19 + HIGH_52_WEEK = 20 + AVG_VOLUME = 21 + OPEN_INTEREST = 22 + OPTION_HISTORICAL_VOL = 23 + OPTION_IMPLIED_VOL = 24 + OPTION_BID_EXCH = 25 + OPTION_ASK_EXCH = 26 + OPTION_CALL_OPEN_INTEREST = 27 + OPTION_PUT_OPEN_INTEREST = 28 + OPTION_CALL_VOLUME = 29 + OPTION_PUT_VOLUME = 30 + INDEX_FUTURE_PREMIUM = 31 + BID_EXCH = 32 + ASK_EXCH = 33 + AUCTION_VOLUME = 34 + AUCTION_PRICE = 35 + AUCTION_IMBALANCE = 36 + MARK_PRICE = 37 + BID_EFP_COMPUTATION = 38 + ASK_EFP_COMPUTATION = 39 + LAST_EFP_COMPUTATION = 40 + OPEN_EFP_COMPUTATION = 41 + HIGH_EFP_COMPUTATION = 42 + LOW_EFP_COMPUTATION = 43 + CLOSE_EFP_COMPUTATION = 44 + LAST_TIMESTAMP = 45 + SHORTABLE = 46 + FUNDAMENTAL_RATIOS = 47 + RT_VOLUME = 48 + HALTED = 49 + BID_YIELD = 50 + ASK_YIELD = 51 + LAST_YIELD = 52 + CUST_OPTION_COMPUTATION = 53 + TRADE_COUNT = 54 + TRADE_RATE = 55 + VOLUME_RATE = 56 + LAST_RTH_TRADE = 57 + RT_HISTORICAL_VOL = 58 + IB_DIVIDENDS = 59 + BOND_FACTOR_MULTIPLIER = 60 + REGULATORY_IMBALANCE = 61 + NEWS_TICK = 62 + SHORT_TERM_VOLUME_3_MIN = 63 + SHORT_TERM_VOLUME_5_MIN = 64 + SHORT_TERM_VOLUME_10_MIN = 65 + DELAYED_BID = 66 + DELAYED_ASK = 67 + DELAYED_LAST = 68 + DELAYED_BID_SIZE = 69 + DELAYED_ASK_SIZE = 70 + DELAYED_LAST_SIZE = 71 + DELAYED_HIGH = 72 + DELAYED_LOW = 73 + DELAYED_VOLUME = 74 + DELAYED_CLOSE = 75 + DELAYED_OPEN = 76 + RT_TRD_VOLUME = 77 + CREDITMAN_MARK_PRICE = 78 + CREDITMAN_SLOW_MARK_PRICE = 79 + DELAYED_BID_OPTION = 80 + DELAYED_ASK_OPTION = 81 + DELAYED_LAST_OPTION = 82 + DELAYED_MODEL_OPTION = 83 + LAST_EXCH = 84 + LAST_REG_TIME = 85 + FUTURES_OPEN_INTEREST = 86 + AVG_OPT_VOLUME = 87 + DELAYED_LAST_TIMESTAMP = 88 + SHORTABLE_SHARES = 89 + DELAYED_HALTED = 90 + REUTERS_2_MUTUAL_FUNDS = 91 + ETF_NAV_CLOSE = 92 + ETF_NAV_PRIOR_CLOSE = 93 + ETF_NAV_BID = 94 + ETF_NAV_ASK = 95 + ETF_NAV_LAST = 96 + ETF_FROZEN_NAV_LAST = 97 + ETF_NAV_HIGH = 98 + ETF_NAV_LOW = 99 + SOCIAL_MARKET_ANALYTICS = 100 + ESTIMATED_IPO_MIDPOINT = 101 + FINAL_IPO_LAST = 102 + DELAYED_YIELD_BID = 103 + DELAYED_YIELD_ASK = 104 + NOT_SET = 105 + + +@dataclass(slots=True, frozen=True) +class TickParams: + reqId: int + minTick: float + bboExchange: str + snapshotPermissions: int + + +@dataclass(slots=True, frozen=True) +class TickData: + """For Ticker.ticks[TickData]""" + + time: datetime + tickType: TickType + price: float + size: float + + +@dataclass(slots=True) class TickAttrib: canAutoExecute: bool = False pastLimit: bool = False preOpen: bool = False -@dataclass +@dataclass(slots=True) class TickAttribBidAsk: bidPastLow: bool = False askPastHigh: bool = False -@dataclass +@dataclass(slots=True) class TickAttribLast: pastLimit: bool = False unreported: bool = False -@dataclass +@dataclass(frozen=True) +class TickPriceData: + """Data from a TickPriceProto message.""" + + reqId: int + tickType: TickType + price: float + size: float + attribs: TickAttrib + + +@dataclass(frozen=True) +class TickSizeData: + """Data from a TickSizeProto message.""" + + reqId: int + tickType: TickType + size: float + + +@dataclass(frozen=True) +class TickStringData: + """Data from a TickStringProto message.""" + + reqId: int + tickType: TickType + value: str + + +@dataclass(frozen=True) +class TickGenericData: + """Data from a generic tick message.""" + + reqId: int + tickType: TickType + value: float + +@dataclass(frozen=True) +class TickComputationData: + """Data from a TickComputationProto message.""" + + reqId: int + tickType: TickType + computation: OptionComputation + +@dataclass(frozen=True) +class TickByTickAllLastData: + """Data from a TickByTickAllLastProto message.""" + + reqId: int + tickType: int + time: int + price: float + size: int + tickAttribLast: TickAttribLast + exchange: str + specialConditions: str + + +@dataclass(frozen=True) +class TickByTickBidAskData: + """Data from a TickByTickBidAskProto message.""" + + reqId: int + time: int + bidPrice: float + askPrice: float + bidSize: int + askSize: int + tickAttribBidAsk: TickAttribBidAsk + + +@dataclass(frozen=True) +class TickByTickMidPointData: + """Data from a TickByTickMidPointProto message.""" + + reqId: int + time: int + midPoint: float + + +TickDataType: TypeAlias = ( + TickPriceData + | TickSizeData + | TickStringData + | TickGenericData + | TickByTickAllLastData + | TickByTickBidAskData + | TickByTickMidPointData + | TickComputationData +) + + +@dataclass(slots=True) class HistogramData: price: float = 0.0 count: int = 0 @@ -237,20 +461,15 @@ class AccountValue(NamedTuple): modelCode: str -class TickData(NamedTuple): - time: datetime - tickType: int - price: float - size: float - - -class HistoricalTick(NamedTuple): +@dataclass(slots=True) +class HistoricalTick: time: datetime price: float size: float -class HistoricalTickBidAsk(NamedTuple): +@dataclass(slots=True) +class HistoricalTickBidAsk: time: datetime tickAttribBidAsk: TickAttribBidAsk priceBid: float @@ -259,7 +478,8 @@ class HistoricalTickBidAsk(NamedTuple): sizeAsk: float -class HistoricalTickLast(NamedTuple): +@dataclass(slots=True) +class HistoricalTickLast: time: datetime tickAttribLast: TickAttribLast price: float diff --git a/ib_async/order.py b/ib_async/order.py index ed11426e..80e516fa 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -162,13 +162,12 @@ class Order: competeAgainstBestOffset: float | Decimal = UNSET_DOUBLE midOffsetAtWhole: float | Decimal = UNSET_DOUBLE midOffsetAtHalf: float | Decimal = UNSET_DOUBLE - customerAccount:str = "" - professionalCustomer:bool = False - bondAccruedInterest:str = "" - includeOvernight:bool = False - manualOrderIndicator:int = UNSET_INTEGER - submitter:str = "" - + customerAccount: str = "" + professionalCustomer: bool = False + bondAccruedInterest: str = "" + includeOvernight: bool = False + manualOrderIndicator: int = UNSET_INTEGER + submitter: str = "" def __repr__(self): attrs = dataclassNonDefaults(self) @@ -479,7 +478,6 @@ def remaining(self) -> float: return float(self.order.totalQuantity) - self.filled() - class BracketOrder(NamedTuple): parent: Order takeProfit: Order @@ -564,12 +562,13 @@ class PercentChangeCondition(OrderCondition): conId: int = 0 exch: str = "" + @dataclass class OrderAllocation: account = "" - position:Decimal = UNSET_DECIMAL - positionDesired:Decimal = UNSET_DECIMAL - positionAfter:Decimal = UNSET_DECIMAL - desiredAllocQty:Decimal = UNSET_DECIMAL - allowedAllocQty:Decimal = UNSET_DECIMAL + position: Decimal = UNSET_DECIMAL + positionDesired: Decimal = UNSET_DECIMAL + positionAfter: Decimal = UNSET_DECIMAL + desiredAllocQty: Decimal = UNSET_DECIMAL + allowedAllocQty: Decimal = UNSET_DECIMAL isMonetary = False diff --git a/ib_async/protobuf_converters/account_converters.py b/ib_async/protobuf_converters/account_converters.py index 530aab1a..42285cc8 100644 --- a/ib_async/protobuf_converters/account_converters.py +++ b/ib_async/protobuf_converters/account_converters.py @@ -1,4 +1,6 @@ -# ib_async/protobuf_converters/account_converters.py +""" +Account data protobuf converters. +""" from ..objects import AccountValue, PortfolioItem, Position from ..protobuf.AccountDataRequest_pb2 import ( @@ -45,7 +47,10 @@ def createAccountDataRequestProto( accountDataRequestProto.acctCode = acctCode return accountDataRequestProto -def createAccountMultiRequestProto(reqId: int, account: str, modelCode: str, ledgerAndNLV: bool) -> AccountUpdatesMultiRequestProto: + +def createAccountMultiRequestProto( + reqId: int, account: str, modelCode: str, ledgerAndNLV: bool +) -> AccountUpdatesMultiRequestProto: """ Creates an AccountUpdatesMultiRequest protobuf message. """ @@ -59,13 +64,16 @@ def createAccountMultiRequestProto(reqId: int, account: str, modelCode: str, led accountUpdatesMultiRequestProto.ledgerAndNLV = ledgerAndNLV return accountUpdatesMultiRequestProto + def createCancelAccMultiRequestProto(reqId: int) -> CancelAccountUpdatesMultiProto: cancelAccountUpdatesMultiProto = CancelAccountUpdatesMultiProto() cancelAccountUpdatesMultiProto.reqId = reqId return cancelAccountUpdatesMultiProto -def createAccountValueFromUpdateMulti(accountValueProto: AccountUpdateMultiProto) -> AccountValue: +def createAccountValueFromUpdateMulti( + accountValueProto: AccountUpdateMultiProto, +) -> AccountValue: """ Converts an AccountUpdateMulti protobuf message to an ib_async AccountValue object. """ diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py index 17cd41c8..016acded 100644 --- a/ib_async/protobuf_converters/contract_converters.py +++ b/ib_async/protobuf_converters/contract_converters.py @@ -1,7 +1,5 @@ """Contract converters""" -from decimal import Decimal - from ..contract import ( ComboLeg, Contract, @@ -12,10 +10,7 @@ FundDistributionPolicyIndicator, ) from ..objects import IneligibilityReason, OptionChain -from ..order import Order, OrderComboLeg -from ..protobuf.AccountDataRequest_pb2 import ( - AccountDataRequest as AccountDataRequestProto, -) +from ..order import Order from ..protobuf.ComboLeg_pb2 import ComboLeg as ComboLegProto from ..protobuf.Contract_pb2 import Contract as ContractProto from ..protobuf.ContractData_pb2 import ContractData as ContractDataProto @@ -26,9 +21,6 @@ from ..protobuf.DeltaNeutralContract_pb2 import ( DeltaNeutralContract as DeltaNeutralContractProto, ) -from ..protobuf.IneligibilityReason_pb2 import ( - IneligibilityReason as IneligibilityReasonProto, -) from ..protobuf.MarketRuleRequest_pb2 import MarketRuleRequest as MarketRuleRequestProto from ..protobuf.MatchingSymbolsRequest_pb2 import ( MatchingSymbolsRequest as MatchingSymbolsRequestProto, @@ -178,7 +170,7 @@ def createComboLegs(contractProto: ContractProto) -> list[ComboLeg]: if comboLegProto.HasField("openClose"): comboLeg.openClose = comboLegProto.openClose if comboLegProto.HasField("shortSalesSlot"): - comboLeg.shortSalesSlot = comboLegProto.shortSalesSlot + comboLeg.shortSaleSlot = comboLegProto.shortSalesSlot if comboLegProto.HasField("designatedLocation"): comboLeg.designatedLocation = comboLegProto.designatedLocation if comboLegProto.HasField("exemptCode"): @@ -188,8 +180,10 @@ def createComboLegs(contractProto: ContractProto) -> list[ComboLeg]: return comboLegs -def createDeltaNeutralContract(contractProto: ContractProto) -> DeltaNeutralContract: - deltaNeutralContract = DeltaNeutralContract() +def createDeltaNeutralContract( + contractProto: ContractProto, +) -> DeltaNeutralContract | None: + deltaNeutralContract = None if contractProto.HasField("deltaNeutralContract"): deltaNeutralContractProto = DeltaNeutralContractProto() deltaNeutralContractProto.CopyFrom(contractProto.deltaNeutralContract) diff --git a/ib_async/protobuf_converters/historical_data_converters.py b/ib_async/protobuf_converters/historical_data_converters.py index 6fec6516..3d383699 100644 --- a/ib_async/protobuf_converters/historical_data_converters.py +++ b/ib_async/protobuf_converters/historical_data_converters.py @@ -1,6 +1,15 @@ """Historical data protobuf converters""" -from ib_async.objects import BarData +from datetime import datetime, tzinfo +from ..objects import ( + BarData, + HistoricalTick, + HistoricalTickLast, + HistoricalTickBidAsk, + TickAttribBidAsk, + TickAttribLast, + HistogramData, +) from ..util import isValidIntValue, parseIBDatetime from ..contract import Contract, TagValue @@ -13,6 +22,25 @@ from ..protobuf.HistoricalDataBar_pb2 import ( HistoricalDataBar as HistoricalDataBarProto, ) +from ..protobuf.HistoricalTicksRequest_pb2 import ( + HistoricalTicksRequest as HistoricalTicksRequestProto, +) +from ..protobuf.HistoricalTick_pb2 import HistoricalTick as HistoricalTickProto +from ..protobuf.HistoricalTickBidAsk_pb2 import ( + HistoricalTickBidAsk as HistoricalTickBidAskProto, +) +from ..protobuf.HistoricalTickLast_pb2 import ( + HistoricalTickLast as HistoricalTickLastProto, +) +from ..protobuf.HistogramDataRequest_pb2 import ( + HistogramDataRequest as HistogramDataRequestProto, +) +from ..protobuf.HistogramDataEntry_pb2 import ( + HistogramDataEntry as HistogramDataEntryProto, +) +from ..protobuf.TickAttribBidAsk_pb2 import TickAttribBidAsk as TickAttribBidAskProto +from ..protobuf.TickAttribLast_pb2 import TickAttribLast as TickAttribLastProto + from .contract_converters import createContractProto @@ -100,3 +128,117 @@ def createBarDataList( bar.barCount = barProto.barCount bars.append(bar) return bars + + +def createHistoricalTicksRequestProto( + reqId: int, + contract: Contract, + startDateTime: str, + endDateTime: str, + numberOfTicks: int, + whatToShow: str, + useRTH: bool, + ignoreSize: bool, + miscOptionsList: list[TagValue], +) -> HistoricalTicksRequestProto: + historicalTicksRequestProto = HistoricalTicksRequestProto() + if isValidIntValue(reqId): + historicalTicksRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + if contractProto is not None: + historicalTicksRequestProto.contract.CopyFrom(contractProto) + if startDateTime: + historicalTicksRequestProto.startDateTime = startDateTime + if endDateTime: + historicalTicksRequestProto.endDateTime = endDateTime + if isValidIntValue(numberOfTicks): + historicalTicksRequestProto.numberOfTicks = numberOfTicks + if whatToShow: + historicalTicksRequestProto.whatToShow = whatToShow + if useRTH: + historicalTicksRequestProto.useRTH = useRTH + if ignoreSize: + historicalTicksRequestProto.ignoreSize = ignoreSize + fillTagValueList(miscOptionsList, historicalTicksRequestProto.miscOptions) + return historicalTicksRequestProto + + +def decodeHistoricalTick( + historicalTickProto: HistoricalTickProto, tz: tzinfo +) -> HistoricalTick: + time = datetime.fromtimestamp(historicalTickProto.time, tz) + price = historicalTickProto.price + size = float(historicalTickProto.size) + historicalTick = HistoricalTick(time, price, size) + return historicalTick + + +def decodeHistoricalTickBidAsk( + historicalTickBidAskProto: HistoricalTickBidAskProto, tz: tzinfo +) -> HistoricalTickBidAsk: + time = datetime.fromtimestamp(historicalTickBidAskProto.time, tz) + + tickAttribBidAskProto = historicalTickBidAskProto.tickAttribBidAsk + bidPastLow = tickAttribBidAskProto.bidPastLow + askPastHigh = tickAttribBidAskProto.askPastHigh + tickAttribBidAsk = TickAttribBidAsk(bidPastLow, askPastHigh) + + priceBid = historicalTickBidAskProto.priceBid + priceAsk = historicalTickBidAskProto.priceAsk + sizeBid = float(historicalTickBidAskProto.sizeBid) + sizeAsk = float(historicalTickBidAskProto.sizeAsk) + + historicalTickBidAsk = HistoricalTickBidAsk( + time, tickAttribBidAsk, priceBid, priceAsk, sizeBid, sizeAsk + ) + + return historicalTickBidAsk + + +def decodeHistoricalTickLast( + historicalTickLastProto: HistoricalTickLastProto, tz: tzinfo +) -> HistoricalTickLast: + time = datetime.fromtimestamp(historicalTickLastProto.time, tz) + + tickAttribLastProto = historicalTickLastProto.tickAttribLast + pastLimit = tickAttribLastProto.pastLimit + unreported = tickAttribLastProto.unreported + tickAttribLast = TickAttribLast(pastLimit, unreported) + + price = historicalTickLastProto.price + size = float(historicalTickLastProto.size) + exchange = historicalTickLastProto.exchange + specialConditions = historicalTickLastProto.specialConditions + + historicalTickLast = HistoricalTickLast( + time, tickAttribLast, price, size, exchange, specialConditions + ) + + return historicalTickLast + + +def createHistogramDataRequestProto( + reqId: int, contract: Contract, useRTH: bool, timePeriod: str +) -> HistogramDataRequestProto: + histogramDataRequestProto = HistogramDataRequestProto() + if isValidIntValue(reqId): + histogramDataRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + if contractProto is not None: + histogramDataRequestProto.contract.CopyFrom(contractProto) + if useRTH: + histogramDataRequestProto.useRTH = useRTH + if timePeriod: + histogramDataRequestProto.timePeriod = timePeriod + return histogramDataRequestProto + + +def decodeHistogramDataEntry( + histogramDataEntryProto: HistogramDataEntryProto, +) -> HistogramData: + histogramData = HistogramData() + if histogramDataEntryProto.HasField("price"): + histogramData.price = histogramDataEntryProto.price + if histogramDataEntryProto.HasField("size"): + histogramData.count = int(histogramDataEntryProto.size) + return histogramData diff --git a/ib_async/protobuf_converters/market_data_converters.py b/ib_async/protobuf_converters/market_data_converters.py new file mode 100644 index 00000000..077de3e7 --- /dev/null +++ b/ib_async/protobuf_converters/market_data_converters.py @@ -0,0 +1,292 @@ +""" +Market data protobuf converters +""" + +from dataclasses import dataclass + +from ..objects import ( + Contract, + OptionComputation, + TagValue, + TickAttrib, + TickAttribBidAsk, + TickAttribLast, + TickByTickAllLastData, + TickByTickBidAskData, + TickByTickMidPointData, + TickGenericData, + TickParams, + TickPriceData, + TickSizeData, + TickStringData, + TickType, + TickComputationData, +) +from ..protobuf.MarketDataRequest_pb2 import ( + MarketDataRequest as MarketDataRequestProto, +) +from ..protobuf.MarketDataType_pb2 import MarketDataType as MarketDataTypeProto +from ..protobuf.TickPrice_pb2 import TickPrice as TickPriceProto +from ..protobuf.TickSize_pb2 import TickSize as TickSizeProto +from ..protobuf.TickString_pb2 import TickString as TickStringProto +from ..protobuf.TickGeneric_pb2 import TickGeneric as TickGenericProto +from ..protobuf.TickOptionComputation_pb2 import ( + TickOptionComputation as TickOptionComputationProto, +) +from ..protobuf.TickReqParams_pb2 import TickReqParams as TickReqParamsProto +from ..protobuf.HistoricalTickLast_pb2 import ( + HistoricalTickLast as HistoricalTickLastProto, +) +from ..protobuf.HistoricalTickBidAsk_pb2 import ( + HistoricalTickBidAsk as HistoricalTickBidAskProto, +) +from ..protobuf.MarketDataTypeRequest_pb2 import ( + MarketDataTypeRequest as MarketDataTypeRequestProto, +) +from ..protobuf.HistoricalTick_pb2 import HistoricalTick as HistoricalTickProt +from ..protobuf.CancelMarketData_pb2 import CancelMarketData as CancelMarketDataProto +from ..util import NO_VALID_ID, UNSET_DOUBLE, UNSET_INTEGER, isValidIntValue +from .contract_converters import createContractProto +from .historical_data_converters import fillTagValueList + + +def createMarketDataTypeRequestProto(marketDataType: int) -> MarketDataTypeRequestProto: + marketDataTypeRequestProto = MarketDataTypeRequestProto() + if isValidIntValue(marketDataType): + marketDataTypeRequestProto.marketDataType = marketDataType + return marketDataTypeRequestProto + + +def createMarketDataRequestProto( + reqId: int, + contract: Contract, + genericTickList: str, + snapshot: bool, + regulatorySnapshot: bool, + marketDataOptionsList: list[TagValue], +) -> MarketDataRequestProto: + marketDataRequestProto = MarketDataRequestProto() + if isValidIntValue(reqId): + marketDataRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + if contractProto is not None: + marketDataRequestProto.contract.CopyFrom(contractProto) + if genericTickList: + marketDataRequestProto.genericTickList = genericTickList + if snapshot: + marketDataRequestProto.snapshot = snapshot + if regulatorySnapshot: + marketDataRequestProto.regulatorySnapshot = regulatorySnapshot + fillTagValueList(marketDataOptionsList, marketDataRequestProto.marketDataOptions) + return marketDataRequestProto + + +def cancelMarketDataProto(reqId: int) -> CancelMarketDataProto: + cancelMarketDataProto = CancelMarketDataProto() + if isValidIntValue(reqId): + cancelMarketDataProto.reqId = reqId + return cancelMarketDataProto + + +def createTickParams(msg: TickReqParamsProto) -> TickParams: + """Create a TickParams object from a TickReqParamsProto message.""" + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + minTick = float(msg.minTick) if msg.HasField("minTick") else UNSET_DOUBLE + bboExchange = msg.bboExchange if msg.HasField("bboExchange") else "" + snapshotPermissions = ( + msg.snapshotPermissions + if msg.HasField("snapshotPermissions") + else UNSET_INTEGER + ) + tickParams = TickParams(reqId, minTick, bboExchange, snapshotPermissions) + return tickParams + + +def createTickPriceData(msg: TickPriceProto) -> tuple[TickPriceData, TickSizeData]: + """Create a TickPriceData object from a TickPriceProto message.""" + if msg.HasField("reqId"): + reqId = msg.reqId + if msg.HasField("tickType") and msg.tickType in TickType: + tickType = TickType(msg.tickType) + if msg.HasField("price"): + price = float( + msg.price, + ) + if msg.HasField("size"): + size = float(msg.size) + if msg.HasField("attrMask"): + canAutoExecute = msg.attrMask & 1 != 0 + pastLimit = msg.attrMask & 2 != 0 + preOpen = msg.attrMask & 4 != 0 + attribs = TickAttrib( + canAutoExecute, + pastLimit, + preOpen, + ) + + tickPrice = TickPriceData(reqId, tickType, price, size, attribs) + + sizeTickType = TickType.NOT_SET + if TickType.BID == tickType: + sizeTickType = TickType.BID_SIZE + elif TickType.ASK == tickType: + sizeTickType = TickType.ASK_SIZE + elif TickType.LAST == tickType: + sizeTickType = TickType.LAST_SIZE + elif TickType.DELAYED_BID == tickType: + sizeTickType = TickType.DELAYED_BID_SIZE + elif TickType.DELAYED_ASK == tickType: + sizeTickType = TickType.DELAYED_ASK_SIZE + elif TickType.DELAYED_LAST == tickType: + sizeTickType = TickType.DELAYED_LAST_SIZE + + tickSize = TickSizeData(reqId, sizeTickType, size) + return tickPrice, tickSize + + +def createTickSizeData(msg: TickSizeProto) -> TickSizeData: + """Create a TickSizeData object from a TickSizeProto message.""" + if msg.HasField("reqId"): + reqId = msg.reqId + if msg.HasField("tickType") and msg.tickType in TickType: + tickType = TickType(msg.tickType) + if msg.HasField("size"): + size = float(msg.size) + tickSize = TickSizeData(reqId, tickType, size) + return tickSize + + +def createTickStringData(msg: TickStringProto) -> TickStringData: + """Create a TickStringData object from a TickStringProto message.""" + if msg.HasField("reqId"): + reqId = msg.reqId + if msg.HasField("tickType") and msg.tickType in TickType: + tickType = TickType(msg.tickType) + if msg.HasField("value"): + value = msg.value + tickString = TickStringData(reqId, tickType, value) + return tickString + + +def createTickGenericData(msg: TickGenericProto) -> TickGenericData: + """Create a TickGenericData object from a TickGenericProto message.""" + if msg.HasField("reqId"): + reqId = msg.reqId + if msg.HasField("tickType") and msg.tickType in TickType: + tickType = TickType(msg.tickType) + if msg.HasField("value"): + value = msg.value + tickGeneric = TickGenericData(reqId, tickType, value) + return tickGeneric + + +def createTickOptionComputation(msg: TickOptionComputationProto) -> TickComputationData: + """Create a OptionComputation object from a TickOptionCompuationProto message.""" + + tickType = ( + TickType(msg.tickType) if msg.HasField("tickType") else TickType.NOT_SET + ) + tickAttrib = msg.tickAttrib if msg.HasField("tickAttrib") else UNSET_INTEGER + impliedVol = msg.impliedVol if msg.HasField("impliedVol") else None + if impliedVol < 0: # -1 is the "not computed" indicator + impliedVol = None + delta = msg.delta if msg.HasField("delta") else None + if delta == -2: # -2 is the "not computed" indicator + delta = None + optPrice = msg.optPrice if msg.HasField("optPrice") else None + if optPrice == -1: # -1 is the "not computed" indicator + optPrice = None + pvDividend = msg.pvDividend if msg.HasField("pvDividend") else None + if pvDividend == -1: # -1 is the "not computed" indicator + pvDividend = None + gamma = msg.gamma if msg.HasField("gamma") else None + if gamma == -2: # -2 is the "not yet computed" indicator + gamma = None + vega = msg.vega if msg.HasField("vega") else None + if vega == -2: # -2 is the "not yet computed" indicator + vega = None + theta = msg.theta if msg.HasField("theta") else None + if theta == -2: # -2 is the "not yet computed" indicator + theta = None + undPrice = msg.undPrice if msg.HasField("undPrice") else None + if undPrice == -1: # -1 is the "not computed" indicator + undPrice = None + + comp = OptionComputation( + tickAttrib, + impliedVol if impliedVol != -1 else None, + delta if delta != -2 else None, + optPrice if optPrice != -1 else None, + pvDividend if pvDividend != -1 else None, + gamma if gamma != -2 else None, + vega if vega != -2 else vega, + theta if theta != -2 else theta, + undPrice if undPrice != -1 else None, + ) + + tick_comp = TickComputationData( + reqId=msg.reqId, + tickType=tickType, + computation=comp, + ) + return tick_comp + + +# def create_tick_by_tick_all_last_data( +# msg: TickByTickAllLastProto, +# ) -> TickByTickAllLastData: +# """Create a TickByTickAllLastData object from a TickByTickAllLastProto message.""" +# if msg.HasField("reqId"): +# reqId = msg.reqId +# if msg.HasField("tickType"): +# tickType = msg.tickType +# if msg.HasField("historicalTickLast"): +# historicalTickLast = msg.historicalTickLast +# if msg.HasField("historicalTickBidAsk"): +# historicalTickBidAsk = msg.historicalTickBidAsk +# if msg.HasField("historicalTickMidPoint"): +# historicalTickMidPoint = msg.historicalTickMidPoint + +# tickByTickAllLastData = TickByTickAllLastData( +# reqId=reqId, +# tickType=tickType, +# time=msg.time, +# price=msg.price, +# size=msg.size, +# tickAttribLast=TickAttribLast( +# pastLimit=msg.tickAttribLast.pastLimit, +# unreported=msg.tickAttribLast.unreported, +# ), +# exchange=msg.exchange, +# specialConditions=msg.specialConditions, +# ) +# return tickByTickAllLastData + + +# def create_tick_by_tick_bid_ask_data( +# msg: TickByTickBidAskProto, +# ) -> TickByTickBidAskData: +# """Create a TickByTickBidAskData object from a TickByTickBidAskProto message.""" +# return TickByTickBidAskData( +# reqId=msg.reqId, +# time=msg.time, +# bidPrice=msg.bidPrice, +# askPrice=msg.askPrice, +# bidSize=msg.bidSize, +# askSize=msg.askSize, +# tickAttribBidAsk=TickAttribBidAsk( +# bidPastLow=msg.tickAttribBidAsk.bidPastLow, +# askPastHigh=msg.tickAttribBidAsk.askPastHigh, +# ), +# ) + + +# def create_tick_by_tick_mid_point_data( +# msg: TickByTickMidPointProto, +# ) -> TickByTickMidPointData: +# """Create a TickByTickMidPointData object from a TickByTickMidPointProto message.""" +# return TickByTickMidPointData( +# reqId=msg.reqId, +# time=msg.time, +# midPoint=msg.midPoint, +# ) diff --git a/ib_async/protobuf_converters/trade_converter.py b/ib_async/protobuf_converters/trade_converter.py index 2ece6159..845bf2d6 100644 --- a/ib_async/protobuf_converters/trade_converter.py +++ b/ib_async/protobuf_converters/trade_converter.py @@ -71,6 +71,7 @@ def createOrderComboLegs(contractProto: ContractProto) -> list[OrderComboLeg]: return orderComboLegs + def createOrder( orderId: int, contractProto: ContractProto, orderProto: OrderProto ) -> Order: @@ -500,7 +501,7 @@ def createTagValueList(protoMap: dict[str, str]) -> list[TagValue]: tagValueList = [] if protoMap is not None and protoMap: for tag, value in protoMap.items(): - tagValue = TagValue(tag,value) + tagValue = TagValue(tag, value) tagValueList.append(tagValue) return tagValueList @@ -715,7 +716,7 @@ def createExecution(executionProto: ExecutionProto) -> Execution: if executionProto.HasField("optExerciseOrLapseType"): execution.optExerciseOrLapseType = getEnumTypeFromString( OptionExerciseType, executionProto.optExerciseOrLapseType - ) + ) return execution diff --git a/ib_async/ticker.py b/ib_async/ticker.py index 9771c894..9a529ce6 100644 --- a/ib_async/ticker.py +++ b/ib_async/ticker.py @@ -1,8 +1,10 @@ """Access to realtime market information.""" +import logging +from contextlib import suppress from dataclasses import dataclass, field from datetime import datetime -from typing import ClassVar, Optional, Union +from typing import ClassVar, Final, Optional, TypeAlias, Union from eventkit import Event, Op @@ -17,11 +19,89 @@ TickByTickAllLast, TickByTickBidAsk, TickByTickMidPoint, + TickComputationData, TickData, + TickDataType, + TickGenericData, + TickPriceData, + TickSizeData, + TickStringData, + TickType, ) -from ib_async.util import dataclassRepr, isNan +from ib_async.util import dataclassRepr, isNan, parseIBDatetime nan = float("nan") +TickDict: TypeAlias = dict[TickType, str] + + +PRICE_TICK_MAP: Final[TickDict] = { + TickType.HIGH: "high", + TickType.DELAYED_HIGH: "high", + TickType.LOW: "low", + TickType.DELAYED_LOW: "low", + TickType.CLOSE: "close", + TickType.DELAYED_CLOSE: "close", + TickType.OPEN: "open", + TickType.DELAYED_OPEN: "open", + TickType.LOW_13_WEEK: "low13week", + TickType.HIGH_13_WEEK: "high13week", + TickType.LOW_26_WEEK: "low26week", + TickType.HIGH_26_WEEK: "high26week", + TickType.LOW_52_WEEK: "low52week", + TickType.HIGH_52_WEEK: "high52week", + TickType.AUCTION_PRICE: "auctionPrice", + TickType.MARK_PRICE: "markPrice", + TickType.BID_YIELD: "bidYield", + TickType.DELAYED_YIELD_BID: "bidYield", + TickType.ASK_YIELD: "askYield", + TickType.DELAYED_YIELD_ASK: "askYield", + TickType.LAST_YIELD: "lastYield", +} + + +SIZE_TICK_MAP: Final[TickDict] = { + TickType.VOLUME: "volume", + TickType.DELAYED_VOLUME: "volume", + TickType.SHORT_TERM_VOLUME_3_MIN: "volumeRate3Min", + TickType.SHORT_TERM_VOLUME_5_MIN: "volumeRate5Min", + TickType.SHORT_TERM_VOLUME_10_MIN: "volumeRate10Min", + TickType.AVG_VOLUME: "avVolume", + TickType.OPTION_CALL_OPEN_INTEREST: "callOpenInterest", + TickType.OPTION_PUT_OPEN_INTEREST: "putOpenInterest", + TickType.OPTION_CALL_VOLUME: "callVolume", + TickType.OPTION_PUT_VOLUME: "putVolume", + TickType.AUCTION_VOLUME: "auctionVolume", + TickType.AUCTION_IMBALANCE: "auctionImbalance", + TickType.REGULATORY_IMBALANCE: "regulatoryImbalance", + TickType.FUTURES_OPEN_INTEREST: "futuresOpenInterest", + TickType.AVG_OPT_VOLUME: "avOptionVolume", + TickType.SHORTABLE_SHARES: "shortableShares", +} + +GENERIC_TICK_MAP: Final[TickDict] = { + TickType.OPTION_HISTORICAL_VOL: "histVolatility", + TickType.OPTION_IMPLIED_VOL: "impliedVolatility", + TickType.INDEX_FUTURE_PREMIUM: "indexFuturePremium", + TickType.SHORTABLE: "shortable", + TickType.HALTED: "halted", + TickType.TRADE_COUNT: "tradeCount", + TickType.TRADE_RATE: "tradeRate", + TickType.VOLUME_RATE: "volumeRate", + TickType.RT_HISTORICAL_VOL: "rtHistVolatility", +} + +GREEKS_TICK_MAP: Final[TickDict] = { + TickType.BID_OPTION_COMPUTATION: "bidGreeks", + TickType.DELAYED_BID_OPTION: "bidGreeks", + TickType.ASK_OPTION_COMPUTATION: "askGreeks", + TickType.DELAYED_ASK_OPTION: "askGreeks", + TickType.LAST_OPTION_COMPUTATION: "lastGreeks", + TickType.DELAYED_LAST_OPTION: "lastGreeks", + TickType.MODEL_OPTION: "modelGreeks", + TickType.DELAYED_MODEL_OPTION: "modelGreeks", +} + +_logger = logging.getLogger("ib_async.ticker") @dataclass @@ -132,7 +212,7 @@ class Ticker: snapshotPermissions: int = 0 defaults: IBDefaults = field(default_factory=IBDefaults, repr=False) - created: bool = False + created: bool = field(default=False, repr=False) def __post_init__(self): # when copying a dataclass, the __post_init__ runs again, so we @@ -195,7 +275,6 @@ def __post_init__(self): self.auctionPrice = self.defaults.unset self.auctionImbalance = self.defaults.unset self.regulatoryImbalance = self.defaults.unset - self.created = True def __eq__(self, other): @@ -207,6 +286,278 @@ def __hash__(self): __repr__ = dataclassRepr __str__ = dataclassRepr + def _on_ticker_data(self, tick_data: TickDataType, last_time: datetime): + _logger.debug( + "Ticker %s. Received tick data: %s, %s", + self.contract.symbol, + tick_data, + last_time, + ) + if isinstance(tick_data, TickPriceData): + self._on_price_size_tick(tick_data, last_time) + elif isinstance(tick_data, TickSizeData): + self._on_size_tick(tick_data, last_time) + elif isinstance(tick_data, TickStringData): + self._on_tick_string(tick_data, last_time) + elif isinstance(tick_data, TickGenericData): + self._on_tick_generic(tick_data, last_time) + elif isinstance(tick_data, TickComputationData): + self._on_opt_computation(tick_data) + else: + _logger.error("Ticker %s. Unknown tick data: %s", self.contract, tick_data) + + def _on_price_size_tick(self, tick_price: TickPriceData, last_time: datetime): + price = tick_price.price + size = tick_price.size + + # https://interactivebrokers.github.io/tws-api/tick_types.html + if tick_price.tickType in {TickType.BID, TickType.DELAYED_BID}: + # Note: Keep these size==0 overrides INSIDE each tickType where it is needed because + # other tickTypes like open/high/low/close are values with size=0 but those + # are still valid prices to receive. + # Bid/Ask updates always have a Price+Size delivered at the same time, while the + # other properties are mainly price-only delivery methods. + if tick_price.size == 0: + price = self.defaults.emptyPrice + size = self.defaults.emptySize + + self.prevBid = self.bid + self.prevBidSize = self.bidSize + self.bid = price + self.bidSize = size + elif tick_price.tickType in {TickType.ASK, TickType.DELAYED_ASK}: + if tick_price.size == 0: + price = self.defaults.emptyPrice + size = self.defaults.emptySize + + self.prevAsk = self.ask + self.prevAskSize = self.askSize + self.ask = price + self.askSize = size + elif tick_price.tickType in {TickType.LAST, TickType.DELAYED_LAST}: + # for 'last' values, price can be valid with size=0 for updates like 'last + # SPX price' since SPX doesn't trade + # Workaround: for TICK-NYSE, it is valid to have price=-1, size=0 because + # it can float between -10,000 and 10,000 + # and it also never reports a size. As a workaround, check if + # ticker.close exists as a proxy for "not TICK-NYSE" + # because TICK-NYSE never has open/close values populated. + if tick_price.price == -1 and tick_price.size == 0 and self.close > 0: + price = self.defaults.emptyPrice + size = self.defaults.emptyPrice + + # BUG? IBKR is sometimes sending a GOOD VALUE followed by a PREVIOUS value + # all under tickType=4? + # e.g. I get the SPX close delivered first with size=0, then I get another + # data point with size=1 priced one point lower, + # but since the older price is delivered second, it replaces the "last" + # value with a wrong value? Not sure if it's + # an IBKR data problem or a logic problem somewhere here? + # More research: IBKR also shows the bad value in their own app, so there + # is a data bug in their own server logic somewhere. + + # self._logger.error(f"[{tickType=}] updating last price size: {price=} {size=} :: BEFORE {ticker=}") + # self._logger.error(f"[{tickType=}] SETTING {ticker.prevLast=} = {ticker.last=}; {ticker.prevLastSize=} = {ticker.lastSize=}") + + self.prevLast = self.last + self.prevLastSize = self.lastSize + self.last = price + self.lastSize = size + + # self._logger.error(f"[{tickType=}] SET {ticker.prevLast=} = {ticker.last=}; {ticker.prevLastSize=} = {ticker.lastSize=}") + # self._logger.error(f"[{tickType=}] updating last price size: {price=} {size=} :: AFTER {ticker=}") + else: + assert tick_price.tickType in PRICE_TICK_MAP, ( + f"Received tick {tick_price.tickType=} {tick_price.price=} but we don't have an attribute mapping for it? Triggered from {self.contract=}" + ) + + setattr(self, PRICE_TICK_MAP[tick_price.tickType], tick_price.price) + + if price or size: + tick = TickData( + last_time, tick_price.tickType, tick_price.price, tick_price.size + ) + self.ticks.append(tick) + + def _on_size_tick(self, tick_size: TickSizeData, last_time: datetime): + price = self.defaults.emptyPrice + + # self._logger.error( + # f"tickSize with tickType {tickType}: " f"processing value: {size!r}" + # ) + + # https://interactivebrokers.github.io/tws-api/tick_types.html + if tick_size.tickType in { + TickType.BID_SIZE, + TickType.DELAYED_BID_SIZE, + }: + if tick_size.size == self.bidSize: + return + + self.prevBidSize = self.bidSize + if tick_size.size == 0: + self.bid = self.defaults.emptyPrice + self.bidSize = self.defaults.emptySize + else: + price = self.bid + self.bidSize = tick_size.size + elif tick_size.tickType in { + TickType.ASK_SIZE, + TickType.DELAYED_ASK_SIZE, + }: + if tick_size.size == self.askSize: + return + + self.prevAskSize = self.askSize + if tick_size.size == 0: + self.ask = self.defaults.emptyPrice + self.askSize = self.defaults.emptySize + else: + price = self.ask + self.askSize = tick_size.size + elif tick_size.tickType in { + TickType.LAST_SIZE, + TickType.DELAYED_LAST_SIZE, + }: + price = self.last + + if self.isUnset(price): + return + + if tick_size.size != self.lastSize: + self.prevLastSize = self.lastSize + self.lastSize = tick_size.size + else: + assert tick_size.tickType in SIZE_TICK_MAP, ( + f"Received tick {tick_size.tickType=} {tick_size.size=} but we don't have an attribute mapping for it? Triggered from {self.contract=}" + ) + + setattr(self, SIZE_TICK_MAP[tick_size.tickType], tick_size.size) + + if price or tick_size.size: + tick = TickData(last_time, tick_size.tickType, price, tick_size.size) + self.ticks.append(tick) + + def _on_tick_string(self, tick_string: TickStringData, last_time: datetime): + try: + if tick_string.tickType == TickType.BID_EXCH: + self.bidExchange = tick_string.value + elif tick_string.tickType == TickType.ASK_EXCH: + self.askExchange = tick_string.value + elif tick_string.tickType == TickType.LAST_EXCH: + self.lastExchange = tick_string.value + elif tick_string.tickType == TickType.LAST_TIMESTAMP: + timestamp = int(tick_string.value) + + # only populate if timestamp isn't '0' (we don't want to report "last trade: 20,000 days ago") + if timestamp: + self.lastTimestamp = datetime.fromtimestamp( + timestamp, self.defaultTimezone + ) + elif tick_string.tickType == TickType.FUNDAMENTAL_RATIOS: + # https://web.archive.org/web/20200725010343/https://interactivebrokers.github.io/tws-api/fundamental_ratios_tags.html + d = dict( + t.split("=") + for t in tick_string.value.split(";") + if t # type: ignore + ) # type: ignore + for k, v in d.items(): + with suppress(ValueError): + if v == "-99999.99": + v = "nan" + d[k] = float(v) # type: ignore + # TODO - WTF? https://github.com/ib-api-reloaded/ib_async/blob/2afdae44a47f067bf3d1a23a4840234f2906ef64/ib_async/wrapper.py#L1135 + # d[k] = int(v) # type: ignore + self.fundamentalRatios = FundamentalRatios(**d) + elif tick_string.tickType in {TickType.RT_VOLUME, TickType.RT_TRD_VOLUME}: + # RT Volume or RT Trade Volume string format: + # price;size;ms since epoch;total volume;VWAP;single trade + # example: + # 701.28;1;1348075471534;67854;701.46918464;true + priceStr, sizeStr, rtTime, volume, vwap, _ = tick_string.value.split( + ";" + ) + if volume: + if tick_string.tickType == TickType.RT_VOLUME: + self.rtVolume = float(volume) + elif tick_string.tickType == TickType.RT_TRD_VOLUME: + self.rtTradeVolume = float(volume) + + if vwap: + self.vwap = float(vwap) + + if rtTime: + self.rtTime = datetime.fromtimestamp( + int(rtTime) / 1000, self.defaults.timezone + ) + + if priceStr == "": + return + + price = float(priceStr) + size = float(sizeStr) + + self.prevLast = self.last + self.prevLastSize = self.lastSize + + self.last = price + self.lastSize = size + + tick = TickData(last_time, tick_string.tickType, price, size) + self.ticks.append(tick) + elif tick_string.tickType == TickType.IB_DIVIDENDS: + # Dividend tick: + # https://interactivebrokers.github.io/tws-api/tick_types.html#ib_dividends + # example value: '0.83,0.92,20130219,0.23' + past12, next12, nextDate, nextAmount = tick_string.value.split(",") + self.dividends = Dividends( + float(past12) if past12 else None, + float(next12) if next12 else None, + parseIBDatetime(nextDate) if nextDate else None, + float(nextAmount) if nextAmount else None, + ) + else: + _logger.error( + f"tickString with tickType {tick_string.tickType}: unhandled value: {tick_string.value!r}" + ) + + except ValueError: + _logger.error( + f"tickString with tickType {tick_string.tickType}: malformed value: {tick_string.value!r}" + ) + + def _on_tick_generic(self, tick_generic: TickGenericData, last_time: datetime): + try: + value = float(tick_generic.value) + value = value if value > 0 else self.defaults.emptySize + except ValueError: + _logger.error( + f"[tickType {tick_generic.tickType}] genericTick: malformed value: {value!r}" + ) + return + + assert tick_generic.tickType in GENERIC_TICK_MAP, ( + f"Received tick {tick_generic.tickType=} {value=} but we don't have an" + f" attribute mapping for it? Triggered from {self.contract=}" + ) + + setattr(self, GENERIC_TICK_MAP[tick_generic.tickType], value) + + tick = TickData(last_time, tick_generic.tickType, value, 0) + self.ticks.append(tick) + + def _on_opt_computation(self, tick_computation: TickComputationData): + assert tick_computation.tickType in GREEKS_TICK_MAP, ( + f"Received tick {tick_computation.tickType=} " + f"{tick_computation.computation.tickAttrib=} but we don't" + f" have an attribute mapping for it? Triggered from {self.contract=}" + ) + setattr( + self, + GREEKS_TICK_MAP[tick_computation.tickType], + tick_computation.computation, + ) + def isUnset(self, value) -> bool: # if default value is nan and value is nan, it is unset. # else, if value matches default value, it is unset. @@ -254,19 +605,56 @@ class TickerUpdateEvent(Event): def trades(self) -> "Tickfilter": """Emit trade ticks.""" - return Tickfilter((4, 5, 48, 68, 71), self) + return Tickfilter( + ( + TickType.LAST, + TickType.LAST_SIZE, + TickType.RT_VOLUME, + TickType.DELAYED_LAST, + TickType.DELAYED_LAST_SIZE, + ), + self, + ) def bids(self) -> "Tickfilter": """Emit bid ticks.""" - return Tickfilter((0, 1, 66, 69), self) + return Tickfilter( + ( + TickType.BID_SIZE, + TickType.BID, + TickType.DELAYED_BID, + TickType.DELAYED_BID_SIZE, + ), + self, + ) def asks(self) -> "Tickfilter": """Emit ask ticks.""" - return Tickfilter((2, 3, 67, 70), self) + return Tickfilter( + ( + TickType.ASK, + TickType.ASK_SIZE, + TickType.DELAYED_ASK, + TickType.DELAYED_ASK_SIZE, + ), + self, + ) def bidasks(self) -> "Tickfilter": """Emit bid and ask ticks.""" - return Tickfilter((0, 1, 66, 69, 2, 3, 67, 70), self) + return Tickfilter( + ( + TickType.BID_SIZE, + TickType.BID, + TickType.DELAYED_BID, + TickType.DELAYED_BID_SIZE, + TickType.ASK, + TickType.ASK_SIZE, + TickType.DELAYED_ASK, + TickType.DELAYED_ASK_SIZE, + ), + self, + ) def midpoints(self) -> "Tickfilter": """Emit midpoint ticks.""" diff --git a/ib_async/util.py b/ib_async/util.py index c1a71244..98f67c25 100644 --- a/ib_async/util.py +++ b/ib_async/util.py @@ -39,7 +39,8 @@ EPOCH: Final = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc) UNSET_INTEGER: Final = 2**31 - 1 UNSET_DOUBLE: Final = sys.float_info.max -UNSET_DECIMAL = Decimal(2**127 - 1) +UNSET_DECIMAL: Final = Decimal(2**127 - 1) +NO_VALID_ID: Final = -1 Time_t: TypeAlias = dt.time | dt.datetime @@ -119,6 +120,7 @@ def dataclassNonDefaults(obj) -> dict[str, Any]: if value is not None and value != field.default and value == value + and field.repr and not ( (isinstance(value, list) and value == []) or (isinstance(value, dict) and value == {}) @@ -625,5 +627,6 @@ def getEnumTypeFromString(cls, stringIn): def listOfValues(cls): return list(map(lambda c: c, cls)) + def isValidIntValue(val: int) -> bool: return val != UNSET_INTEGER diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 4a305d32..c7bcfabe 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -3,12 +3,10 @@ import asyncio import logging import time - from collections import defaultdict -from contextlib import suppress from dataclasses import dataclass, field from datetime import datetime -from typing import Any, cast, Final, Optional, TYPE_CHECKING, TypeAlias, Union +from typing import TYPE_CHECKING, Any, Final, TypeAlias, Union, cast import eventkit as ev @@ -59,19 +57,26 @@ TickByTickAllLast, TickByTickBidAsk, TickByTickMidPoint, - TickData, + TickComputationData, + TickDataType, + TickGenericData, + TickParams, + TickPriceData, + TickSizeData, + TickStringData, + TickType, TradeLogEntry, ) from ib_async.order import Order, OrderState, OrderStatus, Trade from ib_async.ticker import Ticker from ib_async.util import ( + UNSET_DOUBLE, + UNSET_INTEGER, dataclassAsDict, dataclassUpdate, getLoop, globalErrorEvent, parseIBDatetime, - UNSET_DOUBLE, - UNSET_INTEGER, ) if TYPE_CHECKING: @@ -79,74 +84,6 @@ OrderKeyType: TypeAlias = int | tuple[int, int] -TickDict: TypeAlias = dict[int, str] - -PRICE_TICK_MAP: Final[TickDict] = { - 6: "high", - 72: "high", - 7: "low", - 73: "low", - 9: "close", - 75: "close", - 14: "open", - 76: "open", - 15: "low13week", - 16: "high13week", - 17: "low26week", - 18: "high26week", - 19: "low52week", - 20: "high52week", - 35: "auctionPrice", - 37: "markPrice", - 50: "bidYield", - 103: "bidYield", - 51: "askYield", - 104: "askYield", - 52: "lastYield", -} - - -SIZE_TICK_MAP: Final[TickDict] = { - 8: "volume", - 74: "volume", - 63: "volumeRate3Min", - 64: "volumeRate5Min", - 65: "volumeRate10Min", - 21: "avVolume", - 27: "callOpenInterest", - 28: "putOpenInterest", - 29: "callVolume", - 30: "putVolume", - 34: "auctionVolume", - 36: "auctionImbalance", - 61: "regulatoryImbalance", - 86: "futuresOpenInterest", - 87: "avOptionVolume", - 89: "shortableShares", -} - -GENERIC_TICK_MAP: Final[TickDict] = { - 23: "histVolatility", - 24: "impliedVolatility", - 31: "indexFuturePremium", - 46: "shortable", - 49: "halted", - 54: "tradeCount", - 55: "tradeRate", - 56: "volumeRate", - 58: "rtHistVolatility", -} - -GREEKS_TICK_MAP: Final[TickDict] = { - 10: "bidGreeks", - 80: "bidGreeks", - 11: "askGreeks", - 81: "askGreeks", - 12: "lastGreeks", - 82: "lastGreeks", - 13: "modelGreeks", - 83: "modelGreeks", -} class RequestError(Exception): @@ -264,6 +201,8 @@ def __post_init__(self): self.defaultEmptyPrice = self.defaults.emptyPrice self.defaultEmptySize = self.defaults.emptySize self.response_bus = ev.Event("Response bus") + self.ticker_bus = ev.Event("Ticker bus") + self.bar_bus = ev.Event("Bar bus") self.reset() @@ -297,6 +236,9 @@ def reset(self): self._timeout = 0 self._futures = {} self._results = {} + self.response_bus.clear() + self.ticker_bus.clear() + self.bar_bus.clear() self.setTimeout(0) def setEventsDone(self): @@ -361,19 +303,26 @@ def startTicker(self, reqId: int, contract: Contract, tickType: Union[int, str]) Start a tick request that has the reqId associated with the contract. Return the ticker. """ - ticker = self.tickers.get(hash(contract)) + ticker = self.reqId2Ticker.get(reqId) if not ticker: ticker = Ticker(contract=contract, defaults=self.defaults) - self.tickers[hash(contract)] = ticker + self.reqId2Ticker[reqId] = ticker + self.ticker_bus.filter(lambda r, d, t: r == reqId).takewhile( + lambda r, data, t: data is not None + ).pluck(1, 2).connect(ticker._on_ticker_data) - self.reqId2Ticker[reqId] = ticker - self._reqId2Contract[reqId] = contract - self.ticker2ReqId[tickType][ticker] = reqId return ticker def endTicker(self, ticker: Ticker, tickType: Union[int, str]): - reqId = self.ticker2ReqId[tickType].pop(ticker, 0) - self._reqId2Contract.pop(reqId, None) + reqId = None + for r, t in self.reqId2Ticker.items(): + if hash(t.contract) == hash(ticker.contract): + reqId = r + break + + if reqId: + self.reqId2Ticker.pop(reqId, 0) + self.ticker_bus.emit(reqId, None, None) return reqId def startSubscription(self, reqId, subscriber, contract=None): @@ -734,162 +683,60 @@ def headTimestamp(self, reqId: int, headTimestamp: str): self.response_bus.emit(reqId, exc) def historicalTicks(self, reqId: int, ticks: list[HistoricalTick], done: bool): - result = self._results.get(reqId) - if result is not None: - result += ticks - + self.response_bus.emit(reqId, ticks) if done: - self._endReq(reqId) + self.response_bus.emit(reqId, None) def historicalTicksBidAsk( self, reqId: int, ticks: list[HistoricalTickBidAsk], done: bool ): - result = self._results.get(reqId) - if result is not None: - result += ticks - + self.response_bus.emit(reqId, ticks) if done: - self._endReq(reqId) + self.response_bus.emit(reqId, None) def historicalTicksLast( self, reqId: int, ticks: list[HistoricalTickLast], done: bool ): - result = self._results.get(reqId) - if result is not None: - result += ticks - + self.response_bus.emit(reqId, ticks) if done: - self._endReq(reqId) + self.response_bus.emit(reqId, None) # additional wrapper method provided by Client - def priceSizeTick(self, reqId: int, tickType: int, price: float, size: float): + def priceSizeTick(self, reqId: int, tick_price: TickPriceData): ticker = self.reqId2Ticker.get(reqId) if not ticker: self._logger.error(f"priceSizeTick: Unknown reqId: {reqId}") return - - # self._logger.error(f"WHAT R U DOING: {tickType=} {price=} {size=}") - - # Allow overwriting IBKR's default "empty price" of -1 when there is no qty/size on a side. - # https://interactivebrokers.github.io/tws-api/tick_types.html - if tickType in {1, 66}: - # Note: Keep these size==0 overrides INSIDE each tickType where it is needed because - # other tickTypes like open/high/low/close are values with size=0 but those - # are still valid prices to receive. - # Bid/Ask updates always have a Price+Size delivered at the same time, while the - # other properties are mainly price-only delivery methods. - if size == 0: - price = self.defaultEmptyPrice - size = self.defaultEmptySize - - ticker.prevBid = ticker.bid - ticker.prevBidSize = ticker.bidSize - ticker.bid = price - ticker.bidSize = size - elif tickType in {2, 67}: - if size == 0: - price = self.defaultEmptyPrice - size = self.defaultEmptySize - - ticker.prevAsk = ticker.ask - ticker.prevAskSize = ticker.askSize - ticker.ask = price - ticker.askSize = size - elif tickType in {4, 68}: - # for 'last' values, price can be valid with size=0 for updates like 'last SPX price' since SPX doesn't trade - # Workaround: for TICK-NYSE, it is valid to have price=-1, size=0 because it can float between -10,000 and 10,000 - # and it also never reports a size. As a workaround, check if ticker.close exists as a proxy for "not TICK-NYSE" - # because TICK-NYSE never has open/close values populated. - if price == -1 and size == 0 and ticker.close > 0: - price = self.defaultEmptyPrice - size = self.defaultEmptySize - - # BUG? IBKR is sometimes sending a GOOD VALUE followed by a PREVIOUS value all under tickType=4? - # e.g. I get the SPX close delivered first with size=0, then I get another data point with size=1 priced one point lower, - # but since the older price is delivered second, it replaces the "last" value with a wrong value? Not sure if it's - # an IBKR data problem or a logic problem somewhere here? - # More research: IBKR also shows the bad value in their own app, so there is a data bug in their own server logic somewhere. - - # self._logger.error(f"[{tickType=}] updating last price size: {price=} {size=} :: BEFORE {ticker=}") - # self._logger.error(f"[{tickType=}] SETTING {ticker.prevLast=} = {ticker.last=}; {ticker.prevLastSize=} = {ticker.lastSize=}") - - ticker.prevLast = ticker.last - ticker.prevLastSize = ticker.lastSize - ticker.last = price - ticker.lastSize = size - - # self._logger.error(f"[{tickType=}] SET {ticker.prevLast=} = {ticker.last=}; {ticker.prevLastSize=} = {ticker.lastSize=}") - # self._logger.error(f"[{tickType=}] updating last price size: {price=} {size=} :: AFTER {ticker=}") - else: - assert tickType in PRICE_TICK_MAP, ( - f"Received tick {tickType=} {price=} but we don't have an attribute mapping for it? Triggered from {ticker.contract=}" - ) - - setattr(ticker, PRICE_TICK_MAP[tickType], price) - - if price or size: - tick = TickData(self.lastTime, tickType, price, size) - ticker.ticks.append(tick) - + self.ticker_bus.emit(reqId, tick_price, self.lastTime) self.pendingTickers.add(ticker) - def tickSize(self, reqId: int, tickType: int, size: float): + def tickSize(self, reqId: int, tick_size: TickSizeData): ticker = self.reqId2Ticker.get(reqId) if not ticker: self._logger.error(f"tickSize: Unknown reqId: {reqId}") return + self.ticker_bus.emit(reqId, tick_size, self.lastTime) + self.pendingTickers.add(ticker) - price = self.defaultEmptyPrice - - # self._logger.error( - # f"tickSize with tickType {tickType}: " f"processing value: {size!r}" - # ) - - # https://interactivebrokers.github.io/tws-api/tick_types.html - if tickType in {0, 69}: - if size == ticker.bidSize: - return - - ticker.prevBidSize = ticker.bidSize - if size == 0: - ticker.bid = self.defaultEmptyPrice - ticker.bidSize = self.defaultEmptySize - else: - price = ticker.bid - ticker.bidSize = size - elif tickType in {3, 70}: - if size == ticker.askSize: - return - - ticker.prevAskSize = ticker.askSize - if size == 0: - ticker.ask = self.defaultEmptyPrice - ticker.askSize = self.defaultEmptySize - else: - price = ticker.ask - ticker.askSize = size - elif tickType in {5, 71}: - price = ticker.last - - if ticker.isUnset(price): - return - - if size != ticker.lastSize: - ticker.prevLastSize = ticker.lastSize - ticker.lastSize = size - else: - assert tickType in SIZE_TICK_MAP, ( - f"Received tick {tickType=} {size=} but we don't have an attribute mapping for it? Triggered from {ticker.contract=}" - ) - - setattr(ticker, SIZE_TICK_MAP[tickType], size) - - if price or size: - tick = TickData(self.lastTime, tickType, price, size) - ticker.ticks.append(tick) + def tickString(self, reqId: int, tick_string: TickStringData): + if not (ticker := self.reqId2Ticker.get(reqId)): + return + self.ticker_bus.emit(reqId, tick_string, self.lastTime) + self.pendingTickers.add(ticker) + def tickGeneric(self, reqId: int, tick_generic: TickGenericData): + if not (ticker := self.reqId2Ticker.get(reqId)): + return + self.ticker_bus.emit(reqId, tick_generic, self.lastTime) self.pendingTickers.add(ticker) + def tickReqParams(self, reqId: int, tickParams: TickParams): + if not (ticker := self.reqId2Ticker.get(reqId)): + return + ticker.minTick = tickParams.minTick + ticker.bboExchange = tickParams.bboExchange + ticker.snapshotPermissions = tickParams.snapshotPermissions + def tickSnapshotEnd(self, reqId: int): self._endReq(reqId) @@ -979,130 +826,6 @@ def tickByTickMidPoint(self, reqId: int, time: int, midPoint: float): ticker.tickByTicks.append(tick) self.pendingTickers.add(ticker) - def tickString(self, reqId: int, tickType: int, value: str): - if not (ticker := self.reqId2Ticker.get(reqId)): - return - - try: - if tickType == 32: - ticker.bidExchange = value - elif tickType == 33: - ticker.askExchange = value - elif tickType == 84: - ticker.lastExchange = value - elif tickType == 45: - timestamp = int(value) - - # only populate if timestamp isn't '0' (we don't want to report "last trade: 20,000 days ago") - if timestamp: - ticker.lastTimestamp = datetime.fromtimestamp( - timestamp, self.defaultTimezone - ) - elif tickType == 47: - # https://web.archive.org/web/20200725010343/https://interactivebrokers.github.io/tws-api/fundamental_ratios_tags.html - d = dict( - t.split("=") - for t in value.split(";") - if t # type: ignore - ) # type: ignore - for k, v in d.items(): - with suppress(ValueError): - if v == "-99999.99": - v = "nan" - d[k] = float(v) # type: ignore - d[k] = int(v) # type: ignore - ticker.fundamentalRatios = FundamentalRatios(**d) - elif tickType in {48, 77}: - # RT Volume or RT Trade Volume string format: - # price;size;ms since epoch;total volume;VWAP;single trade - # example: - # 701.28;1;1348075471534;67854;701.46918464;true - priceStr, sizeStr, rtTime, volume, vwap, _ = value.split(";") - if volume: - if tickType == 48: - ticker.rtVolume = float(volume) - elif tickType == 77: - ticker.rtTradeVolume = float(volume) - - if vwap: - ticker.vwap = float(vwap) - - if rtTime: - ticker.rtTime = datetime.fromtimestamp( - int(rtTime) / 1000, self.defaultTimezone - ) - - if priceStr == "": - return - - price = float(priceStr) - size = float(sizeStr) - - ticker.prevLast = ticker.last - ticker.prevLastSize = ticker.lastSize - - ticker.last = price - ticker.lastSize = size - - tick = TickData(self.lastTime, tickType, price, size) - ticker.ticks.append(tick) - elif tickType == 59: - # Dividend tick: - # https://interactivebrokers.github.io/tws-api/tick_types.html#ib_dividends - # example value: '0.83,0.92,20130219,0.23' - past12, next12, nextDate, nextAmount = value.split(",") - ticker.dividends = Dividends( - float(past12) if past12 else None, - float(next12) if next12 else None, - parseIBDatetime(nextDate) if nextDate else None, - float(nextAmount) if nextAmount else None, - ) - else: - self._logger.error( - f"tickString with tickType {tickType}: unhandled value: {value!r}" - ) - - self.pendingTickers.add(ticker) - except ValueError: - self._logger.error( - f"tickString with tickType {tickType}: malformed value: {value!r}" - ) - - def tickGeneric(self, reqId: int, tickType: int, value: float): - ticker = self.reqId2Ticker.get(reqId) - if not ticker: - return - - try: - value = float(value) - value = value if value > 0 else self.defaultEmptySize - except ValueError: - self._logger.error( - f"[tickType {tickType}] genericTick: malformed value: {value!r}" - ) - return - - assert tickType in GENERIC_TICK_MAP, ( - f"Received tick {tickType=} {value=} but we don't have an attribute mapping for it? Triggered from {ticker.contract=}" - ) - - setattr(ticker, GENERIC_TICK_MAP[tickType], value) - - tick = TickData(self.lastTime, tickType, value, 0) - ticker.ticks.append(tick) - self.pendingTickers.add(ticker) - - def tickReqParams( - self, reqId: int, minTick: float, bboExchange: str, snapshotPermissions: int - ): - ticker = self.reqId2Ticker.get(reqId) - if not ticker: - return - - ticker.minTick = minTick - ticker.bboExchange = bboExchange - ticker.snapshotPermissions = snapshotPermissions - def smartComponents(self, reqId, components): self._endReq(reqId, components) @@ -1183,44 +906,18 @@ def updateMktDepthL2( self.pendingTickers.add(ticker) def tickOptionComputation( - self, - reqId: int, - tickType: int, - tickAttrib: int, - impliedVol: float, - delta: float, - optPrice: float, - pvDividend: float, - gamma: float, - vega: float, - theta: float, - undPrice: float, + self, reqId: int, tick_computation: TickComputationData ): - comp = OptionComputation( - tickAttrib, - impliedVol if impliedVol != -1 else None, - delta if delta != -2 else None, - optPrice if optPrice != -1 else None, - pvDividend if pvDividend != -1 else None, - gamma if gamma != -2 else None, - vega if vega != -2 else vega, - theta if theta != -2 else theta, - undPrice if undPrice != -1 else None, - ) ticker = self.reqId2Ticker.get(reqId) if ticker: # reply from reqMktData # https://interactivebrokers.github.io/tws-api/tick_types.html - assert tickType in GREEKS_TICK_MAP, ( - f"Received tick {tickType=} {tickAttrib=} but we don't have an attribute mapping for it? Triggered from {ticker.contract=}" - ) - - setattr(ticker, GREEKS_TICK_MAP[tickType], comp) + self.ticker_bus.emit(reqId, tick_computation, self.lastTime) self.pendingTickers.add(ticker) - elif reqId in self._futures: + elif reqId and tick_computation.tickType == TickType.NOT_SET: # reply from calculateImpliedVolatility or calculateOptionPrice - self._endReq(reqId, comp) + self.response_bus.emit(reqId, tick_computation.computation) else: self._logger.error(f"tickOptionComputation: Unknown reqId: {reqId}") @@ -1265,8 +962,7 @@ def scannerDataEnd(self, reqId: int): dataList.updateEvent.emit(dataList) def histogramData(self, reqId: int, items: list[HistogramData]): - result = [HistogramData(item.price, item.count) for item in items] - self._endReq(reqId, result) + self.response_bus.emit(reqId, items) def securityDefinitionOptionParameter( self, @@ -1458,7 +1154,7 @@ def error( self.response_bus.emit(reqId, error) else: # a None will be interpreted as an empty result - self._logger.error("is request %s, %s", reqId,msg) + self._logger.error("is request %s, %s", reqId, msg) self.response_bus.emit(reqId, None) self._logger.info(msg) else: From 4573253c81cecc14fd21be59dba685cdbf255ad9 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:00:41 +0100 Subject: [PATCH 03/48] disable extremely high volume logging on ticker --- ib_async/ticker.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ib_async/ticker.py b/ib_async/ticker.py index 9a529ce6..a920279e 100644 --- a/ib_async/ticker.py +++ b/ib_async/ticker.py @@ -287,12 +287,12 @@ def __hash__(self): __str__ = dataclassRepr def _on_ticker_data(self, tick_data: TickDataType, last_time: datetime): - _logger.debug( - "Ticker %s. Received tick data: %s, %s", - self.contract.symbol, - tick_data, - last_time, - ) + # _logger.debug( + # "Ticker %s. Received tick data: %s, %s", + # self.contract.symbol, + # tick_data, + # last_time, + # ) if isinstance(tick_data, TickPriceData): self._on_price_size_tick(tick_data, last_time) elif isinstance(tick_data, TickSizeData): From c6787eade7084bcb3376ace6ac8ac89f32993a27 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Mon, 10 Nov 2025 21:04:10 +0100 Subject: [PATCH 04/48] Add IneligibilityReason dataclass and add historical schedule - Introduced IneligibilityReason dataclass for better data structure. - Updated ContractDetails to use a list of IneligibilityReason. - historical schedule. - Improved market data converters to handle edge cases. - Cleaned up unused imports and methods across various modules. --- ib_async/contract.py | 6 +- ib_async/decoder.py | 92 +++++-------------- ib_async/ib.py | 10 +- ib_async/message.py | 4 +- ib_async/objects.py | 5 - .../contract_converters.py | 15 +-- .../historical_data_converters.py | 89 ++++++++++++++---- .../market_data_converters.py | 2 +- ib_async/ticker.py | 2 +- ib_async/wrapper.py | 19 +--- 10 files changed, 119 insertions(+), 125 deletions(-) diff --git a/ib_async/contract.py b/ib_async/contract.py index 550e9a5e..3203f847 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -25,6 +25,10 @@ class FundDistributionPolicyIndicator(Enum): AccumulationFund = ("N", "Accumulation Fund") IncomeFund = ("Y", "Income Fund") +@dataclass +class IneligibilityReason: + id_: str = field(default_factory=str) + description: str = field(default_factory=str) @dataclass class Contract: @@ -653,7 +657,7 @@ class ContractDetails: fundBlueSkyTerritories = "" fundDistributionPolicyIndicator = FundDistributionPolicyIndicator.NoneItem fundAssetType = FundAssetType.NoneItem - ineligibilityReasonList = None + ineligibilityReasonList: list[IneligibilityReason] = field(default_factory=list) eventContract1 = "" eventContractDescription1 = "" eventContractDescription2 = "" diff --git a/ib_async/decoder.py b/ib_async/decoder.py index 4c83a241..e5a67fce 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -72,6 +72,8 @@ from .protobuf.HistoricalTicksLast_pb2 import ( HistoricalTicksLast as HistoricalTicksLastProto, ) +from .protobuf.HistoricalSchedule_pb2 import HistoricalSchedule as HistoricalScheduleProto +from .protobuf.HistoricalSession_pb2 import HistoricalSession as HistoricalSessionProto from .protobuf.ManagedAccounts_pb2 import ManagedAccounts as ManagedAccountsProto from .protobuf.MarketRule_pb2 import MarketRule as MarketRuleProto from .protobuf.MarketDataType_pb2 import MarketDataType as MarketDataTypeProto @@ -112,10 +114,11 @@ ) from .protobuf_converters.historical_data_converters import ( createBarDataList, - decodeHistogramDataEntry, - decodeHistoricalTick, - decodeHistoricalTickBidAsk, - decodeHistoricalTickLast, + createHistogramDataEntry, + createHistoricalSchedule, + createHistoricalTick, + createHistoricalTickBidAsk, + createHistoricalTickLast, ) from .protobuf_converters.market_data_converters import ( createTickOptionComputation, @@ -133,7 +136,7 @@ createOrderStatus, createTradeFromOpenOrder, ) -from .util import NO_VALID_ID, UNSET_DOUBLE, UNSET_INTEGER, ZoneInfo, parseIBDatetime +from .util import NO_VALID_ID from .wrapper import Wrapper @@ -237,6 +240,10 @@ class Decoder: "historicalTicksLastProto", ), MessageId.IN.HISTOGRAM_DATA: (HistogramDataProto, "histogramDataProto"), + MessageId.IN.HISTORICAL_SCHEDULE: ( + HistoricalScheduleProto, + "historicalScheduleProto", + ), MessageId.IN.TICK_REQ_PARAMS: (TickReqParamsProto, "tickReqParamsProto"), MessageId.IN.TICK_PRICE: (TickPriceProto, "tickPriceProto"), MessageId.IN.TICK_SIZE: (TickSizeProto, "tickSizeProto"), @@ -413,7 +420,7 @@ def historicalTicksProto(self, msg: HistoricalTicksProto): historicalTicks = [] if msg.historicalTicks: for historicalTickProto in msg.historicalTicks: - historicalTick = decodeHistoricalTick( + historicalTick = createHistoricalTick( historicalTickProto, self.wrapper.defaults.timezone ) historicalTicks.append(historicalTick) @@ -425,7 +432,7 @@ def historicalTicksBidAskProto(self, msg: HistoricalTicksBidAskProto): historicalTicksBidAsk: list[HistoricalTickBidAsk] = [] if msg.historicalTicksBidAsk: for historicalTickProto in msg.historicalTicksBidAsk: - historicalTickBidAsk = decodeHistoricalTickBidAsk( + historicalTickBidAsk = createHistoricalTickBidAsk( historicalTickProto, self.wrapper.defaults.timezone ) historicalTicksBidAsk.append(historicalTickBidAsk) @@ -437,7 +444,7 @@ def historicalTicksLastProto(self, msg: HistoricalTicksLastProto): historicalTicksLast: list[HistoricalTickLast] = [] if msg.historicalTicksLast: for historicalTickProto in msg.historicalTicksLast: - historicalTickLast = decodeHistoricalTickLast( + historicalTickLast = createHistoricalTickLast( historicalTickProto, self.wrapper.defaults.timezone ) historicalTicksLast.append(historicalTickLast) @@ -447,10 +454,15 @@ def histogramDataProto(self, msg: HistogramDataProto): histogram: list[HistogramData] = [] if msg.histogramDataEntries: for histogramDataEntryProto in msg.histogramDataEntries: - histogramEntry = decodeHistogramDataEntry(histogramDataEntryProto) + histogramEntry = createHistogramDataEntry(histogramDataEntryProto) histogram.append(histogramEntry) self.wrapper.histogramData(msg.reqId, histogram) + def historicalScheduleProto(self, msg: HistoricalScheduleProto): + historicalSchedule = createHistoricalSchedule(msg) + self.wrapper.historicalSchedule(msg.reqId, historicalSchedule) + + def marketDataTypeProto(self, msg: MarketDataTypeProto): self.wrapper.marketDataType(msg.reqId, msg.marketDataType) @@ -564,24 +576,6 @@ def bondContractDetails(self, fields): self.parse(c) self.wrapper.bondContractDetails(int(reqId), cd) - def historicalData(self, fields): - _, reqId, startDateStr, endDateStr, numBars, *fields = fields - get = iter(fields).__next__ - - for _ in range(int(numBars)): - bar = BarData( - date=get(), - open=float(get()), - high=float(get()), - low=float(get()), - close=float(get()), - volume=float(get()), - average=float(get()), - barCount=int(get()), - ) - self.wrapper.historicalData(int(reqId), bar) - - self.wrapper.historicalDataEnd(int(reqId), startDateStr, endDateStr) def historicalDataUpdate(self, fields): _, reqId, *fields = fields @@ -634,24 +628,6 @@ def scannerData(self, fields): self.wrapper.scannerDataEnd(int(reqId)) - def tickOptionComputation(self, fields): - _, reqId, tickTypeInt, tickAttrib, *fields = fields - impliedVol, delta, optPrice, pvDividend, gamma, vega, theta, undPrice = fields - - self.wrapper.tickOptionComputation( - int(reqId), - int(tickTypeInt), - int(tickAttrib), - float(impliedVol), - float(delta), - float(optPrice), - float(pvDividend), - float(gamma), - float(vega), - float(theta), - float(undPrice), - ) - def deltaNeutralValidation(self, fields): _, _, reqId, conId, delta, price = fields @@ -660,32 +636,6 @@ def deltaNeutralValidation(self, fields): DeltaNeutralContract(int(conId), float(delta or 0), float(price or 0)), ) - def securityDefinitionOptionParameter(self, fields): - ( - _, - reqId, - exchange, - underlyingConId, - tradingClass, - multiplier, - n, - *fields, - ) = fields - n = int(n) - - expirations = fields[:n] - strikes = [float(field) for field in fields[n + 1 :]] - - self.wrapper.securityDefinitionOptionParameter( - int(reqId), - exchange, - underlyingConId, - tradingClass, - multiplier, - expirations, - strikes, - ) - def softDollarTiers(self, fields): _, reqId, n, *fields = fields get = iter(fields).__next__ diff --git a/ib_async/ib.py b/ib_async/ib.py index ea5896b7..6100786d 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -2538,7 +2538,6 @@ def reqHistoricalScheduleAsync( useRTH: bool = True, ) -> Awaitable[HistoricalSchedule]: reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId, contract) end = util.formatIBDatetime(endDateTime) self.client.reqHistoricalData( reqId, @@ -2552,8 +2551,13 @@ def reqHistoricalScheduleAsync( False, None, ) - - return future + awaitable = ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) + return awaitable def reqHistoricalTicksAsync( self, diff --git a/ib_async/message.py b/ib_async/message.py index ee910bfa..3fb30d91 100644 --- a/ib_async/message.py +++ b/ib_async/message.py @@ -1,10 +1,10 @@ +from dataclasses import dataclass + class MessageId: """ Encapsulates TWS API message ID constants and protocol-related logic. """ - from dataclasses import dataclass - PROTOBUF_MSG_ID = 200 """Protobuf message ID offset""" diff --git a/ib_async/objects.py b/ib_async/objects.py index 391ed987..b59a53df 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -796,8 +796,3 @@ class IBDefaults: # optionally change the timezone used for log history events in objects (no impact on orders or data processing) timezone: tzinfo = timezone.utc - -@dataclass -class IneligibilityReason: - id_: str = field(default_factory=str) - description: str = field(default_factory=str) diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py index 016acded..43e80003 100644 --- a/ib_async/protobuf_converters/contract_converters.py +++ b/ib_async/protobuf_converters/contract_converters.py @@ -7,9 +7,9 @@ ContractDetails, DeltaNeutralContract, FundAssetType, - FundDistributionPolicyIndicator, + FundDistributionPolicyIndicator, IneligibilityReason ) -from ..objects import IneligibilityReason, OptionChain +from ..objects import OptionChain from ..order import Order from ..protobuf.ComboLeg_pb2 import ComboLeg as ComboLegProto from ..protobuf.Contract_pb2 import Contract as ContractProto @@ -199,7 +199,7 @@ def createDeltaNeutralContract( return deltaNeutralContract -def decodeIneligibilityReasonList( +def createIneligibilityReasonList( contractDetailsProto: ContractDetailsProto, ) -> list[IneligibilityReason]: ineligibilityReasonList = [] @@ -228,7 +228,8 @@ def setLastTradeDate( if isBond: contract.maturity = split[0] else: - contract.contract.lastTradeDateOrContractMonth = split[0] + if contract.contract: + contract.contract.lastTradeDateOrContractMonth = split[0] if len(split) > 1: contract.lastTradeTime = split[1] @@ -337,7 +338,7 @@ def createContractDetails( FundAssetType, details.fundAssetType ) - ineligibilityReasonList = decodeIneligibilityReasonList(details) + ineligibilityReasonList = createIneligibilityReasonList(details) if ineligibilityReasonList is not None and ineligibilityReasonList: contractDetails.ineligibilityReasonList = ineligibilityReasonList @@ -493,8 +494,8 @@ def createComboLegProtoList( for i, comboLeg in enumerate(comboLegs): perLegPrice = UNSET_DOUBLE if orderComboLegs and i < len(orderComboLegs): - perLegPrice = orderComboLegs[i].price - comboLegProto = createComboLegProto(comboLeg, perLegPrice) + perLegPrice:float = orderComboLegs[i].price + comboLegProto = createComboLegProto(comboLeg, perLegPrice) if comboLegProto is not None: comboLegProtoList.append(comboLegProto) return comboLegProtoList diff --git a/ib_async/protobuf_converters/historical_data_converters.py b/ib_async/protobuf_converters/historical_data_converters.py index 3d383699..afcde89a 100644 --- a/ib_async/protobuf_converters/historical_data_converters.py +++ b/ib_async/protobuf_converters/historical_data_converters.py @@ -1,30 +1,38 @@ """Historical data protobuf converters""" from datetime import datetime, tzinfo + +from ..contract import Contract, TagValue from ..objects import ( BarData, + HistogramData, + HistoricalSchedule, + HistoricalSession, HistoricalTick, - HistoricalTickLast, HistoricalTickBidAsk, + HistoricalTickLast, TickAttribBidAsk, TickAttribLast, - HistogramData, ) -from ..util import isValidIntValue, parseIBDatetime - -from ..contract import Contract, TagValue from ..protobuf.HeadTimestampRequest_pb2 import ( HeadTimestampRequest as HeadTimestampRequestProto, ) -from ..protobuf.HistoricalDataRequest_pb2 import ( - HistoricalDataRequest as HistoricalDataRequestProto, +from ..protobuf.HistogramDataEntry_pb2 import ( + HistogramDataEntry as HistogramDataEntryProto, +) +from ..protobuf.HistogramDataRequest_pb2 import ( + HistogramDataRequest as HistogramDataRequestProto, ) from ..protobuf.HistoricalDataBar_pb2 import ( HistoricalDataBar as HistoricalDataBarProto, ) -from ..protobuf.HistoricalTicksRequest_pb2 import ( - HistoricalTicksRequest as HistoricalTicksRequestProto, +from ..protobuf.HistoricalDataRequest_pb2 import ( + HistoricalDataRequest as HistoricalDataRequestProto, +) +from ..protobuf.HistoricalSchedule_pb2 import ( + HistoricalSchedule as HistoricalScheduleProto, ) +from ..protobuf.HistoricalSession_pb2 import HistoricalSession as HistoricalSessionProto from ..protobuf.HistoricalTick_pb2 import HistoricalTick as HistoricalTickProto from ..protobuf.HistoricalTickBidAsk_pb2 import ( HistoricalTickBidAsk as HistoricalTickBidAskProto, @@ -32,15 +40,12 @@ from ..protobuf.HistoricalTickLast_pb2 import ( HistoricalTickLast as HistoricalTickLastProto, ) -from ..protobuf.HistogramDataRequest_pb2 import ( - HistogramDataRequest as HistogramDataRequestProto, -) -from ..protobuf.HistogramDataEntry_pb2 import ( - HistogramDataEntry as HistogramDataEntryProto, +from ..protobuf.HistoricalTicksRequest_pb2 import ( + HistoricalTicksRequest as HistoricalTicksRequestProto, ) from ..protobuf.TickAttribBidAsk_pb2 import TickAttribBidAsk as TickAttribBidAskProto from ..protobuf.TickAttribLast_pb2 import TickAttribLast as TickAttribLastProto - +from ..util import NO_VALID_ID, isValidIntValue, parseIBDatetime from .contract_converters import createContractProto @@ -163,7 +168,7 @@ def createHistoricalTicksRequestProto( return historicalTicksRequestProto -def decodeHistoricalTick( +def createHistoricalTick( historicalTickProto: HistoricalTickProto, tz: tzinfo ) -> HistoricalTick: time = datetime.fromtimestamp(historicalTickProto.time, tz) @@ -173,7 +178,7 @@ def decodeHistoricalTick( return historicalTick -def decodeHistoricalTickBidAsk( +def createHistoricalTickBidAsk( historicalTickBidAskProto: HistoricalTickBidAskProto, tz: tzinfo ) -> HistoricalTickBidAsk: time = datetime.fromtimestamp(historicalTickBidAskProto.time, tz) @@ -195,7 +200,7 @@ def decodeHistoricalTickBidAsk( return historicalTickBidAsk -def decodeHistoricalTickLast( +def createHistoricalTickLast( historicalTickLastProto: HistoricalTickLastProto, tz: tzinfo ) -> HistoricalTickLast: time = datetime.fromtimestamp(historicalTickLastProto.time, tz) @@ -233,7 +238,7 @@ def createHistogramDataRequestProto( return histogramDataRequestProto -def decodeHistogramDataEntry( +def createHistogramDataEntry( histogramDataEntryProto: HistogramDataEntryProto, ) -> HistogramData: histogramData = HistogramData() @@ -242,3 +247,49 @@ def decodeHistogramDataEntry( if histogramDataEntryProto.HasField("size"): histogramData.count = int(histogramDataEntryProto.size) return histogramData + + +def createHistoricalSchedule( + historicalScheduleProto: HistoricalScheduleProto, +) -> HistoricalSchedule: + startDateTime = ( + historicalScheduleProto.startDateTime + if historicalScheduleProto.HasField("startDateTime") + else "" + ) + endDateTime = ( + historicalScheduleProto.endDateTime + if historicalScheduleProto.HasField("endDateTime") + else "" + ) + timeZone = ( + historicalScheduleProto.timeZone + if historicalScheduleProto.HasField("timeZone") + else "" + ) + + sessions = [] + if historicalScheduleProto.historicalSessions: + for historicalSessionProto in historicalScheduleProto.historicalSessions: + historicalSession = HistoricalSession() + historicalSession.startDateTime = ( + historicalSessionProto.startDateTime + if historicalSessionProto.HasField("startDateTime") + else "" + ) + historicalSession.endDateTime = ( + historicalSessionProto.endDateTime + if historicalSessionProto.HasField("endDateTime") + else "" + ) + historicalSession.refDate = ( + historicalSessionProto.refDate + if historicalSessionProto.HasField("refDate") + else "" + ) + sessions.append(historicalSession) + + historicalSchedule = HistoricalSchedule( + startDateTime, endDateTime, timeZone, sessions + ) + return historicalSchedule diff --git a/ib_async/protobuf_converters/market_data_converters.py b/ib_async/protobuf_converters/market_data_converters.py index 077de3e7..3fbc9d89 100644 --- a/ib_async/protobuf_converters/market_data_converters.py +++ b/ib_async/protobuf_converters/market_data_converters.py @@ -188,7 +188,7 @@ def createTickOptionComputation(msg: TickOptionComputationProto) -> TickComputat ) tickAttrib = msg.tickAttrib if msg.HasField("tickAttrib") else UNSET_INTEGER impliedVol = msg.impliedVol if msg.HasField("impliedVol") else None - if impliedVol < 0: # -1 is the "not computed" indicator + if impliedVol and impliedVol < 0: # -1 is the "not computed" indicator impliedVol = None delta = msg.delta if msg.HasField("delta") else None if delta == -2: # -2 is the "not computed" indicator diff --git a/ib_async/ticker.py b/ib_async/ticker.py index a920279e..c28f4e93 100644 --- a/ib_async/ticker.py +++ b/ib_async/ticker.py @@ -452,7 +452,7 @@ def _on_tick_string(self, tick_string: TickStringData, last_time: datetime): # only populate if timestamp isn't '0' (we don't want to report "last trade: 20,000 days ago") if timestamp: self.lastTimestamp = datetime.fromtimestamp( - timestamp, self.defaultTimezone + timestamp, self.defaults.timezone ) elif tick_string.tickType == TickType.FUNDAMENTAL_RATIOS: # https://web.archive.org/web/20200725010343/https://interactivebrokers.github.io/tws-api/fundamental_ratios_tags.html diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index c7bcfabe..d8f653c9 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -6,7 +6,7 @@ from collections import defaultdict from dataclasses import dataclass, field from datetime import datetime -from typing import TYPE_CHECKING, Any, Final, TypeAlias, Union, cast +from typing import TYPE_CHECKING, Any, TypeAlias, Union, cast import eventkit as ev @@ -23,12 +23,9 @@ BarDataList, CommissionReport, DepthMktDataDescription, - Dividends, DOMLevel, - Execution, FamilyCode, Fill, - FundamentalRatios, HistogramData, HistoricalNews, HistoricalSchedule, @@ -43,7 +40,6 @@ NewsProvider, NewsTick, OptionChain, - OptionComputation, PnL, PnLSingle, PortfolioItem, @@ -58,7 +54,6 @@ TickByTickBidAsk, TickByTickMidPoint, TickComputationData, - TickDataType, TickGenericData, TickParams, TickPriceData, @@ -67,12 +62,10 @@ TickType, TradeLogEntry, ) -from ib_async.order import Order, OrderState, OrderStatus, Trade +from ib_async.order import Order, OrderStatus, Trade from ib_async.ticker import Ticker from ib_async.util import ( UNSET_DOUBLE, - UNSET_INTEGER, - dataclassAsDict, dataclassUpdate, getLoop, globalErrorEvent, @@ -1049,13 +1042,9 @@ def tickEFP( def historicalSchedule( self, reqId: int, - startDateTime: str, - endDateTime: str, - timeZone: str, - sessions: list[HistoricalSession], + schedule:HistoricalSchedule, ): - schedule = HistoricalSchedule(startDateTime, endDateTime, timeZone, sessions) - self._endReq(reqId, schedule) + self.response_bus.emit(reqId, schedule) def wshMetaData(self, reqId: int, dataJson: str): self.ib.wshMetaEvent.emit(dataJson) From f82026a3c2db9777878d2dc01eea18cff0b501c2 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:41:30 +0100 Subject: [PATCH 05/48] Changes - BiDict class to encapsulate wrapper state management - reactive bus implemented: ticker_bus, response_bus, subscription_bus - object logic encapsulated on the object itself, rather than in Wrapper class - all subscriptions are implemented. realtimeBar, , keepUpToDate, scanners, PnL and PnLSingle - placeOrder,cancelOrder, whatIfOrder - Ticker and tickByTick - Implement tests for subscription converters including ScannerParametersRequest, ScannerSubscriptionRequest, and PnL requests. - Add tests for trade converters covering order creation, conditions, and execution details. - Ensure comprehensive coverage for various order conditions and soft dollar tiers. - Validate the conversion of protobuf messages to domain objects and vice versa. - Most of methods are implemented, except for news related and market L2. --- ib_async/client.py | 768 +++++---------- ib_async/contract.py | 2 +- ib_async/decoder.py | 381 ++++---- ib_async/ib.py | 209 ++-- ib_async/message.py | 364 ++++--- ib_async/objects.py | 231 +++-- ib_async/order.py | 59 +- ib_async/protobuf_converters/__init__.py | 0 .../protobuf_converters/account_converters.py | 18 +- .../contract_converters.py | 50 +- .../historical_data_converters.py | 178 +++- .../market_data_converters.py | 194 ++-- .../subscription_converters.py | 193 ++++ .../protobuf_converters/trade_converter.py | 609 +++++++++++- ib_async/ticker.py | 92 +- ib_async/util.py | 17 +- ib_async/wrapper.py | 871 ++++++++++------- tests/conftest.py | 13 + tests/test_account_converters.py | 188 ++++ tests/test_bidict.py | 157 +++ tests/test_contract_converters.py | 267 +++++ tests/test_contract_requests.py | 179 ++++ tests/test_historical_data_converters.py | 324 +++++++ tests/test_market_data_converters.py | 177 ++++ tests/test_subscription_converters.py | 169 ++++ tests/test_trade_converter.py | 911 ++++++++++++++++++ 26 files changed, 4963 insertions(+), 1658 deletions(-) create mode 100644 ib_async/protobuf_converters/__init__.py create mode 100644 ib_async/protobuf_converters/subscription_converters.py create mode 100644 tests/test_account_converters.py create mode 100644 tests/test_bidict.py create mode 100644 tests/test_contract_converters.py create mode 100644 tests/test_contract_requests.py create mode 100644 tests/test_historical_data_converters.py create mode 100644 tests/test_market_data_converters.py create mode 100644 tests/test_subscription_converters.py create mode 100644 tests/test_trade_converter.py diff --git a/ib_async/client.py b/ib_async/client.py index 326738b0..d8aa0f13 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -1,19 +1,20 @@ """Socket client for communicating with Interactive Brokers.""" import asyncio -import io import logging import math import struct import time from collections import deque -from typing import Any, Callable, Deque, List, Optional +from typing import Deque, List, Optional from eventkit import Event from google.protobuf.message import Message +from ib_async.contract import Contract +from ib_async.order import Order, OrderCancel + from .connection import Connection -from .contract import Contract from .decoder import Decoder from .message import MessageId from .objects import ConnectionStats, WshEventData @@ -29,19 +30,25 @@ from .protobuf.CancelAccountSummary_pb2 import ( CancelAccountSummary as CancelAccountSummaryProto, ) -from .protobuf.CancelOrderRequest_pb2 import ( - CancelOrderRequest as CancelOrderRequestProto, +from .protobuf.CancelFundamentalsData_pb2 import ( + CancelFundamentalsData as CancelFundamentalsDataProto, ) -from .protobuf.CancelPositions_pb2 import CancelPositions as CancelPositionsProto from .protobuf.CancelHeadTimestamp_pb2 import ( CancelHeadTimestamp as CancelHeadTimestampProto, ) -from .protobuf.CompletedOrdersRequest_pb2 import ( - CompletedOrdersRequest as CompletedOrdersRequestProto, -) from .protobuf.CancelHistoricalData_pb2 import ( CancelHistoricalData as CancelHistoricalDataProto, ) +from .protobuf.CancelPositions_pb2 import CancelPositions as CancelPositionsProto +from .protobuf.CancelRealTimeBars_pb2 import ( + CancelRealTimeBars as CancelRealTimeBarsProto, +) +from .protobuf.CancelTickByTick_pb2 import ( + CancelTickByTick as CancelTickByTickProto, +) +from .protobuf.CompletedOrdersRequest_pb2 import ( + CompletedOrdersRequest as CompletedOrdersRequestProto, +) from .protobuf.ContractDataRequest_pb2 import ( ContractDataRequest as ContractDataRequestProto, ) @@ -56,33 +63,57 @@ ManagedAccountsRequest as ManagedAccountsRequestProto, ) from .protobuf.OpenOrdersRequest_pb2 import OpenOrdersRequest as OpenOrdersRequestProto - from .protobuf.PositionsRequest_pb2 import PositionsRequest as PositionsRequestProto from .protobuf.StartApiRequest_pb2 import StartApiRequest as StartApiRequestProto from .protobuf_converters.account_converters import ( createAccountDataRequestProto, createAccountMultiRequestProto, createCancelAccMultiRequestProto, + createUserInfoRequestProto, ) from .protobuf_converters.contract_converters import ( createContractProto, createMarketRuleRequestProto, createMatchingSymbolsRequestProto, createSecDefOptParamsRequestProto, + createSmartComponentsRequestProto, ) from .protobuf_converters.historical_data_converters import ( + createCancelHistoricalDataProto, + createFundamentalsDataRequestProto, createHeadTimestampRequestProto, createHistogramDataRequestProto, createHistoricalDataRequestProto, createHistoricalTicksRequestProto, + createRealTimeBarsRequestProto, ) -from .protobuf_converters.trade_converter import createExecutionRequestProto from .protobuf_converters.market_data_converters import ( cancelMarketDataProto, + createCalculateImpliedVolatilityRequestProto, + createCalculateOptionPriceRequestProto, + createCancelCalculateImpliedVolatilityProto, + createCancelCalculateOptionPriceProto, createMarketDataRequestProto, createMarketDataTypeRequestProto, + createTickByTickRequestProto, +) +from .protobuf_converters.subscription_converters import ( + createCancelPnLProto, + createCancelScannerSubscriptionProto, + createPnLRequestProto, + createPnLSingleRequestProto, + createScannerSubscriptionRequestProto, + createScannerParametersRequestProto, +) +from .protobuf_converters.trade_converter import ( + createCancelOrderRequestProto, + createExecutionRequestProto, + createExerciseOptionsRequestProto, + createGlobalCancelRequestProto, + createPlaceOrderRequestProto, ) -from .util import UNSET_DOUBLE, UNSET_INTEGER, dataclassAsTuple, getLoop, run +from .util import UNSET_DOUBLE, dataclassAsTuple, getLoop, run + class Client: @@ -195,7 +226,7 @@ def reset(self): self._numBytesRecv = 0 self._numMsgRecv = 0 self._isThrottling = False - self._msgQ: Deque[str] = deque() + self._msgQ: Deque[bytes] = deque() self._timeQ: Deque[float] = deque() def serverVersion(self) -> int: @@ -243,6 +274,49 @@ def updateReqId(self, minReqId): """Update the next reqId to be at least ``minReqId``.""" self._reqIdSeq = max(self._reqIdSeq, minReqId) + def sendProto(self, msgId: int, proto: Message): + """Serialize and send the given protobuf message.""" + if not self.isConnected(): + raise ConnectionError("Not connected") + body = proto.SerializeToString() + header = msgId.to_bytes(4, "big") + msg = header + body + self.sendMsg(msg) + + def sendMsg(self, msg: bytes): + loop = getLoop() + t = loop.time() + times = self._timeQ + msgs = self._msgQ + while times and t - times[0] > self.RequestsInterval: + times.popleft() + + if msg: + msgs.append(msg) + + while msgs and (len(times) < self.MaxRequests or not self.MaxRequests): + msg = msgs.popleft() + self.conn.sendMsg(self._prefix(msg)) + times.append(t) + if self._logger.isEnabledFor(logging.DEBUG): + self._logger.debug( + ">>> id %s: %s", + MessageId.OUT(int.from_bytes(msg[:4], "big")).name, + msg, + ) + + if msgs: + if not self._isThrottling: + self._isThrottling = True + self.throttleStart.emit() + self._logger.info("Started to throttle requests") + loop.call_at(times[0] + self.RequestsInterval, self.sendMsg, None) + else: + if self._isThrottling: + self._isThrottling = False + self.throttleEnd.emit() + self._logger.info("Stopped to throttle requests") + def getAccounts(self) -> List[str]: """Get the list of account names that are under management.""" if not self.isReady(): @@ -343,6 +417,7 @@ def _onSocketHasData(self, data): self._numMsgRecv += 1 if not self._serverVersion: + # connection handshake # First message is always the legacy server version string fields = payload.decode(errors="backslashreplace").split("\0") fields.pop() @@ -367,12 +442,17 @@ def _onSocketHasData(self, data): self.startApi() self._logger.info("Logged on to server version %s", self._serverVersion) else: + # we are connected if not self._apiReady: if self._hasReqId and self._accounts: self._apiReady = True self.apiStart.emit() if debug: - self._logger.debug("<<< %r", payload) + self._logger.debug( + "<<< id %s: %r", + MessageId.IN(int.from_bytes(payload[:4], "big")).name, + payload, + ) # After handshake, all incoming messages are treated as Protobuf # The entire payload (ID + data) is passed to the decoder self.decoder.processProtoBuf(payload) @@ -422,287 +502,47 @@ def reqMktData( regulatorySnapshot, mktDataOptions, ) - self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_MKT_DATA), mktDataRequestProto - ) + self.sendProto(MessageId.OUT.REQ_MKT_DATA, mktDataRequestProto) def cancelMktData(self, reqId): self.sendProto( - MessageId.to_protobuf(MessageId.OUT.CANCEL_MKT_DATA), + MessageId.OUT.CANCEL_MKT_DATA, cancelMarketDataProto(reqId), ) - def placeOrder(self, orderId, contract, order): - version = self.serverVersion() - - # IBKR API BUG FIX: - # IBKR sometimes back-populates the 'volatility' field into live orders, but then if we try to - # modify an order using cached order objects, IBKR rejects modifications because 'volatility' - # is not allowed to be set (even though _they_ added it to our previously submitted order). - # Solution: if an order is NOT a VOL order, delete the 'volatility' value to prevent this error. - if not order.orderType.startswith("VOL"): - # ONLY volatility orders can have 'volatility' set when sending API data. - order.volatility = None - - # The IBKR API protocol is just a series of in-order arguments denoted by position. - # The upstream API parses all fields based on the first value (the message type). - fields = [ - 3, # PLACE_ORDER message type - orderId, - contract, - contract.secIdType, - contract.secId, - order.action, - order.totalQuantity, - order.orderType, - order.lmtPrice, - order.auxPrice, - order.tif, - order.ocaGroup, - order.account, - order.openClose, - order.origin, - order.orderRef, - order.transmit, - order.parentId, - order.blockOrder, - order.sweepToFill, - order.displaySize, - order.triggerMethod, - order.outsideRth, - order.hidden, - ] - - if contract.secType == "BAG": - legs = contract.comboLegs or [] - fields += [len(legs)] - for leg in legs: - fields += [ - leg.conId, - leg.ratio, - leg.action, - leg.exchange, - leg.openClose, - leg.shortSaleSlot, - leg.designatedLocation, - leg.exemptCode, - ] - - legs = order.orderComboLegs or [] - fields += [len(legs)] - for leg in legs: - fields += [leg.price] - - params = order.smartComboRoutingParams or [] - fields += [len(params)] - for param in params: - fields += [param.tag, param.value] - - fields += [ - "", - order.discretionaryAmt, - order.goodAfterTime, - order.goodTillDate, - order.faGroup, - order.faMethod, - order.faPercentage, - ] - - if version < 177: - fields += [order.faProfile] - - fields += [ - order.modelCode, - order.shortSaleSlot, - order.designatedLocation, - order.exemptCode, - order.ocaType, - order.rule80A, - order.settlingFirm, - order.allOrNone, - order.minQty, - order.percentOffset, - order.eTradeOnly, # always False - order.firmQuoteOnly, # always False - order.nbboPriceCap, # always UNSET - order.auctionStrategy, - order.startingPrice, - order.stockRefPrice, - order.delta, - order.stockRangeLower, - order.stockRangeUpper, - order.overridePercentageConstraints, - order.volatility, - order.volatilityType, - order.deltaNeutralOrderType, - order.deltaNeutralAuxPrice, - ] - - if order.deltaNeutralOrderType: - fields += [ - order.deltaNeutralConId, - order.deltaNeutralSettlingFirm, - order.deltaNeutralClearingAccount, - order.deltaNeutralClearingIntent, - order.deltaNeutralOpenClose, - order.deltaNeutralShortSale, - order.deltaNeutralShortSaleSlot, - order.deltaNeutralDesignatedLocation, - ] - - fields += [ - order.continuousUpdate, - order.referencePriceType, - order.trailStopPrice, - order.trailingPercent, - order.scaleInitLevelSize, - order.scaleSubsLevelSize, - order.scalePriceIncrement, - ] - - if 0 < order.scalePriceIncrement < UNSET_DOUBLE: - fields += [ - order.scalePriceAdjustValue, - order.scalePriceAdjustInterval, - order.scaleProfitOffset, - order.scaleAutoReset, - order.scaleInitPosition, - order.scaleInitFillQty, - order.scaleRandomPercent, - ] - - fields += [ - order.scaleTable, - order.activeStartTime, - order.activeStopTime, - order.hedgeType, - ] - - if order.hedgeType: - fields += [order.hedgeParam] - - fields += [ - order.optOutSmartRouting, - order.clearingAccount, - order.clearingIntent, - order.notHeld, - ] - - dnc = contract.deltaNeutralContract - if dnc: - fields += [True, dnc.conId, dnc.delta, dnc.price] - else: - fields += [False] - - fields += [order.algoStrategy] - if order.algoStrategy: - params = order.algoParams or [] - fields += [len(params)] - for param in params: - fields += [param.tag, param.value] - - fields += [ - order.algoId, - order.whatIf, - order.orderMiscOptions, - order.solicited, - order.randomizeSize, - order.randomizePrice, - ] - - if order.orderType in {"PEG BENCH", "PEGBENCH"}: - fields += [ - order.referenceContractId, - order.isPeggedChangeAmountDecrease, - order.peggedChangeAmount, - order.referenceChangeAmount, - order.referenceExchangeId, - ] - - fields += [len(order.conditions)] - if order.conditions: - for cond in order.conditions: - fields += dataclassAsTuple(cond) - fields += [order.conditionsIgnoreRth, order.conditionsCancelOrder] - - fields += [ - order.adjustedOrderType, - order.triggerPrice, - order.lmtPriceOffset, - order.adjustedStopPrice, - order.adjustedStopLimitPrice, - order.adjustedTrailingAmount, - order.adjustableTrailingUnit, - order.extOperator, - order.softDollarTier.name, - order.softDollarTier.val, - order.cashQty, - order.mifid2DecisionMaker, - order.mifid2DecisionAlgo, - order.mifid2ExecutionTrader, - order.mifid2ExecutionAlgo, - order.dontUseAutoPriceForHedge, - order.isOmsContainer, - order.discretionaryUpToLimitPrice, - order.usePriceMgmtAlgo, - ] - - if version >= 158: - fields += [order.duration] - - if version >= 160: - fields += [order.postToAts] - - if version >= 162: - fields += [order.autoCancelParent] - - if version >= 166: - fields += [order.advancedErrorOverride] - - if version >= 169: - fields += [order.manualOrderTime] - - if version >= 170: - if contract.exchange == "IBKRATS": - fields += [order.minTradeQty] - if order.orderType in {"PEG BEST", "PEGBEST"}: - fields += [order.minCompeteSize, order.competeAgainstBestOffset] - if order.competeAgainstBestOffset == math.inf: - fields += [order.midOffsetAtWhole, order.midOffsetAtHalf] - elif order.orderType in {"PEG MID", "PEGMID"}: - fields += [order.midOffsetAtWhole, order.midOffsetAtHalf] - - self.send(*fields) + def placeOrder(self,orderId:int, contract: Contract, order: Order): + orderRequestProto = createPlaceOrderRequestProto( + orderId, contract, order + ) + self.sendProto(MessageId.OUT.PLACE_ORDER, orderRequestProto) - def cancelOrder(self, orderId, manualCancelOrderTime=""): - fields = [4, 1, orderId] - if self.serverVersion() >= 169: - fields += [manualCancelOrderTime] - self.send(*fields) + def cancelOrder(self, orderId: int, orderCancel: OrderCancel): + self.sendProto( + MessageId.OUT.CANCEL_ORDER, + createCancelOrderRequestProto(orderId, orderCancel), + ) def reqOpenOrders(self): openOrdersRequestProto = OpenOrdersRequestProto() self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_OPEN_ORDERS), + MessageId.OUT.REQ_OPEN_ORDERS, openOrdersRequestProto, ) def reqAccountUpdates(self, subscribe, acctCode): proto = createAccountDataRequestProto(subscribe, acctCode) - self.sendProto(MessageId.to_protobuf(MessageId.OUT.REQ_ACCT_DATA), proto) + self.sendProto(MessageId.OUT.REQ_ACCT_DATA, proto) def reqExecutions(self, reqId, execFilter): executionRequestProto = createExecutionRequestProto(reqId, execFilter) - self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_EXECUTIONS), executionRequestProto - ) + self.sendProto(MessageId.OUT.REQ_EXECUTIONS, executionRequestProto) def reqIds(self, numIds: int): """Request a new request ID.""" self._logger.debug("Calling reqIds (Protobuf)") proto = IdsRequestProto() proto.numIds = numIds - self.sendProto(MessageId.to_protobuf(MessageId.OUT.REQ_IDS), proto) + self.sendProto(MessageId.OUT.REQ_IDS, proto) def reqContractDetails(self, reqId, contract): contractDetailsRequestProto = ContractDataRequestProto() @@ -710,7 +550,7 @@ def reqContractDetails(self, reqId, contract): contractProto = createContractProto(contract, None) contractDetailsRequestProto.contract.CopyFrom(contractProto) self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_CONTRACT_DATA), + MessageId.OUT.REQ_CONTRACT_DATA, contractDetailsRequestProto, ) @@ -730,128 +570,6 @@ def reqContractDetailsLegacy(self, reqId, contract): self.send(*fields) - def sendProto(self, msgId: int, proto: Message): - # TODO implement throtling - body = proto.SerializeToString() - header = msgId.to_bytes(4, "big") - msg = header + body - self.conn.sendMsg(self._prefix(msg)) - if self._logger.isEnabledFor(logging.DEBUG): - self._logger.debug( - "Sending Protobuf message: msgId=%s, proto=%r, body=%r", - msgId, - proto, - body, - ) - - def send(self, *fields, makeEmpty=True): - """Serialize and send the given fields using the IB socket protocol. - - if 'makeEmpty' is True (default), then the IBKR values representing "no value" - become the empty string.""" - if not self.isConnected(): - raise ConnectionError("Not connected") - - # fmt: off - FORMAT_HANDLERS: dict[Any, Callable[[Any], str]] = { - # Contracts are formatted in IBKR null delimiter format - Contract: lambda c: "\0".join([ - str(f) - for f in ( - c.conId, - c.symbol, - c.secType, - c.lastTradeDateOrContractMonth, - c.strike, - c.right, - c.multiplier, - c.exchange, - c.primaryExchange, - c.currency, - c.localSymbol, - c.tradingClass, - ) - ]), - - # Float conversion has 3 stages: - # - Convert 'IBKR unset' double to empty (if requested) - # - Convert infinity to 'Infinite' string (if appropriate) - # - else, convert float to string normally - float: lambda f: "" - if (makeEmpty and f == UNSET_DOUBLE) - else ("Infinite" if (f == math.inf) else str(f)), - - # Int conversion has 2 stages: - # - Convert 'IBKR unset' to empty (if requested) - # - else, convert int to string normally - int: lambda f: "" if makeEmpty and f == UNSET_INTEGER else str(f), - - # None is always just an empty string. - # (due to a quirk of Python, 'type(None)' is how you properly generate the NoneType value) - type(None): lambda _: "", - - # Strings are always strings - str: lambda s: s, - - # Bools become strings "1" or "0" - bool: lambda b: "1" if b else "0", - - # Lists of tags become semicolon-appended KV pairs - list: lambda lst: "".join([f"{v.tag}={v.value};" for v in lst]), - } - # fmt: on - - # start of new message - msg = io.StringIO() - - for field in fields: - # Fetch type converter for this field (falls back to 'str(field)' as a default for unmatched types) - # (extra `isinstance()` wrapper needed here because Contract subclasses are their own type, but we want - # to only match against the Contract parent class for formatting operations) - convert = FORMAT_HANDLERS.get( - Contract if isinstance(field, Contract) else type(field), str - ) - - # Convert field to IBKR protocol string part - s = convert(field) - - # Append converted IBKR protocol string to message buffer - msg.write(s) - msg.write("\0") - - generated = msg.getvalue() - self.sendMsg(generated) - - def sendMsg(self, msg: str): - loop = getLoop() - t = loop.time() - times = self._timeQ - msgs = self._msgQ - while times and t - times[0] > self.RequestsInterval: - times.popleft() - - if msg: - msgs.append(msg) - - while msgs and (len(times) < self.MaxRequests or not self.MaxRequests): - msg = msgs.popleft() - self.conn.sendMsg(self._prefix(msg.encode())) - times.append(t) - if self._logger.isEnabledFor(logging.DEBUG): - self._logger.debug(">>> %s", msg[:-1].replace("\0", ",")) - - if msgs: - if not self._isThrottling: - self._isThrottling = True - self.throttleStart.emit() - self._logger.debug("Started to throttle requests") - loop.call_at(times[0] + self.RequestsInterval, self.sendMsg, None) - else: - if self._isThrottling: - self._isThrottling = False - self.throttleEnd.emit() - self._logger.debug("Stopped to throttle requests") - def reqMktDepth(self, reqId, contract, numRows, isSmartDepth, mktDepthOptions): self.send( 10, @@ -890,21 +608,21 @@ def reqAutoOpenOrders(self, bAutoBind: bool): autoOpenOrdersRequestProto = AutoOpenOrdersRequestProto() autoOpenOrdersRequestProto.autoBind = bAutoBind self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_AUTO_OPEN_ORDERS), + MessageId.OUT.REQ_AUTO_OPEN_ORDERS, autoOpenOrdersRequestProto, ) def reqAllOpenOrders(self): allOpenOrdersRequestProto = AllOpenOrdersRequestProto() self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_ALL_OPEN_ORDERS), + MessageId.OUT.REQ_ALL_OPEN_ORDERS, allOpenOrdersRequestProto, ) def reqManagedAccts(self): self._logger.debug("Calling reqManagedAccts (Protobuf)") proto = ManagedAccountsRequestProto() - self.sendProto(MessageId.to_protobuf(MessageId.OUT.REQ_MANAGED_ACCTS), proto) + self.sendProto(MessageId.OUT.REQ_MANAGED_ACCTS, proto) def requestFA(self, faData): self.send(18, 1, faData) @@ -938,40 +656,22 @@ def reqHistoricalData( chartOptions, ) - # if contract.secType == "BAG": - # legs = contract.comboLegs or [] - # fields += [len(legs)] - # for leg in legs: - # fields += [leg.conId, leg.ratio, leg.action, leg.exchange] self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_HISTORICAL_DATA), + MessageId.OUT.REQ_HISTORICAL_DATA, historicalDataRequestProto, ) def exerciseOptions( self, reqId, contract, exerciseAction, exerciseQuantity, account, override ): - self.send( - 21, - 2, - reqId, - contract.conId, - contract.symbol, - contract.secType, - contract.lastTradeDateOrContractMonth, - contract.strike, - contract.right, - contract.multiplier, - contract.exchange, - contract.currency, - contract.localSymbol, - contract.tradingClass, - exerciseAction, - exerciseQuantity, - account, - override, + self.sendProto( + MessageId.OUT.EXERCISE_OPTIONS, + createExerciseOptionsRequestProto( + reqId,contract, exerciseAction, exerciseQuantity, account, override + ) ) + def reqScannerSubscription( self, reqId, @@ -979,155 +679,139 @@ def reqScannerSubscription( scannerSubscriptionOptions, scannerSubscriptionFilterOptions, ): - sub = subscription - self.send( - 22, - reqId, - sub.numberOfRows, - sub.instrument, - sub.locationCode, - sub.scanCode, - sub.abovePrice, - sub.belowPrice, - sub.aboveVolume, - sub.marketCapAbove, - sub.marketCapBelow, - sub.moodyRatingAbove, - sub.moodyRatingBelow, - sub.spRatingAbove, - sub.spRatingBelow, - sub.maturityDateAbove, - sub.maturityDateBelow, - sub.couponRateAbove, - sub.couponRateBelow, - sub.excludeConvertible, - sub.averageOptionVolumeAbove, - sub.scannerSettingPairs, - sub.stockTypeFilter, - scannerSubscriptionFilterOptions, - scannerSubscriptionOptions, + self.sendProto( + MessageId.OUT.REQ_SCANNER_SUBSCRIPTION, + createScannerSubscriptionRequestProto( + reqId, + subscription, + scannerSubscriptionOptions, + scannerSubscriptionFilterOptions, + ), ) def cancelScannerSubscription(self, reqId): - self.send(23, 1, reqId) + self.sendProto( + MessageId.OUT.CANCEL_SCANNER_SUBSCRIPTION, + createCancelScannerSubscriptionProto(reqId), + ) def reqScannerParameters(self): - self.send(24, 1) + self.sendProto( + MessageId.OUT.REQ_SCANNER_PARAMETERS, createScannerParametersRequestProto() + ) def cancelHistoricalData(self, reqId): cancelRequest = CancelHistoricalDataProto() cancelRequest.reqId = reqId - self.sendProto( - MessageId.to_protobuf(MessageId.OUT.CANCEL_HISTORICAL_DATA), cancelRequest - ) + self.sendProto(MessageId.OUT.CANCEL_HISTORICAL_DATA, cancelRequest) def reqCurrentTime(self): currentTimeRequestProto = CurrentTimeRequestProto() self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_CURRENT_TIME), + MessageId.OUT.REQ_CURRENT_TIME, currentTimeRequestProto, ) def reqCurrentTimeMili(self): currentTimeMiliRequestProto = CurrentTimeInMillisRequestProto() self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_CURRENT_TIME_IN_MILLIS), + MessageId.OUT.REQ_CURRENT_TIME_IN_MILLIS, currentTimeMiliRequestProto, ) def reqRealTimeBars( self, reqId, contract, barSize, whatToShow, useRTH, realTimeBarsOptions ): - self.send( - 50, 3, reqId, contract, barSize, whatToShow, useRTH, realTimeBarsOptions + realTimeBarsRequestProto = createRealTimeBarsRequestProto( + reqId, contract, barSize, whatToShow, useRTH, realTimeBarsOptions + ) + self.sendProto( + MessageId.OUT.REQ_REAL_TIME_BARS, + realTimeBarsRequestProto, ) def cancelRealTimeBars(self, reqId): - self.send(51, 1, reqId) + self.sendProto( + MessageId.OUT.CANCEL_REAL_TIME_BARS, + CancelRealTimeBarsProto(reqId=reqId), + ) def reqFundamentalData(self, reqId, contract, reportType, fundamentalDataOptions): - options = fundamentalDataOptions or [] - self.send( - 52, - 2, - reqId, - contract.conId, - contract.symbol, - contract.secType, - contract.exchange, - contract.primaryExchange, - contract.currency, - contract.localSymbol, - reportType, - len(options), - options, + fundamentalsDataRequestProto = createFundamentalsDataRequestProto( + reqId, contract, reportType, fundamentalDataOptions + ) + self.sendProto( + MessageId.OUT.REQ_FUNDAMENTAL_DATA, + fundamentalsDataRequestProto, ) def cancelFundamentalData(self, reqId): - self.send(53, 1, reqId) + cancelFundamentalProto = CancelFundamentalsDataProto() + cancelFundamentalProto.reqId = reqId + self.sendProto( + MessageId.OUT.CANCEL_FUNDAMENTAL_DATA, + cancelFundamentalProto, + ) def calculateImpliedVolatility( self, reqId, contract, optionPrice, underPrice, implVolOptions ): - self.send( - 54, - 3, - reqId, - contract, - optionPrice, - underPrice, - len(implVolOptions), - implVolOptions, + iv_request = createCalculateImpliedVolatilityRequestProto( + reqId, contract, optionPrice, underPrice, implVolOptions + ) + self.sendProto( + MessageId.OUT.REQ_CALC_IMPLIED_VOLAT, + iv_request, ) def calculateOptionPrice( self, reqId, contract, volatility, underPrice, optPrcOptions ): - self.send( - 55, - 3, - reqId, - contract, - volatility, - underPrice, - len(optPrcOptions), - optPrcOptions, + opt_price = createCalculateOptionPriceRequestProto( + reqId, contract, volatility, underPrice, optPrcOptions ) + self.sendProto(MessageId.OUT.REQ_CALC_OPTION_PRICE, opt_price) def cancelCalculateImpliedVolatility(self, reqId): - self.send(56, 1, reqId) + self.sendProto( + MessageId.OUT.CANCEL_CALC_IMPLIED_VOLAT, + createCancelCalculateImpliedVolatilityProto(reqId=reqId), + ) def cancelCalculateOptionPrice(self, reqId): - self.send(57, 1, reqId) + self.sendProto( + MessageId.OUT.CANCEL_CALC_OPTION_PRICE, + createCancelCalculateOptionPriceProto(reqId=reqId), + ) - def reqGlobalCancel(self): - self.send(58, 1) + def reqGlobalCancel(self, orderCancel: OrderCancel): + self.sendProto( + MessageId.OUT.REQ_GLOBAL_CANCEL, createGlobalCancelRequestProto(orderCancel) + ) def reqMarketDataType(self, marketDataType): self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_MARKET_DATA_TYPE), + MessageId.OUT.REQ_MARKET_DATA_TYPE, createMarketDataTypeRequestProto(marketDataType=marketDataType), ) def reqPositions(self): - self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_POSITIONS), PositionsRequestProto() - ) + self.sendProto(MessageId.OUT.REQ_POSITIONS, PositionsRequestProto()) def cancelPositions(self): self.sendProto( - MessageId.to_protobuf(MessageId.OUT.CANCEL_POSITIONS), + MessageId.OUT.CANCEL_POSITIONS, CancelPositionsProto(), ) def reqAccountSummary(self, reqId, groupName, tags): proto = AccountSummaryRequestProto(reqId=reqId, group=groupName, tags=tags) - self.sendProto(MessageId.to_protobuf(MessageId.OUT.REQ_ACCOUNT_SUMMARY), proto) + self.sendProto(MessageId.OUT.REQ_ACCOUNT_SUMMARY, proto) def cancelAccountSummary(self, reqId): cancelAccountSummaryProto = CancelAccountSummaryProto(reqId=reqId) self.sendProto( - MessageId.to_protobuf(MessageId.OUT.CANCEL_ACCOUNT_SUMMARY), + MessageId.OUT.CANCEL_ACCOUNT_SUMMARY, cancelAccountSummaryProto, ) @@ -1155,9 +839,7 @@ def startApi(self): startApiRequestProto.clientId = self.clientId if self.optCapab: # Only set if not an empty string startApiRequestProto.optionalCapabilities = self.optCapab - self.sendProto( - MessageId.to_protobuf(MessageId.OUT.START_API), startApiRequestProto - ) + self.sendProto(MessageId.OUT.START_API, startApiRequestProto) def verifyAndAuthRequest(self, apiName, apiVersion, opaqueIsvKey): self.send(72, 1, apiName, apiVersion, opaqueIsvKey) @@ -1178,13 +860,13 @@ def reqAccountUpdatesMulti( reqId, account, modelCode, ledgerAndNLV ) self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_ACCOUNT_UPDATES_MULTI), + MessageId.OUT.REQ_ACCOUNT_UPDATES_MULTI, reqAccUpdatesMultiProto, ) def cancelAccountUpdatesMulti(self, reqId): self.sendProto( - MessageId.to_protobuf(MessageId.OUT.CANCEL_ACCOUNT_UPDATES_MULTI), + MessageId.OUT.CANCEL_ACCOUNT_UPDATES_MULTI, createCancelAccMultiRequestProto(reqId), ) @@ -1204,7 +886,7 @@ def reqSecDefOptParams( underlyingConId, ) self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_SEC_DEF_OPT_PARAMS), + MessageId.OUT.REQ_SEC_DEF_OPT_PARAMS, secDefOptParamsRequestProto, ) @@ -1217,7 +899,7 @@ def reqFamilyCodes(self): def reqMatchingSymbols(self, reqId, pattern): matchingSymbolsRequestProto = createMatchingSymbolsRequestProto(reqId, pattern) self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_MATCHING_SYMBOLS), + MessageId.OUT.REQ_MATCHING_SYMBOLS, matchingSymbolsRequestProto, ) @@ -1225,7 +907,10 @@ def reqMktDepthExchanges(self): self.send(82) def reqSmartComponents(self, reqId, bboExchange): - self.send(83, reqId, bboExchange) + self.sendProto( + MessageId.OUT.REQ_SMART_COMPONENTS, + createSmartComponentsRequestProto(reqId, bboExchange), + ) def reqNewsArticle(self, reqId, providerCode, articleId, newsArticleOptions): self.send(84, reqId, providerCode, articleId, newsArticleOptions) @@ -1259,7 +944,7 @@ def reqHeadTimeStamp(self, reqId, contract, whatToShow, useRTH, formatDate): reqId, contract, whatToShow, useRTH != 0, formatDate ) self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_HEAD_TIMESTAMP), + MessageId.OUT.REQ_HEAD_TIMESTAMP, headTimestampRequestProto, ) @@ -1267,36 +952,50 @@ def cancelHeadTimeStamp(self, reqId): cancelHeadTimestampRequestProto = CancelHeadTimestampProto() cancelHeadTimestampRequestProto.reqId = reqId self.sendProto( - MessageId.to_protobuf(MessageId.OUT.CANCEL_HEAD_TIMESTAMP), + MessageId.OUT.CANCEL_HEAD_TIMESTAMP, cancelHeadTimestampRequestProto, ) def reqHistogramData(self, tickerId, contract, useRTH, timePeriod): self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_HISTOGRAM_DATA), + MessageId.OUT.REQ_HISTOGRAM_DATA, createHistogramDataRequestProto(tickerId, contract, useRTH, timePeriod), ) - def cancelHistogramData(self, tickerId): - self.send(89, tickerId) + def cancelHistogramData(self, reqId): + self.sendProto( + MessageId.OUT.CANCEL_HISTOGRAM_DATA, + createCancelHistoricalDataProto(reqId), + ) def reqMarketRule(self, marketRuleId: int): marketRuleRequestProto = createMarketRuleRequestProto(marketRuleId) - self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_MARKET_RULE), marketRuleRequestProto - ) + self.sendProto(MessageId.OUT.REQ_MARKET_RULE, marketRuleRequestProto) def reqPnL(self, reqId, account, modelCode): - self.send(92, reqId, account, modelCode) + self.sendProto( + MessageId.OUT.REQ_PNL, + createPnLRequestProto(reqId, account, modelCode) + ) + def cancelPnL(self, reqId): - self.send(93, reqId) + self.sendProto( + MessageId.OUT.CANCEL_PNL, + createCancelPnLProto(reqId) + ) def reqPnLSingle(self, reqId, account, modelCode, conid): - self.send(94, reqId, account, modelCode, conid) + self.sendProto( + MessageId.OUT.REQ_PNL_SINGLE, + createPnLSingleRequestProto(reqId, account, modelCode, conid) + ) def cancelPnLSingle(self, reqId): - self.send(95, reqId) + self.sendProto( + MessageId.OUT.CANCEL_PNL_SINGLE, + createCancelPnLProto(reqId) + ) def reqHistoricalTicks( self, @@ -1322,21 +1021,30 @@ def reqHistoricalTicks( miscOptions, ) self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_HISTORICAL_TICKS), + MessageId.OUT.REQ_HISTORICAL_TICKS, historicalTicksRequestProto, ) def reqTickByTickData(self, reqId, contract, tickType, numberOfTicks, ignoreSize): - self.send(97, reqId, contract, tickType, numberOfTicks, ignoreSize) + tickByTickRequestProto = createTickByTickRequestProto( + reqId, contract, tickType, numberOfTicks, ignoreSize + ) + self.sendProto( + MessageId.OUT.REQ_TICK_BY_TICK_DATA, + tickByTickRequestProto, + ) def cancelTickByTickData(self, reqId): - self.send(98, reqId) + self.sendProto( + MessageId.OUT.CANCEL_TICK_BY_TICK_DATA, + CancelTickByTickProto(reqId=reqId), + ) def reqCompletedOrders(self, apiOnly: bool): completedOrdersRequestProto = CompletedOrdersRequestProto() completedOrdersRequestProto.apiOnly = apiOnly self.sendProto( - MessageId.to_protobuf(MessageId.OUT.REQ_COMPLETED_ORDERS), + MessageId.OUT.REQ_COMPLETED_ORDERS, completedOrdersRequestProto, ) @@ -1363,4 +1071,8 @@ def cancelWshEventData(self, reqId): self.send(103, reqId) def reqUserInfo(self, reqId): - self.send(104, reqId) + self.sendProto( + MessageId.OUT.REQ_USER_INFO, + createUserInfoRequestProto(reqId), + ) + diff --git a/ib_async/contract.py b/ib_async/contract.py index 3203f847..3e359d55 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -30,7 +30,7 @@ class IneligibilityReason: id_: str = field(default_factory=str) description: str = field(default_factory=str) -@dataclass +@dataclass(slots=True) class Contract: """ ``Contract(**kwargs)`` can create any contract using keyword diff --git a/ib_async/decoder.py b/ib_async/decoder.py index e5a67fce..16d3fecb 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -1,8 +1,8 @@ """Deserialize and dispatch messages.""" import logging -from datetime import datetime -from typing import Any + +from ib_async.protobuf_converters.subscription_converters import createScannerDataList from .contract import ( Contract, @@ -12,12 +12,9 @@ ) from .message import MessageId from .objects import ( - BarData, DepthMktDataDescription, FamilyCode, HistogramData, - HistoricalSession, - HistoricalTick, HistoricalTickBidAsk, HistoricalTickLast, NewsProvider, @@ -25,8 +22,6 @@ SmartComponent, SoftDollarTier, TagValue, - TickAttribBidAsk, - TickAttribLast, TickType, ) from .order import OrderStatus @@ -61,10 +56,17 @@ from .protobuf.ExecutionDetailsEnd_pb2 import ( ExecutionDetailsEnd as ExecutionDetailsEndProto, ) +from .protobuf.FundamentalsData_pb2 import FundamentalsData as FundamentalsDataProto from .protobuf.HeadTimestamp_pb2 import HeadTimestamp as HeadTimestampProto from .protobuf.HistogramData_pb2 import HistogramData as HistogramDataProto from .protobuf.HistoricalData_pb2 import HistoricalData as HistoricalDataProto from .protobuf.HistoricalDataEnd_pb2 import HistoricalDataEnd as HistoricalDataEndProto +from .protobuf.HistoricalDataUpdate_pb2 import ( + HistoricalDataUpdate as HistoricalDataUpdateProto, +) +from .protobuf.HistoricalSchedule_pb2 import ( + HistoricalSchedule as HistoricalScheduleProto, +) from .protobuf.HistoricalTicks_pb2 import HistoricalTicks as HistoricalTicksProto from .protobuf.HistoricalTicksBidAsk_pb2 import ( HistoricalTicksBidAsk as HistoricalTicksBidAskProto, @@ -72,34 +74,41 @@ from .protobuf.HistoricalTicksLast_pb2 import ( HistoricalTicksLast as HistoricalTicksLastProto, ) -from .protobuf.HistoricalSchedule_pb2 import HistoricalSchedule as HistoricalScheduleProto -from .protobuf.HistoricalSession_pb2 import HistoricalSession as HistoricalSessionProto from .protobuf.ManagedAccounts_pb2 import ManagedAccounts as ManagedAccountsProto -from .protobuf.MarketRule_pb2 import MarketRule as MarketRuleProto from .protobuf.MarketDataType_pb2 import MarketDataType as MarketDataTypeProto +from .protobuf.MarketRule_pb2 import MarketRule as MarketRuleProto from .protobuf.NextValidId_pb2 import NextValidId as NextValidIdProto from .protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto from .protobuf.OpenOrdersEnd_pb2 import OpenOrdersEnd as OpenOrderEndProto from .protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto +from .protobuf.OrderBound_pb2 import OrderBound as OrderBoundProto from .protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto from .protobuf.Position_pb2 import Position as PositionProto from .protobuf.PositionEnd_pb2 import PositionEnd as PositionEndProto +from .protobuf.PnL_pb2 import PnL as PnLProto +from .protobuf.PnLSingle_pb2 import PnLSingle as PnLSingleProto +from .protobuf.RealTimeBarTick_pb2 import RealTimeBarTick as RealTimeBarTickProto +from .protobuf.ScannerParameters_pb2 import ScannerParameters as ScannerParametersProto +from .protobuf.ScannerData_pb2 import ScannerData as ScannerDataProto from .protobuf.SecDefOptParameter_pb2 import ( SecDefOptParameter as SecDefOptParameterProto, ) from .protobuf.SecDefOptParameterEnd_pb2 import ( SecDefOptParameterEnd as SecDefOptParameterEndProto, ) +from .protobuf.SmartComponents_pb2 import SmartComponents as SmartComponentsProto from .protobuf.SymbolSamples_pb2 import SymbolSamples as SymbolSamplesProto from .protobuf.TickGeneric_pb2 import TickGeneric as TickGenericProto from .protobuf.TickOptionComputation_pb2 import ( TickOptionComputation as TickOptionComputationProto, ) from .protobuf.TickPrice_pb2 import TickPrice as TickPriceProto +from .protobuf.TickReqParams_pb2 import TickReqParams as TickReqParamsProto from .protobuf.TickSize_pb2 import TickSize as TickSizeProto from .protobuf.TickSnapshotEnd_pb2 import TickSnapshotEnd as TickSnapshotEndProto from .protobuf.TickString_pb2 import TickString as TickStringProto -from .protobuf.TickReqParams_pb2 import TickReqParams as TickReqParamsProto +from .protobuf.TickByTickData_pb2 import TickByTickData as TickByTickDataProto +from .protobuf.UserInfo_pb2 import UserInfo as UserInfoProto from .protobuf_converters.account_converters import ( createAccountSummary, createAccountValue, @@ -111,21 +120,24 @@ createContractDescription, createContractDetails, createOptionChain, + createSmartComponents, ) from .protobuf_converters.historical_data_converters import ( + createBarData, createBarDataList, createHistogramDataEntry, createHistoricalSchedule, createHistoricalTick, createHistoricalTickBidAsk, createHistoricalTickLast, + createRealTimeBarTick, ) from .protobuf_converters.market_data_converters import ( + createTickGenericData, createTickOptionComputation, createTickParams, createTickPriceData, createTickSizeData, - createTickGenericData, createTickStringData, ) from .protobuf_converters.trade_converter import ( @@ -136,14 +148,18 @@ createOrderStatus, createTradeFromOpenOrder, ) -from .util import NO_VALID_ID + +from .util import NO_VALID_ID, UNSET_DOUBLE, UNSET_INTEGER from .wrapper import Wrapper class Decoder: - """Decode IB messages and invoke corresponding wrapper methods.""" + """Decode IB messages from bytes to proto and to ib-async objects, then invoke + corresponding wrapper methods. + """ - PROTOBUF_MESSAGE_HANDLERS = { + # MessageId.IN -> (DataTypeProto, methodHandler) + PROTOBUF_MESSAGE_HANDLERS: dict[MessageId.IN, tuple[type, str]] = { # Handles NEXT_VALID_ID message during handshake MessageId.IN.NEXT_VALID_ID: (NextValidIdProto, "nextValidIdProto"), # Handles incoming contract details for reqContractDetails @@ -191,24 +207,29 @@ class Decoder: AccountUpdateMultiEndProto, "accountUpdateMultiEndProto", ), + # Handles incoming open order updates MessageId.IN.OPEN_ORDER: (OpenOrderProto, "openOrderProto"), MessageId.IN.OPEN_ORDER_END: (OpenOrderEndProto, "openOrderEndProto"), + # Handles incoming execution details MessageId.IN.EXECUTION_DATA: (ExecutionDetailsProto, "execDetailsProto"), MessageId.IN.EXECUTION_DATA_END: ( ExecutionDetailsEndProto, "execDetailsEndProto", ), + # Handles account summary MessageId.IN.ACCOUNT_SUMMARY: (AccountSummaryProto, "accountSummaryProto"), MessageId.IN.ACCOUNT_SUMMARY_END: ( AccountSummaryEndProto, "accountSummaryEndProto", ), + # Handles order status updates MessageId.IN.ORDER_STATUS: (OrderStatusProto, "orderStatusProto"), MessageId.IN.COMPLETED_ORDER: (CompletedOrderProto, "completedOrderProto"), MessageId.IN.COMPLETED_ORDERS_END: ( CompletedOrdersEndProto, "completedOrdersEndProto", ), + MessageId.IN.ORDER_BOUND: (OrderBoundProto, "orderBoundProto"), MessageId.IN.PORTFOLIO_VALUE: (PortfolioValueProto, "updatePortfolioProto"), MessageId.IN.ACCT_UPDATE_TIME: ( AccountUpdateTimeProto, @@ -230,6 +251,10 @@ class Decoder: HistoricalDataEndProto, "historicalDataProtoEnd", ), + MessageId.IN.HISTORICAL_DATA_UPDATE: ( + HistoricalDataUpdateProto, + "historicalDataUpdateProto", + ), MessageId.IN.HISTORICAL_TICKS: (HistoricalTicksProto, "historicalTicksProto"), MessageId.IN.HISTORICAL_TICKS_BID_ASK: ( HistoricalTicksBidAskProto, @@ -244,6 +269,7 @@ class Decoder: HistoricalScheduleProto, "historicalScheduleProto", ), + MessageId.IN.REAL_TIME_BARS: (RealTimeBarTickProto, "realTimeBarTickProto"), MessageId.IN.TICK_REQ_PARAMS: (TickReqParamsProto, "tickReqParamsProto"), MessageId.IN.TICK_PRICE: (TickPriceProto, "tickPriceProto"), MessageId.IN.TICK_SIZE: (TickSizeProto, "tickSizeProto"), @@ -254,6 +280,20 @@ class Decoder: "tickOptionComputationProto", ), MessageId.IN.TICK_SNAPSHOT_END: (TickSnapshotEndProto, "tickSnapshotEndProto"), + MessageId.IN.TICK_BY_TICK: ( + TickByTickDataProto, + "tickByTickDataProto", + ), + MessageId.IN.FUNDAMENTAL_DATA: (FundamentalsDataProto, "fundamentalsDataProto"), + MessageId.IN.SCANNER_PARAMETERS: ( + ScannerParametersProto, + "scannerParametersProto", + ), + MessageId.IN.SCANNER_DATA: (ScannerDataProto, "scannerDataProto"), + MessageId.IN.PNL: (PnLProto, "pnlProto"), + MessageId.IN.PNL_SINGLE: (PnLSingleProto, "pnlSingleProto"), + MessageId.IN.USER_INFO: (UserInfoProto, "userInfoProto"), + MessageId.IN.SMART_COMPONENTS: (SmartComponentsProto, "smartComponentsProto"), } def __init__(self, wrapper: Wrapper, serverVersion: int): @@ -262,22 +302,33 @@ def __init__(self, wrapper: Wrapper, serverVersion: int): self.logger = logging.getLogger("ib_async.Decoder") def processProtoBuf(self, payload: bytes): - """Process a binary Protobuf message payload.""" + """ + Process a binary Protobuf message payload by calling the appropriate handler + method. + + `wrapper.PROTOBUF_MESSAGE_HANDLERS.get(msgId) -> (DataTypeProto, methodHandler)` + + """ + msgId_raw = int.from_bytes(payload[:4], "big") try: - msgId_raw = int.from_bytes(payload[:4], "big") - data = payload[4:] - msgId = MessageId.from_protobuf(msgId_raw) + msgId = MessageId.IN(msgId_raw) handler_info = self.PROTOBUF_MESSAGE_HANDLERS.get(msgId) if handler_info: proto_class, handler_method_name = handler_info proto_message = proto_class() - proto_message.ParseFromString(data) + proto_message.ParseFromString(payload[4:]) handler_method = getattr(self, handler_method_name) handler_method(proto_message) else: self.logger.warning("Unknown Protobuf message id: %s", msgId) - except Exception: - self.logger.exception("Error processing Protobuf message id: %s", msgId) + except ValueError as err: + self.logger.warning( + "ValueError: Processing protobuf message id: %s, %s", msgId_raw, err + ) + except Exception as err: + self.logger.exception( + "Error processing Protobuf message id: %s, %s", msgId_raw, err + ) def errorMessageProto(self, msg: ErrorMessageProto): reqId = msg.id @@ -319,10 +370,10 @@ def marketRuleProto(self, msg: MarketRuleProto): def secDefOptParameterProto(self, msg: SecDefOptParameterProto): reqId = msg.reqId optionChain = createOptionChain(msg) - self.wrapper.response_bus.emit(reqId, optionChain) + self.wrapper.securityDefinitionOptionParameter(reqId, optionChain) def secDefOptParameterEndProto(self, msg: SecDefOptParameterEndProto): - self.wrapper.response_bus.emit(msg.reqId, None) + self.wrapper.securityDefinitionOptionParameterEnd(msg.reqId) def managedAccountsProto(self, msg: ManagedAccountsProto): accountsList = msg.accountsList @@ -364,8 +415,11 @@ def nextValidIdProto(self, msg: NextValidIdProto): self.wrapper.nextValidId(msg.orderId) def openOrderProto(self, msg: OpenOrderProto): - trade = createTradeFromOpenOrder(msg) - self.wrapper.openOrder(trade) + trade, orderState = createTradeFromOpenOrder(msg) + if trade: + self.wrapper.openOrder(trade, orderState) + return + self.logger.error("Error processing order, %r", msg) def openOrderEndProto(self, msg: OpenOrderEndProto): self.wrapper.openOrderEnd() @@ -390,6 +444,12 @@ def completedOrderProto(self, msg: CompletedOrderProto): def completedOrdersEndProto(self, msg: CompletedOrdersEndProto): self.wrapper.completedOrdersEnd() + def orderBoundProto(self, msg: OrderBoundProto): + permId = msg.permId if msg.HasField("permId") else UNSET_INTEGER + clientId = msg.clientId if msg.HasField("clientId") else UNSET_INTEGER + orderId = msg.orderId if msg.HasField("orderId") else UNSET_INTEGER + self.wrapper.orderBound(permId, clientId, orderId) + def updateAccountTimeProto(self, msg: AccountUpdateTimeProto): time = msg.timeStamp if msg.HasField("timeStamp") else "" self.wrapper.updateAccountTime(time) @@ -458,10 +518,20 @@ def histogramDataProto(self, msg: HistogramDataProto): histogram.append(histogramEntry) self.wrapper.histogramData(msg.reqId, histogram) + def historicalDataUpdateProto(self, msg: HistoricalDataUpdateProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + bar = createBarData(msg.historicalDataBar) + self.wrapper.historicalDataUpdate(reqId, bar) + def historicalScheduleProto(self, msg: HistoricalScheduleProto): historicalSchedule = createHistoricalSchedule(msg) self.wrapper.historicalSchedule(msg.reqId, historicalSchedule) - + + def realTimeBarTickProto(self, msg: RealTimeBarTickProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + + realTimeBarTick = createRealTimeBarTick(msg, self.wrapper.defaults.timezone) + self.wrapper.realtimeBar(reqId, realTimeBarTick) def marketDataTypeProto(self, msg: MarketDataTypeProto): self.wrapper.marketDataType(msg.reqId, msg.marketDataType) @@ -490,9 +560,85 @@ def tickStringProto(self, msg: TickStringProto): def tickOptionComputationProto(self, msg: TickOptionComputationProto): tick_computation = createTickOptionComputation(msg) - self.wrapper.tickOptionComputation( - tick_computation.reqId, tick_computation + self.wrapper.tickOptionComputation(tick_computation.reqId, tick_computation) + + def tickSnapshotEndProto(self, msg: TickSnapshotEndProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + self.wrapper.tickSnapshotEnd(reqId) + + def tickByTickDataProto(self, msg: TickByTickDataProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + tickType = msg.tickType if msg.HasField("tickType") else 0 + + if tickType == 0: + pass + elif tickType == 1 or tickType == 2: + # Last or AllLast + if msg.HasField("historicalTickLast"): + tick_last = createHistoricalTickLast( + msg.historicalTickLast, self.wrapper.defaults.timezone + ) + self.wrapper.tickByTickAllLast(reqId, tick_last) + elif tickType == 3: + # BidAsk + if msg.HasField("historicalTickBidAsk"): + tick_bid_ask = createHistoricalTickBidAsk( + msg.historicalTickBidAsk, self.wrapper.defaults.timezone + ) + self.wrapper.tickByTickBidAsk(reqId, tick_bid_ask) + elif tickType == 4: + # MidPoint + if msg.HasField("historicalTickMidPoint"): + tick_mid = createHistoricalTick( + msg.historicalTickMidPoint, self.wrapper.defaults.timezone + ) + self.wrapper.tickByTickMidPoint(reqId, tick_mid) + + def fundamentatalDataProto(self, msg: FundamentalsDataProto): + self.wrapper.fundamentalData(msg.reqId, msg.fundamentalData) + + def scannerParametersProto(self, msg: ScannerParametersProto): + xml = msg.xml if msg.HasField("xml") else "" + self.wrapper.scannerParameters(xml) + + def scannerDataProto(self, msg: ScannerDataProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + scanData = createScannerDataList(msg) + self.wrapper.scannerData(reqId, scanData) + + def pnlProto(self, msg: PnLProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + dailyPnL = msg.dailyPnL if msg.HasField("dailyPnL") else UNSET_DOUBLE + unrealizedPnL = ( + msg.unrealizedPnL if msg.HasField("unrealizedPnL") else UNSET_DOUBLE ) + realizedPnL = msg.realizedPnL if msg.HasField("realizedPnL") else UNSET_DOUBLE + + self.wrapper.pnl(reqId, dailyPnL, unrealizedPnL, realizedPnL) + + def pnlSingleProto(self, msg: PnLSingleProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + position = int(msg.position) if msg.HasField("position") else UNSET_INTEGER + dailyPnL = msg.dailyPnL if msg.HasField("dailyPnL") else UNSET_DOUBLE + unrealizedPnL = ( + msg.unrealizedPnL if msg.HasField("unrealizedPnL") else UNSET_DOUBLE + ) + realizedPnL = msg.realizedPnL if msg.HasField("realizedPnL") else UNSET_DOUBLE + value = msg.value if msg.HasField("value") else UNSET_DOUBLE + + self.wrapper.pnlSingle( + reqId, position, dailyPnL, unrealizedPnL, realizedPnL, value + ) + + def userInfoProto(self, msg: UserInfoProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + whiteBrandingId = msg.whiteBrandingId if msg.HasField("whiteBrandingId") else "" + self.wrapper.userInfo(reqId, whiteBrandingId) + + def smartComponentsProto(self, msg: SmartComponentsProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + components = createSmartComponents(msg) + self.wrapper.smartComponents(reqId, components) ##################### legacy methods ########################################## @@ -576,58 +722,6 @@ def bondContractDetails(self, fields): self.parse(c) self.wrapper.bondContractDetails(int(reqId), cd) - - def historicalDataUpdate(self, fields): - _, reqId, *fields = fields - get = iter(fields).__next__ - - bar = BarData( - barCount=int(get() or 0), - date=get(), - open=float(get() or 0), - close=float(get() or 0), - high=float(get() or 0), - low=float(get() or 0), - average=float(get() or 0), - volume=float(get() or 0), - ) - - self.wrapper.historicalDataUpdate(int(reqId), bar) - - def scannerData(self, fields): - _, _, reqId, n, *fields = fields - - for _ in range(int(n)): - cd = ContractDetails() - cd.contract = c = Contract() - ( - rank, - c.conId, - c.symbol, - c.secType, - c.lastTradeDateOrContractMonth, - c.strike, - c.right, - c.exchange, - c.currency, - c.localSymbol, - cd.marketName, - c.tradingClass, - distance, - benchmark, - projection, - legsStr, - *fields, - ) = fields - - self.parse(cd) - self.parse(c) - self.wrapper.scannerData( - int(reqId), int(rank), cd, distance, benchmark, projection, legsStr - ) - - self.wrapper.scannerDataEnd(int(reqId)) - def deltaNeutralValidation(self, fields): _, _, reqId, conId, delta, price = fields @@ -692,128 +786,3 @@ def newsProviders(self, fields): providers = [NewsProvider(code=get(), name=get()) for _ in range(int(n))] self.wrapper.newsProviders(providers) - - def histogramData(self, fields): - _, reqId, n, *fields = fields - get = iter(fields).__next__ - - histogram = [ - HistogramData(price=float(get()), count=int(get())) for _ in range(int(n)) - ] - - self.wrapper.histogramData(int(reqId), histogram) - - def historicalTicks(self, fields): - _, reqId, n, *fields = fields - get = iter(fields).__next__ - - ticks = [] - for _ in range(int(n)): - time = int(get()) - get() - price = float(get()) - size = float(get()) - dt = datetime.fromtimestamp(time, self.wrapper.defaultTimezone) - ticks.append(HistoricalTick(dt, price, size)) - - done = bool(int(get())) - self.wrapper.historicalTicks(int(reqId), ticks, done) - - def historicalTicksBidAsk(self, fields): - _, reqId, n, *fields = fields - get = iter(fields).__next__ - - ticks = [] - for _ in range(int(n)): - time = int(get()) - mask = int(get()) - attrib = TickAttribBidAsk( - askPastHigh=bool(mask & 1), bidPastLow=bool(mask & 2) - ) - priceBid = float(get()) - priceAsk = float(get()) - sizeBid = float(get()) - sizeAsk = float(get()) - dt = datetime.fromtimestamp(time, self.wrapper.defaultTimezone) - ticks.append( - HistoricalTickBidAsk(dt, attrib, priceBid, priceAsk, sizeBid, sizeAsk) - ) - - done = bool(int(get())) - self.wrapper.historicalTicksBidAsk(int(reqId), ticks, done) - - def historicalTicksLast(self, fields): - _, reqId, n, *fields = fields - get = iter(fields).__next__ - - ticks = [] - for _ in range(int(n)): - time = int(get()) - mask = int(get()) - attrib = TickAttribLast(pastLimit=bool(mask & 1), unreported=bool(mask & 2)) - price = float(get()) - size = float(get()) - exchange = get() - specialConditions = get() - dt = datetime.fromtimestamp(time, self.wrapper.defaultTimezone) - ticks.append( - HistoricalTickLast(dt, attrib, price, size, exchange, specialConditions) - ) - - done = bool(int(get())) - self.wrapper.historicalTicksLast(int(reqId), ticks, done) - - def tickByTick(self, fields): - _, reqId, tickType, time, *fields = fields - reqId = int(reqId) - tickType = int(tickType) - time = int(time) - - if tickType in {1, 2}: - price, size, mask, exchange, specialConditions = fields - mask = int(mask) - attrib: Any = TickAttribLast( - pastLimit=bool(mask & 1), unreported=bool(mask & 2) - ) - - self.wrapper.tickByTickAllLast( - reqId, - tickType, - time, - float(price), - float(size), - attrib, - exchange, - specialConditions, - ) - elif tickType == 3: - bidPrice, askPrice, bidSize, askSize, mask = fields - mask = int(mask) - attrib = TickAttribBidAsk( - bidPastLow=bool(mask & 1), askPastHigh=bool(mask & 2) - ) - - self.wrapper.tickByTickBidAsk( - reqId, - time, - float(bidPrice), - float(askPrice), - float(bidSize), - float(askSize), - attrib, - ) - elif tickType == 4: - (midPoint,) = fields - - self.wrapper.tickByTickMidPoint(reqId, time, float(midPoint)) - - def historicalSchedule(self, fields): - (_, reqId, startDateTime, endDateTime, timeZone, count, *fields) = fields - get = iter(fields).__next__ - sessions = [ - HistoricalSession(startDateTime=get(), endDateTime=get(), refDate=get()) - for _ in range(int(count)) - ] - self.wrapper.historicalSchedule( - int(reqId), startDateTime, endDateTime, timeZone, sessions - ) diff --git a/ib_async/ib.py b/ib_async/ib.py index 6100786d..548a6c47 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -59,6 +59,7 @@ BracketOrder, LimitOrder, Order, + OrderCancel, OrderState, OrderStateNumeric, OrderStatus, @@ -185,6 +186,9 @@ class IB: * ``newOrderEvent`` (trade: :class:`.Trade`): Emits a newly placed trade. + * ``orderBoundEvent`` (permId: int, clientId: int, orderId: int): + Emits when TWS order is bound. + * ``orderModifyEvent`` (trade: :class:`.Trade`): Emits when order is modified. @@ -263,6 +267,7 @@ class IB: "newOrderEvent", "orderModifyEvent", "cancelOrderEvent", + "orderBoundEvent", "openOrderEvent", "orderStatusEvent", "execDetailsEvent", @@ -302,6 +307,7 @@ def _createEvents(self): self.pendingTickersEvent = Event("pendingTickersEvent") self.barUpdateEvent = Event("barUpdateEvent") self.newOrderEvent = Event("newOrderEvent") + self.orderBoundEvent = Event("orderBoundEvent") self.orderModifyEvent = Event("orderModifyEvent") self.cancelOrderEvent = Event("cancelOrderEvent") self.openOrderEvent = Event("openOrderEvent") @@ -447,16 +453,6 @@ def _raise_if_error(data: Any) -> Any: raise data return data - @staticmethod - async def _raise_if_error_async(ait: AsyncIterator[_T]) -> AsyncIterator[_T]: - """ - Raise error in asyn iterator. - """ - async for item in ait: - if isinstance(item, RequestError): - raise item - yield item - def _run(self, *awaitables: Awaitable): return util.run(*awaitables, timeout=self.RequestTimeout) @@ -597,7 +593,7 @@ def pnl(self, account="", modelCode="") -> list[PnL]: """ return [ v - for v in self.wrapper.reqId2PnL.values() + for v in self.wrapper.Pnl.values() if (not account or v.account == account) and (not modelCode or v.modelCode == modelCode) ] @@ -618,7 +614,7 @@ def pnlSingle( """ return [ v - for v in self.wrapper.reqId2PnlSingle.values() + for v in self.wrapper.pnlSingles.values() if (not account or v.account == account) and (not modelCode or v.modelCode == modelCode) and (not conId or v.conId == conId) @@ -665,14 +661,11 @@ def ticker(self, contract: Contract) -> Ticker | None: Args: contract: Contract to get ticker for. """ - for ticker in self.wrapper.reqId2Ticker.values(): - if hash(ticker.contract) == hash(contract): - return ticker - return None + return self.wrapper.tickers.get_by_object_id(hash(contract)) def tickers(self) -> list[Ticker]: """Get a list of all tickers.""" - return list(self.wrapper.reqId2Ticker.values()) + return list(self.wrapper.tickers.values()) def pendingTickers(self) -> list[Ticker]: """Get a list of all tickers that have pending ticks or domTicks.""" @@ -827,7 +820,7 @@ def placeOrder(self, contract: Contract, order: Order) -> Trade: self.client.placeOrder(orderId, contract, order) now = datetime.datetime.now(self.wrapper.defaultTimezone) key = self.wrapper.orderKey(self.wrapper.clientId, orderId, order.permId) - trade = self.wrapper.trades.get(key) + trade = self.wrapper.trades.get_by_object_id(key) if trade: # this is a modification of an existing order assert trade.orderStatus.status not in OrderStatus.DoneStates @@ -843,26 +836,29 @@ def placeOrder(self, contract: Contract, order: Order) -> Trade: orderStatus = OrderStatus(orderId=orderId, status=OrderStatus.PendingSubmit) logEntry = TradeLogEntry(now, orderStatus.status) trade = Trade(contract, order, orderStatus, [], [logEntry]) - self.wrapper.trades[key] = trade + # use orderId temporaly as id, wrapper.openOrder will update it + self.wrapper.trades.add(orderId, key, trade) self._logger.info(f"placeOrder: New order {trade}") self.newOrderEvent.emit(trade) return trade def cancelOrder( - self, order: Order, manualCancelOrderTime: str = "" + self, order: Order, orderCancel: OrderCancel | None = None ) -> Optional[Trade]: """ Cancel the order and return the Trade it belongs to. Args: order: The order to be canceled. - manualCancelOrderTime: For audit trail. + orderCancel: (OrderCancel) default None. """ - self.client.cancelOrder(order.orderId, manualCancelOrderTime) + if orderCancel is None: + orderCancel = OrderCancel() + self.client.cancelOrder(order.orderId, orderCancel) now = datetime.datetime.now(self.wrapper.defaultTimezone) key = self.wrapper.orderKey(order.clientId, order.orderId, order.permId) - trade = self.wrapper.trades.get(key) + trade = self.wrapper.trades.get_by_object_id(key) if trade: if not trade.isDone(): status = trade.orderStatus.status @@ -890,12 +886,17 @@ def cancelOrder( return trade - def reqGlobalCancel(self): + def reqGlobalCancel(self, orderCancel: OrderCancel | None = None): """ Cancel all active trades including those placed by other clients or TWS/IB gateway. + + Args: + orderCancel: (OrderCancel|None) default None """ - self.client.reqGlobalCancel() + if orderCancel is None: + orderCancel = OrderCancel() + self.client.reqGlobalCancel(orderCancel) self._logger.info("reqGlobalCancel") def reqCurrentTime(self) -> datetime.datetime: @@ -1039,12 +1040,11 @@ def reqPnL(self, account: str, modelCode: str = "") -> PnL: modelCode: If specified, filter for this account model. """ key = (account, modelCode) - assert key not in self.wrapper.pnlKey2ReqId + assert key not in self.wrapper.Pnl reqId = self.client.getReqId() - self.wrapper.pnlKey2ReqId[key] = reqId pnl = PnL(account, modelCode) - self.wrapper.reqId2PnL[reqId] = pnl + self.wrapper.Pnl.add(reqId, key, pnl) self.client.reqPnL(reqId, account, modelCode) return pnl @@ -1058,10 +1058,10 @@ def cancelPnL(self, account, modelCode: str = ""): modelCode: If specified, cancel for this account model. """ key = (account, modelCode) - reqId = self.wrapper.pnlKey2ReqId.pop(key, None) + reqId = self.wrapper.Pnl.get_request_id(key) if reqId: self.client.cancelPnL(reqId) - self.wrapper.reqId2PnL.pop(reqId, None) + self.wrapper.Pnl.remove_by_request_id(reqId) else: self._logger.error( "cancelPnL: No subscription for " @@ -1083,12 +1083,11 @@ def reqPnLSingle(self, account: str, modelCode: str, conId: int) -> PnLSingle: conId: Filter for this contract ID. """ key = (account, modelCode, conId) - assert key not in self.wrapper.pnlSingleKey2ReqId + assert key not in self.wrapper.pnlSingles reqId = self.client.getReqId() - self.wrapper.pnlSingleKey2ReqId[key] = reqId pnlSingle = PnLSingle(account, modelCode, conId) - self.wrapper.reqId2PnlSingle[reqId] = pnlSingle + self.wrapper.pnlSingles.add(reqId, key, pnlSingle) self.client.reqPnLSingle(reqId, account, modelCode, conId) return pnlSingle @@ -1104,10 +1103,10 @@ def cancelPnLSingle(self, account: str, modelCode: str, conId: int): conId: Cancel for this contract ID. """ key = (account, modelCode, conId) - reqId = self.wrapper.pnlSingleKey2ReqId.pop(key, None) + reqId = self.wrapper.pnlSingles.get_request_id(key) if reqId: self.client.cancelPnLSingle(reqId) - self.wrapper.reqId2PnlSingle.pop(reqId, None) + self.wrapper.pnlSingles.remove_by_request_id(reqId) else: self._logger.error( "cancelPnLSingle: No subscription for " @@ -1545,6 +1544,8 @@ def reqSmartComponents(self, bboExchange: str) -> list[SmartComponent]: Note: The exchanges must be open when using this request, otherwise an empty list is returned. + + ie `ib.reqSmartComponents(spy_ticker.bboExchange)` """ return self._run(self.reqSmartComponentsAsync(bboExchange)) @@ -1672,7 +1673,7 @@ def reqScannerData( This method is blocking. - https://interactivebrokers.github.io/tws-api/market_scanners.html + https://ibkrcampus.com/campus/ibkr-api-page/twsapi-doc/#market-scanner Args: subscription: Basic filters. @@ -1696,7 +1697,7 @@ def reqScannerSubscription( """ Subscribe to market scan data. - https://interactivebrokers.github.io/tws-api/market_scanners.html + https://ibkrcampus.com/campus/ibkr-api-page/twsapi-doc/#market-scanner Args: subscription: What to scan for. @@ -1711,6 +1712,7 @@ def reqScannerSubscription( dataList.scannerSubscriptionFilterOptions = ( scannerSubscriptionFilterOptions or [] ) + self.wrapper.startSubscription(reqId, dataList) self.client.reqScannerSubscription( reqId, @@ -1724,7 +1726,7 @@ def cancelScannerSubscription(self, dataList: ScanDataList): """ Cancel market data subscription. - https://interactivebrokers.github.io/tws-api/market_scanners.html + https://ibkrcampus.com/campus/ibkr-api-page/twsapi-doc/#market-scanner Args: dataList: The scan data list that was obtained from @@ -1738,6 +1740,7 @@ def reqScannerParameters(self) -> str: Requests an XML list of scanner parameters. This method is blocking. + https://ibkrcampus.com/campus/ibkr-api-page/twsapi-doc/#market-scanner """ return self._run(self.reqScannerParametersAsync()) @@ -2066,7 +2069,7 @@ def reqUserInfo(self) -> str: """Get the White Branding ID of the user.""" return self._run(self.reqUserInfoAsync()) - # now entering the parallel async universe + # now entering the parallel async universe, fasten your belt async def connectAsync( self, @@ -2133,6 +2136,10 @@ async def connectAsync( msg = f"{name} request timed out" errors.append(msg) self._logger.error(msg) + elif isinstance(resp, Exception): + msg = f"{name} request failed: {resp}" + errors.append(msg) + self._logger.error(msg, exc_info=resp) # the request for executions must come after all orders are in if fetchFields & StartupFetch.EXECUTIONS: @@ -2234,34 +2241,34 @@ async def qualifyContractsAsync( async def reqTickersAsync( self, *contracts: Contract, regulatorySnapshot: bool = False ) -> list[Ticker]: - futures = [] tickers = [] reqIds = [] for contract in contracts: reqId = self.client.getReqId() reqIds.append(reqId) - future = self.wrapper.startReq(reqId, contract) - futures.append(future) ticker = self.wrapper.startTicker(reqId, contract, "snapshot") tickers.append(ticker) self.client.reqMktData(reqId, contract, "", True, regulatorySnapshot, []) - await asyncio.gather(*futures) - - for ticker in tickers: - self.wrapper.endTicker(ticker, "snapshot") + awaitables = [t.ticker_bus for t in tickers] + await asyncio.gather(*awaitables) return tickers def whatIfOrderAsync( self, contract: Contract, order: Order ) -> Awaitable[OrderState]: + reqId = self.client.getReqId() whatIfOrder = copy.copy(order) whatIfOrder.whatIf = True - reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId, contract) + whatIfOrder.orderId = reqId self.client.placeOrder(reqId, contract, whatIfOrder) - return future + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) def reqCurrentTimeAsync(self) -> Awaitable[datetime.datetime]: self.client.reqCurrentTime() @@ -2383,9 +2390,7 @@ def reqExecutionsAsync( reqId = self.client.getReqId() self.client.reqExecutions(reqId, execFilter or ExecutionFilter()) fills = ( - self.wrapper.response_bus.filter( - lambda rId, e: rId == reqId - ) + self.wrapper.response_bus.filter(lambda rId, e: rId == reqId) .takewhile(lambda r, data: data is not None) .pluck(1) .map(self._raise_if_error) @@ -2488,9 +2493,20 @@ async def reqHistoricalDataAsync( timeout: float = 60, ) -> BarDataList: reqId = self.client.getReqId() - # if keepUpToDate: - # self.wrapper.startSubscription(reqId, bars, contract) end = util.formatIBDatetime(endDateTime) + + bars = BarDataList() + bars.reqId = reqId + bars.contract = contract + bars.endDateTime = endDateTime + bars.durationStr = durationStr + bars.barSizeSetting = barSizeSetting + bars.whatToShow = whatToShow + bars.useRTH = useRTH + bars.formatDate = formatDate + bars.keepUpToDate = keepUpToDate + bars.chartOptions = chartOptions or [] + self.client.reqHistoricalData( reqId, contract, @@ -2515,19 +2531,12 @@ async def reqHistoricalDataAsync( except asyncio.TimeoutError: self.client.cancelHistoricalData(reqId) self._logger.warning(f"reqHistoricalData: Timeout for {contract}") - - bars = BarDataList() - bars.reqId = reqId - bars.contract = contract - bars.endDateTime = endDateTime - bars.durationStr = durationStr - bars.barSizeSetting = barSizeSetting - bars.whatToShow = whatToShow - bars.useRTH = useRTH - bars.formatDate = formatDate - bars.keepUpToDate = keepUpToDate - bars.chartOptions = chartOptions or [] + # add data bars.extend(result) + + if keepUpToDate: + self.wrapper.startSubscription(reqId, bars, contract) + return bars def reqHistoricalScheduleAsync( @@ -2608,12 +2617,16 @@ async def reqHeadTimeStampAsync( return result - def reqSmartComponentsAsync(self, bboExchange): + def reqSmartComponentsAsync(self, bboExchange) -> Awaitable[list[SmartComponent]]: reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId) self.client.reqSmartComponents(reqId, bboExchange) - return future + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) def reqMktDepthExchangesAsync(self) -> Awaitable[list[DepthMktDataDescription]]: future = self.wrapper.startReq("mktDepthExchanges") @@ -2639,12 +2652,15 @@ def reqFundamentalDataAsync( fundamentalDataOptions: list[TagValue] = [], ) -> Awaitable[str]: reqId = self.client.getReqId() - - future = self.wrapper.startReq(reqId, contract) self.client.reqFundamentalData( reqId, contract, reportType, fundamentalDataOptions ) - return future + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) async def reqScannerDataAsync( self, @@ -2658,16 +2674,21 @@ async def reqScannerDataAsync( scannerSubscriptionFilterOptions or [], ) - future = self.wrapper.startReq(dataList.reqId, container=dataList) - await future + self.wrapper.startSubscription(dataList.reqId, dataList) + await dataList.subscription_bus.take(1).pluck(1).map(self._raise_if_error) - self.client.cancelScannerSubscription(dataList.reqId) - return future.result() + self.cancelScannerSubscription(dataList) + return dataList def reqScannerParametersAsync(self) -> Awaitable[str]: - future = self.wrapper.startReq("scannerParams") + reqId = "scannerParams" self.client.reqScannerParameters() - return future + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) async def calculateImpliedVolatilityAsync( self, @@ -2677,13 +2698,18 @@ async def calculateImpliedVolatilityAsync( implVolOptions: list[TagValue] = [], ) -> Optional[OptionComputation]: reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId, contract) self.client.calculateImpliedVolatility( reqId, contract, optionPrice, underPrice, implVolOptions ) try: - await asyncio.wait_for(future, 4) - return future.result() + awaitable = ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) + result = await asyncio.wait_for(awaitable, 4) + return result except asyncio.TimeoutError: self._logger.error("calculateImpliedVolatilityAsync: Timeout") return None @@ -2698,13 +2724,18 @@ async def calculateOptionPriceAsync( optPrcOptions: list[TagValue] = [], ) -> Optional[OptionComputation]: reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId, contract) self.client.calculateOptionPrice( reqId, contract, volatility, underPrice, optPrcOptions ) try: - await asyncio.wait_for(future, 4) - return future.result() + awaitable = ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) + result = await asyncio.wait_for(awaitable, 4) + return result except asyncio.TimeoutError: self._logger.error("calculateOptionPriceAsync: Timeout") return None @@ -2797,11 +2828,15 @@ async def getWshEventDataAsync(self, data: WshEventData) -> str: self.cancelWshEventData() return future.result() - def reqUserInfoAsync(self): + def reqUserInfoAsync(self) -> Awaitable[str]: reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId) self.client.reqUserInfo(reqId) - return future + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) if __name__ == "__main__": diff --git a/ib_async/message.py b/ib_async/message.py index 3fb30d91..d3eb156f 100644 --- a/ib_async/message.py +++ b/ib_async/message.py @@ -1,202 +1,182 @@ -from dataclasses import dataclass +from enum import IntEnum + class MessageId: """ - Encapsulates TWS API message ID constants and protocol-related logic. + Encapsulates TWS API message ID constants. """ - PROTOBUF_MSG_ID = 200 - """Protobuf message ID offset""" - - @dataclass(frozen=True, slots=True) - class IN: + class IN(IntEnum): """Incoming Message IDs (TWS -> Client)""" - TICK_PRICE = 1 - TICK_SIZE = 2 - ORDER_STATUS = 3 - ERR_MSG = 4 - OPEN_ORDER = 5 - ACCT_VALUE = 6 - PORTFOLIO_VALUE = 7 - ACCT_UPDATE_TIME = 8 - NEXT_VALID_ID = 9 - CONTRACT_DATA = 10 - EXECUTION_DATA = 11 - MARKET_DEPTH = 12 - MARKET_DEPTH_L2 = 13 - NEWS_BULLETINS = 14 - MANAGED_ACCTS = 15 - RECEIVE_FA = 16 - HISTORICAL_DATA = 17 - BOND_CONTRACT_DATA = 18 - SCANNER_PARAMETERS = 19 - SCANNER_DATA = 20 - TICK_OPTION_COMPUTATION = 21 - TICK_GENERIC = 45 - TICK_STRING = 46 - TICK_EFP = 47 - CURRENT_TIME = 49 - REAL_TIME_BARS = 50 - FUNDAMENTAL_DATA = 51 - CONTRACT_DATA_END = 52 - OPEN_ORDER_END = 53 - ACCT_DOWNLOAD_END = 54 - EXECUTION_DATA_END = 55 - DELTA_NEUTRAL_VALIDATION = 56 - TICK_SNAPSHOT_END = 57 - MARKET_DATA_TYPE = 58 - COMMISSION_AND_FEES_REPORT = 59 - POSITION_DATA = 61 - POSITION_END = 62 - ACCOUNT_SUMMARY = 63 - ACCOUNT_SUMMARY_END = 64 - VERIFY_MESSAGE_API = 65 - VERIFY_COMPLETED = 66 - DISPLAY_GROUP_LIST = 67 - DISPLAY_GROUP_UPDATED = 68 - VERIFY_AND_AUTH_MESSAGE_API = 69 - VERIFY_AND_AUTH_COMPLETED = 70 - POSITION_MULTI = 71 - POSITION_MULTI_END = 72 - ACCOUNT_UPDATE_MULTI = 73 - ACCOUNT_UPDATE_MULTI_END = 74 - SECURITY_DEFINITION_OPTION_PARAMETER = 75 - SECURITY_DEFINITION_OPTION_PARAMETER_END = 76 - SOFT_DOLLAR_TIERS = 77 - FAMILY_CODES = 78 - SYMBOL_SAMPLES = 79 - MKT_DEPTH_EXCHANGES = 80 - TICK_REQ_PARAMS = 81 - SMART_COMPONENTS = 82 - NEWS_ARTICLE = 83 - TICK_NEWS = 84 - NEWS_PROVIDERS = 85 - HISTORICAL_NEWS = 86 - HISTORICAL_NEWS_END = 87 - HEAD_TIMESTAMP = 88 - HISTOGRAM_DATA = 89 - HISTORICAL_DATA_UPDATE = 90 - REROUTE_MKT_DATA_REQ = 91 - REROUTE_MKT_DEPTH_REQ = 92 - MARKET_RULE = 93 - PNL = 94 - PNL_SINGLE = 95 - HISTORICAL_TICKS = 96 - HISTORICAL_TICKS_BID_ASK = 97 - HISTORICAL_TICKS_LAST = 98 - TICK_BY_TICK = 99 - ORDER_BOUND = 100 - COMPLETED_ORDER = 101 - COMPLETED_ORDERS_END = 102 - REPLACE_FA_END = 103 - WSH_META_DATA = 104 - WSH_EVENT_DATA = 105 - HISTORICAL_SCHEDULE = 106 - USER_INFO = 107 - HISTORICAL_DATA_END = 108 - CURRENT_TIME_IN_MILLIS = 109 + TICK_PRICE = 201 + TICK_SIZE = 202 + ORDER_STATUS = 203 + ERR_MSG = 204 + OPEN_ORDER = 205 + ACCT_VALUE = 206 + PORTFOLIO_VALUE = 207 + ACCT_UPDATE_TIME = 208 + NEXT_VALID_ID = 209 + CONTRACT_DATA = 210 + EXECUTION_DATA = 211 + MARKET_DEPTH = 212 + MARKET_DEPTH_L2 = 213 + NEWS_BULLETINS = 214 + MANAGED_ACCTS = 215 + RECEIVE_FA = 216 + HISTORICAL_DATA = 217 + BOND_CONTRACT_DATA = 218 + SCANNER_PARAMETERS = 219 + SCANNER_DATA = 220 + TICK_OPTION_COMPUTATION = 221 + TICK_GENERIC = 245 + TICK_STRING = 246 + TICK_EFP = 247 + CURRENT_TIME = 249 + REAL_TIME_BARS = 250 + FUNDAMENTAL_DATA = 251 + CONTRACT_DATA_END = 252 + OPEN_ORDER_END = 253 + ACCT_DOWNLOAD_END = 254 + EXECUTION_DATA_END = 255 + DELTA_NEUTRAL_VALIDATION = 256 + TICK_SNAPSHOT_END = 257 + MARKET_DATA_TYPE = 258 + COMMISSION_AND_FEES_REPORT = 259 + POSITION_DATA = 261 + POSITION_END = 262 + ACCOUNT_SUMMARY = 263 + ACCOUNT_SUMMARY_END = 264 + VERIFY_MESSAGE_API = 265 + VERIFY_COMPLETED = 266 + DISPLAY_GROUP_LIST = 267 + DISPLAY_GROUP_UPDATED = 268 + VERIFY_AND_AUTH_MESSAGE_API = 269 + VERIFY_AND_AUTH_COMPLETED = 270 + POSITION_MULTI = 271 + POSITION_MULTI_END = 272 + ACCOUNT_UPDATE_MULTI = 273 + ACCOUNT_UPDATE_MULTI_END = 274 + SECURITY_DEFINITION_OPTION_PARAMETER = 275 + SECURITY_DEFINITION_OPTION_PARAMETER_END = 276 + SOFT_DOLLAR_TIERS = 277 + FAMILY_CODES = 278 + SYMBOL_SAMPLES = 279 + MKT_DEPTH_EXCHANGES = 280 + TICK_REQ_PARAMS = 281 + SMART_COMPONENTS = 282 + NEWS_ARTICLE = 283 + TICK_NEWS = 284 + NEWS_PROVIDERS = 285 + HISTORICAL_NEWS = 286 + HISTORICAL_NEWS_END = 287 + HEAD_TIMESTAMP = 288 + HISTOGRAM_DATA = 289 + HISTORICAL_DATA_UPDATE = 290 + REROUTE_MKT_DATA_REQ = 291 + REROUTE_MKT_DEPTH_REQ = 292 + MARKET_RULE = 293 + PNL = 294 + PNL_SINGLE = 295 + HISTORICAL_TICKS = 296 + HISTORICAL_TICKS_BID_ASK = 297 + HISTORICAL_TICKS_LAST = 298 + TICK_BY_TICK = 299 + ORDER_BOUND = 300 + COMPLETED_ORDER = 301 + COMPLETED_ORDERS_END = 302 + REPLACE_FA_END = 303 + WSH_META_DATA = 304 + WSH_EVENT_DATA = 305 + HISTORICAL_SCHEDULE = 306 + USER_INFO = 307 + HISTORICAL_DATA_END = 308 + CURRENT_TIME_IN_MILLIS = 309 - @dataclass(frozen=True, slots=True) - class OUT: + class OUT(IntEnum): """Outgoing Message IDs (Client -> TWS)""" - REQ_MKT_DATA = 1 - CANCEL_MKT_DATA = 2 - PLACE_ORDER = 3 - CANCEL_ORDER = 4 - REQ_OPEN_ORDERS = 5 - REQ_ACCT_DATA = 6 - REQ_EXECUTIONS = 7 - REQ_IDS = 8 - REQ_CONTRACT_DATA = 9 - REQ_MKT_DEPTH = 10 - CANCEL_MKT_DEPTH = 11 - REQ_NEWS_BULLETINS = 12 - CANCEL_NEWS_BULLETINS = 13 - SET_SERVER_LOGLEVEL = 14 - REQ_AUTO_OPEN_ORDERS = 15 - REQ_ALL_OPEN_ORDERS = 16 - REQ_MANAGED_ACCTS = 17 - REQ_FA = 18 - REPLACE_FA = 19 - REQ_HISTORICAL_DATA = 20 - EXERCISE_OPTIONS = 21 - REQ_SCANNER_SUBSCRIPTION = 22 - CANCEL_SCANNER_SUBSCRIPTION = 23 - REQ_SCANNER_PARAMETERS = 24 - CANCEL_HISTORICAL_DATA = 25 - REQ_CURRENT_TIME = 49 - REQ_REAL_TIME_BARS = 50 - CANCEL_REAL_TIME_BARS = 51 - REQ_FUNDAMENTAL_DATA = 52 - CANCEL_FUNDAMENTAL_DATA = 53 - REQ_CALC_IMPLIED_VOLAT = 54 - REQ_CALC_OPTION_PRICE = 55 - CANCEL_CALC_IMPLIED_VOLAT = 56 - CANCEL_CALC_OPTION_PRICE = 57 - REQ_GLOBAL_CANCEL = 58 - REQ_MARKET_DATA_TYPE = 59 - REQ_POSITIONS = 61 - REQ_ACCOUNT_SUMMARY = 62 - CANCEL_ACCOUNT_SUMMARY = 63 - CANCEL_POSITIONS = 64 - VERIFY_REQUEST = 65 - VERIFY_MESSAGE = 66 - QUERY_DISPLAY_GROUPS = 67 - SUBSCRIBE_TO_GROUP_EVENTS = 68 - UPDATE_DISPLAY_GROUP = 69 - UNSUBSCRIBE_FROM_GROUP_EVENTS = 70 - START_API = 71 - VERIFY_AND_AUTH_REQUEST = 72 - VERIFY_AND_AUTH_MESSAGE = 73 - REQ_POSITIONS_MULTI = 74 - CANCEL_POSITIONS_MULTI = 75 - REQ_ACCOUNT_UPDATES_MULTI = 76 - CANCEL_ACCOUNT_UPDATES_MULTI = 77 - REQ_SEC_DEF_OPT_PARAMS = 78 - REQ_SOFT_DOLLAR_TIERS = 79 - REQ_FAMILY_CODES = 80 - REQ_MATCHING_SYMBOLS = 81 - REQ_MKT_DEPTH_EXCHANGES = 82 - REQ_SMART_COMPONENTS = 83 - REQ_NEWS_ARTICLE = 84 - REQ_NEWS_PROVIDERS = 85 - REQ_HISTORICAL_NEWS = 86 - REQ_HEAD_TIMESTAMP = 87 - REQ_HISTOGRAM_DATA = 88 - CANCEL_HISTOGRAM_DATA = 89 - CANCEL_HEAD_TIMESTAMP = 90 - REQ_MARKET_RULE = 91 - REQ_PNL = 92 - CANCEL_PNL = 93 - REQ_PNL_SINGLE = 94 - CANCEL_PNL_SINGLE = 95 - REQ_HISTORICAL_TICKS = 96 - REQ_TICK_BY_TICK_DATA = 97 - CANCEL_TICK_BY_TICK_DATA = 98 - REQ_COMPLETED_ORDERS = 99 - REQ_WSH_META_DATA = 100 - CANCEL_WSH_META_DATA = 101 - REQ_WSH_EVENT_DATA = 102 - CANCEL_WSH_EVENT_DATA = 103 - REQ_USER_INFO = 104 - REQ_CURRENT_TIME_IN_MILLIS = 105 - CANCEL_CONTRACT_DATA = 106 - CANCEL_HISTORICAL_TICKS = 107 - - @staticmethod - def to_protobuf(legacy_id: int) -> int: - """ - Converts a legacy outgoing ID to its Protobuf equivalent by adding the - Protobuf message ID offset. - """ - return legacy_id + MessageId.PROTOBUF_MSG_ID - - @staticmethod - def from_protobuf(proto_id: int) -> int: - """ - Converts an incoming Protobuf ID back to its legacy equivalent by - subtracting the Protobuf message ID offset. - """ - return proto_id - MessageId.PROTOBUF_MSG_ID + REQ_MKT_DATA = 201 + CANCEL_MKT_DATA = 202 + PLACE_ORDER = 203 + CANCEL_ORDER = 204 + REQ_OPEN_ORDERS = 205 + REQ_ACCT_DATA = 206 + REQ_EXECUTIONS = 207 + REQ_IDS = 208 + REQ_CONTRACT_DATA = 209 + REQ_MKT_DEPTH = 210 + CANCEL_MKT_DEPTH = 211 + REQ_NEWS_BULLETINS = 212 + CANCEL_NEWS_BULLETINS = 213 + SET_SERVER_LOGLEVEL = 214 + REQ_AUTO_OPEN_ORDERS = 215 + REQ_ALL_OPEN_ORDERS = 216 + REQ_MANAGED_ACCTS = 217 + REQ_FA = 218 + REPLACE_FA = 219 + REQ_HISTORICAL_DATA = 220 + EXERCISE_OPTIONS = 221 + REQ_SCANNER_SUBSCRIPTION = 222 + CANCEL_SCANNER_SUBSCRIPTION = 223 + REQ_SCANNER_PARAMETERS = 224 + CANCEL_HISTORICAL_DATA = 225 + REQ_CURRENT_TIME = 249 + REQ_REAL_TIME_BARS = 250 + CANCEL_REAL_TIME_BARS = 251 + REQ_FUNDAMENTAL_DATA = 252 + CANCEL_FUNDAMENTAL_DATA = 253 + REQ_CALC_IMPLIED_VOLAT = 254 + REQ_CALC_OPTION_PRICE = 255 + CANCEL_CALC_IMPLIED_VOLAT = 256 + CANCEL_CALC_OPTION_PRICE = 257 + REQ_GLOBAL_CANCEL = 258 + REQ_MARKET_DATA_TYPE = 259 + REQ_POSITIONS = 261 + REQ_ACCOUNT_SUMMARY = 262 + CANCEL_ACCOUNT_SUMMARY = 263 + CANCEL_POSITIONS = 264 + VERIFY_REQUEST = 265 + VERIFY_MESSAGE = 266 + QUERY_DISPLAY_GROUPS = 267 + SUBSCRIBE_TO_GROUP_EVENTS = 268 + UPDATE_DISPLAY_GROUP = 269 + UNSUBSCRIBE_FROM_GROUP_EVENTS = 270 + START_API = 271 + VERIFY_AND_AUTH_REQUEST = 272 + VERIFY_AND_AUTH_MESSAGE = 273 + REQ_POSITIONS_MULTI = 274 + CANCEL_POSITIONS_MULTI = 275 + REQ_ACCOUNT_UPDATES_MULTI = 276 + CANCEL_ACCOUNT_UPDATES_MULTI = 277 + REQ_SEC_DEF_OPT_PARAMS = 278 + REQ_SOFT_DOLLAR_TIERS = 279 + REQ_FAMILY_CODES = 280 + REQ_MATCHING_SYMBOLS = 281 + REQ_MKT_DEPTH_EXCHANGES = 282 + REQ_SMART_COMPONENTS = 283 + REQ_NEWS_ARTICLE = 284 + REQ_NEWS_PROVIDERS = 285 + REQ_HISTORICAL_NEWS = 286 + REQ_HEAD_TIMESTAMP = 287 + REQ_HISTOGRAM_DATA = 288 + CANCEL_HISTOGRAM_DATA = 289 + CANCEL_HEAD_TIMESTAMP = 290 + REQ_MARKET_RULE = 291 + REQ_PNL = 292 + CANCEL_PNL = 293 + REQ_PNL_SINGLE = 294 + CANCEL_PNL_SINGLE = 295 + REQ_HISTORICAL_TICKS = 296 + REQ_TICK_BY_TICK_DATA = 297 + CANCEL_TICK_BY_TICK_DATA = 298 + REQ_COMPLETED_ORDERS = 299 + REQ_WSH_META_DATA = 300 + CANCEL_WSH_META_DATA = 301 + REQ_WSH_EVENT_DATA = 302 + CANCEL_WSH_EVENT_DATA = 303 + REQ_USER_INFO = 304 + REQ_CURRENT_TIME_IN_MILLIS = 305 + CANCEL_CONTRACT_DATA = 306 + CANCEL_HISTORICAL_TICKS = 307 \ No newline at end of file diff --git a/ib_async/objects.py b/ib_async/objects.py index b59a53df..1c1cecaa 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -1,11 +1,15 @@ """Object hierarchy.""" from __future__ import annotations - from dataclasses import dataclass, field -from datetime import date as date_, datetime, timezone, tzinfo +from datetime import date as date_ +from datetime import datetime, timezone, tzinfo from enum import Enum -from typing import Any, List, NamedTuple, Optional, TypeAlias, Union +from typing import TYPE_CHECKING, Any, List, NamedTuple, Optional, TypeAlias, Union + +if TYPE_CHECKING: + from ib_async import IB + from eventkit import Event @@ -62,7 +66,7 @@ def __bool__(self): return bool(self.name or self.val or self.displayName) -@dataclass +@dataclass(slots=True) class Execution: execId: str = "" time: datetime = field(default=EPOCH) @@ -89,7 +93,7 @@ class Execution: ) -@dataclass +@dataclass(slots=True) class CommissionReport: execId: str = "" commission: float = 0.0 @@ -99,7 +103,7 @@ class CommissionReport: yieldRedemptionDate: int = 0 -@dataclass +@dataclass(slots=True) class ExecutionFilter: clientId: int = 0 acctCode: str = "" @@ -112,7 +116,7 @@ class ExecutionFilter: specificDates: list[int] = field(default_factory=list) -@dataclass +@dataclass(slots=True, frozen=True) class BarData: date: Union[date_, datetime] = EPOCH open: float = 0.0 @@ -124,7 +128,7 @@ class BarData: barCount: int = 0 -@dataclass +@dataclass(slots=True, frozen=True) class RealTimeBar: time: datetime = EPOCH endTime: int = -1 @@ -137,6 +141,64 @@ class RealTimeBar: count: int = 0 +@dataclass(slots=True, frozen=True) +class OptionComputation: + tickAttrib: int + impliedVol: float | None = None + delta: float | None = None + optPrice: float | None = None + pvDividend: float | None = None + gamma: float | None = None + vega: float | None = None + theta: float | None = None + undPrice: float | None = None + + def __add__(self, other: OptionComputation) -> OptionComputation: + if not isinstance(other, self.__class__): + raise TypeError(f"Cannot add {type(self)} and {type(other)}") + + return self.__class__( + tickAttrib=0, + impliedVol=(self.impliedVol or 0) + (other.impliedVol or 0), + delta=(self.delta or 0) + (other.delta or 0), + optPrice=(self.optPrice or 0) + (other.optPrice or 0), + gamma=(self.gamma or 0) + (other.gamma or 0), + vega=(self.vega or 0) + (other.vega or 0), + theta=(self.theta or 0) + (other.theta or 0), + undPrice=self.undPrice, + ) + + def __sub__(self, other: OptionComputation) -> OptionComputation: + if not isinstance(other, self.__class__): + raise TypeError(f"Cannot subtract {type(self)} and {type(other)}") + + return self.__class__( + tickAttrib=0, + impliedVol=(self.impliedVol or 0) - (other.impliedVol or 0), + delta=(self.delta or 0) - (other.delta or 0), + optPrice=(self.optPrice or 0) - (other.optPrice or 0), + gamma=(self.gamma or 0) - (other.gamma or 0), + vega=(self.vega or 0) - (other.vega or 0), + theta=(self.theta or 0) - (other.theta or 0), + undPrice=self.undPrice, + ) + + def __mul__(self, other: int | float) -> OptionComputation: + if not isinstance(other, (int, float)): + raise TypeError(f"Cannot multiply {type(self)} and {type(other)}") + + return self.__class__( + tickAttrib=0, + impliedVol=(self.impliedVol or 0) * other, + delta=(self.delta or 0) * other, + optPrice=(self.optPrice or 0) * other, + gamma=(self.gamma or 0) * other, + vega=(self.vega or 0) * other, + theta=(self.theta or 0) * other, + undPrice=self.undPrice, + ) + + class TickType(Enum): BID_SIZE = 0 BID = 1 @@ -283,7 +345,7 @@ class TickAttribLast: unreported: bool = False -@dataclass(frozen=True) +@dataclass(slots=True,frozen=True) class TickPriceData: """Data from a TickPriceProto message.""" @@ -294,7 +356,7 @@ class TickPriceData: attribs: TickAttrib -@dataclass(frozen=True) +@dataclass(slots=True, frozen=True) class TickSizeData: """Data from a TickSizeProto message.""" @@ -303,7 +365,7 @@ class TickSizeData: size: float -@dataclass(frozen=True) +@dataclass(slots=True, frozen=True) class TickStringData: """Data from a TickStringProto message.""" @@ -312,7 +374,7 @@ class TickStringData: value: str -@dataclass(frozen=True) +@dataclass(slots=True, frozen=True) class TickGenericData: """Data from a generic tick message.""" @@ -320,7 +382,8 @@ class TickGenericData: tickType: TickType value: float -@dataclass(frozen=True) + +@dataclass(slots=True, frozen=True) class TickComputationData: """Data from a TickComputationProto message.""" @@ -328,7 +391,8 @@ class TickComputationData: tickType: TickType computation: OptionComputation -@dataclass(frozen=True) + +@dataclass(slots=True, frozen=True) class TickByTickAllLastData: """Data from a TickByTickAllLastProto message.""" @@ -342,7 +406,7 @@ class TickByTickAllLastData: specialConditions: str -@dataclass(frozen=True) +@dataclass(slots=True, frozen=True) class TickByTickBidAskData: """Data from a TickByTickBidAskProto message.""" @@ -355,7 +419,7 @@ class TickByTickBidAskData: tickAttribBidAsk: TickAttribBidAsk -@dataclass(frozen=True) +@dataclass(slots=True, frozen=True) class TickByTickMidPointData: """Data from a TickByTickMidPointProto message.""" @@ -382,7 +446,7 @@ class HistogramData: count: int = 0 -@dataclass +@dataclass(slots=True) class NewsProvider: code: str = "" name: str = "" @@ -397,16 +461,22 @@ class DepthMktDataDescription: aggGroup: int = UNSET_INTEGER -@dataclass +@dataclass(slots=True) class PnL: account: str = "" modelCode: str = "" dailyPnL: float = nan unrealizedPnL: float = nan realizedPnL: float = nan + + def getKey(self): + """return PnL key + ie: ib.cancelPnL(pnl.getKey()) + """ + return (self.account, self.modelCode) -@dataclass +@dataclass(slots=True) class TradeLogEntry: time: datetime status: str = "" @@ -414,7 +484,7 @@ class TradeLogEntry: errorCode: int = 0 -@dataclass +@dataclass(slots=True) class PnLSingle: account: str = "" modelCode: str = "" @@ -424,16 +494,22 @@ class PnLSingle: realizedPnL: float = nan position: int = 0 value: float = nan + + def getKey(self): + """return PnLSingle key + ie: ib.cancelPnLSingle(pnl_single.getKey()) + """ + return (self.account, self.modelCode, self.conId) -@dataclass +@dataclass(slots=True, frozen=True) class HistoricalSession: startDateTime: str = "" endDateTime: str = "" refDate: str = "" -@dataclass +@dataclass(slots=True, frozen=True) class HistoricalSchedule: startDateTime: str = "" endDateTime: str = "" @@ -478,7 +554,7 @@ class HistoricalTickBidAsk: sizeAsk: float -@dataclass(slots=True) +@dataclass(slots=True, frozen=True) class HistoricalTickLast: time: datetime tickAttribLast: TickAttribLast @@ -550,72 +626,14 @@ class Position(NamedTuple): position: float avgCost: float - -class Fill(NamedTuple): +@dataclass(slots=True) +class Fill: contract: Contract execution: Execution commissionReport: CommissionReport time: datetime -@dataclass(slots=True, frozen=True) -class OptionComputation: - tickAttrib: int - impliedVol: float | None = None - delta: float | None = None - optPrice: float | None = None - pvDividend: float | None = None - gamma: float | None = None - vega: float | None = None - theta: float | None = None - undPrice: float | None = None - - def __add__(self, other: OptionComputation) -> OptionComputation: - if not isinstance(other, self.__class__): - raise TypeError(f"Cannot add {type(self)} and {type(other)}") - - return self.__class__( - tickAttrib=0, - impliedVol=(self.impliedVol or 0) + (other.impliedVol or 0), - delta=(self.delta or 0) + (other.delta or 0), - optPrice=(self.optPrice or 0) + (other.optPrice or 0), - gamma=(self.gamma or 0) + (other.gamma or 0), - vega=(self.vega or 0) + (other.vega or 0), - theta=(self.theta or 0) + (other.theta or 0), - undPrice=self.undPrice, - ) - - def __sub__(self, other: OptionComputation) -> OptionComputation: - if not isinstance(other, self.__class__): - raise TypeError(f"Cannot subtract {type(self)} and {type(other)}") - - return self.__class__( - tickAttrib=0, - impliedVol=(self.impliedVol or 0) - (other.impliedVol or 0), - delta=(self.delta or 0) - (other.delta or 0), - optPrice=(self.optPrice or 0) - (other.optPrice or 0), - gamma=(self.gamma or 0) - (other.gamma or 0), - vega=(self.vega or 0) - (other.vega or 0), - theta=(self.theta or 0) - (other.theta or 0), - undPrice=self.undPrice, - ) - - def __mul__(self, other: int | float) -> OptionComputation: - if not isinstance(other, (int, float)): - raise TypeError(f"Cannot multiply {type(self)} and {type(other)}") - - return self.__class__( - tickAttrib=0, - impliedVol=(self.impliedVol or 0) * other, - delta=(self.delta or 0) * other, - optPrice=(self.optPrice or 0) * other, - gamma=(self.gamma or 0) * other, - vega=(self.vega or 0) * other, - theta=(self.theta or 0) * other, - undPrice=self.undPrice, - ) - - @dataclass(slots=True, frozen=True) class OptionChain: exchange: str @@ -712,12 +730,31 @@ class BarDataList(List[BarData]): def __init__(self, *args): super().__init__(*args) self.updateEvent = Event("updateEvent") + self.subscription_bus = Event("Subscription bus") + def __eq__(self, other) -> bool: return self is other + def _on_data(self, ib: "IB", bar: BarData): + """Called on bar update when keepUpToDate=True.""" -class RealTimeBarList(List[RealTimeBar]): + lastDate = self[-1].date + if bar.date < lastDate: + return + + hasNewBar = len(self) == 0 or bar.date > lastDate + if hasNewBar: + self.append(bar) + elif self[-1] != bar: + self[-1] = bar + else: + return + ib.barUpdateEvent.emit(self, hasNewBar) + self.updateEvent.emit(self, hasNewBar) + + +class RealTimeBarList(list[RealTimeBar]): """ List of :class:`.RealTimeBar` that also stores all request parameters. @@ -732,17 +769,24 @@ class RealTimeBarList(List[RealTimeBar]): barSize: int whatToShow: str useRTH: bool - realTimeBarsOptions: List[TagValue] + realTimeBarsOptions: list[TagValue] def __init__(self, *args): super().__init__(*args) self.updateEvent = Event("updateEvent") + self.subscription_bus = Event("Subscription bus") def __eq__(self, other) -> bool: return self is other + def _on_data(self, ib: "IB", bar: RealTimeBar): + """Called on real time bar update.""" + self.append(bar) + ib.barUpdateEvent.emit(self, True) + self.updateEvent.emit(self, True) -class ScanDataList(List[ScanData]): + +class ScanDataList(list[ScanData]): """ List of :class:`.ScanData` that also stores all request parameters. @@ -752,15 +796,25 @@ class ScanDataList(List[ScanData]): reqId: int subscription: ScannerSubscription - scannerSubscriptionOptions: List[TagValue] - scannerSubscriptionFilterOptions: List[TagValue] + scannerSubscriptionOptions: list[TagValue] + scannerSubscriptionFilterOptions: list[TagValue] def __init__(self, *args): super().__init__(*args) self.updateEvent = Event("updateEvent") + self.subscription_bus = Event("Subscription bus") def __eq__(self, other): return self is other + + def _on_data(self, ib: "IB", data: ScanData): + """Called on scanner data.""" + rank = data[0].rank if 0 <= len(data) else None + if rank == 0: + self.clear() + self.extend(data) + ib.scannerDataEvent.emit(self) + self.updateEvent.emit(self) class DynamicObject: @@ -795,4 +849,3 @@ class IBDefaults: # optionally change the timezone used for log history events in objects (no impact on orders or data processing) timezone: tzinfo = timezone.utc - diff --git a/ib_async/order.py b/ib_async/order.py index 80e516fa..997502ed 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -6,7 +6,7 @@ from dataclasses import dataclass, field from decimal import Decimal -from typing import ClassVar, NamedTuple +from typing import ClassVar, NamedTuple, TypeAlias from eventkit import Event @@ -21,6 +21,8 @@ class Order: Order for trading contracts. https://interactivebrokers.github.io/tws-api/available_orders.html + + https://www.interactivebrokers.com/campus/ibkr-api-page/twsapi-ref/#order-ref """ orderId: int = 0 @@ -131,7 +133,7 @@ class Order: adjustedTrailingAmount: float | Decimal = UNSET_DOUBLE adjustableTrailingUnit: int = 0 lmtPriceOffset: float | Decimal = UNSET_DOUBLE - conditions: list[OrderCondition] = field(default_factory=list) + conditions: list[OrderConditionType] = field(default_factory=list) conditionsCancelOrder: bool = False conditionsIgnoreRth: bool = False extOperator: str = "" @@ -243,6 +245,11 @@ def __init__( @dataclass class OrderStatus: + """ + Reference: + https://ibkrcampus.com/campus/ibkr-api-page/twsapi-doc/#order-status + """ + orderId: int = 0 status: str = "" filled: float = 0.0 @@ -327,6 +334,19 @@ class OrderState: minCommission: float = UNSET_DOUBLE maxCommission: float = UNSET_DOUBLE commissionCurrency: str = "" + marginCurrency:str = "" + initMarginBeforeOutsideRTH:float = UNSET_DOUBLE # type: float + maintMarginBeforeOutsideRTH:float = UNSET_DOUBLE # type: float + equityWithLoanBeforeOutsideRTH:float = UNSET_DOUBLE # type: float + initMarginChangeOutsideRTH:float = UNSET_DOUBLE # type: float + maintMarginChangeOutsideRTH:float = UNSET_DOUBLE # type: float + equityWithLoanChangeOutsideRTH:float = UNSET_DOUBLE # type: float + initMarginAfterOutsideRTH:float = UNSET_DOUBLE # type: float + maintMarginAfterOutsideRTH:float = UNSET_DOUBLE # type: float + equityWithLoanAfterOutsideRTH:float = UNSET_DOUBLE # type: float + suggestedSize = UNSET_DECIMAL + rejectReason = "" + orderAllocations = None warningText: str = "" completedTime: str = "" completedStatus: str = "" @@ -563,8 +583,22 @@ class PercentChangeCondition(OrderCondition): exch: str = "" +OrderConditionType: TypeAlias = ( + PriceCondition + | TimeCondition + | MarginCondition + | ExecutionCondition + | VolumeCondition + | PercentChangeCondition +) + + @dataclass class OrderAllocation: + """ + Reference: https://ibkrcampus.com/campus/ibkr-api-page/twsapi-ref/#order-static-pub-func + """ + account = "" position: Decimal = UNSET_DECIMAL positionDesired: Decimal = UNSET_DECIMAL @@ -572,3 +606,24 @@ class OrderAllocation: desiredAllocQty: Decimal = UNSET_DECIMAL allowedAllocQty: Decimal = UNSET_DECIMAL isMonetary = False + + +@dataclass +class OrderCancel: + """ + Reference https://ibkrcampus.com/campus/ibkr-api-page/twsapi-ref/#orderallocation-ref + + manualOrderCancelTime: Used by brokers and advisors when manually entering an order + cancellation request. Format should be “YYYYMMDD-HH:mm:ss” + using UTC as the timezone value. + + extOperator: Following CME Rule 576, the ExtOperator field will signify the unique + API operator at the time of trading for order management. + + manualOrderIndicator: Following CME Rule 576, the ManualOrderIndicator field will + signify if an order is manual (1) or automated (0). + """ + + manualOrderCancelTime: str = "" + extOperator: str = "" + manualOrderIndicator: int = UNSET_INTEGER diff --git a/ib_async/protobuf_converters/__init__.py b/ib_async/protobuf_converters/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ib_async/protobuf_converters/account_converters.py b/ib_async/protobuf_converters/account_converters.py index 42285cc8..1e0d2696 100644 --- a/ib_async/protobuf_converters/account_converters.py +++ b/ib_async/protobuf_converters/account_converters.py @@ -2,21 +2,24 @@ Account data protobuf converters. """ +from ib_async.util import isValidIntValue + from ..objects import AccountValue, PortfolioItem, Position from ..protobuf.AccountDataRequest_pb2 import ( AccountDataRequest as AccountDataRequestProto, ) -from ..protobuf.AccountUpdatesMultiRequest_pb2 import ( - AccountUpdatesMultiRequest as AccountUpdatesMultiRequestProto, -) from ..protobuf.AccountSummary_pb2 import AccountSummary as AccountSummaryProto -from ..protobuf.AccountValue_pb2 import AccountValue as AccountValueProto from ..protobuf.AccountUpdateMulti_pb2 import ( AccountUpdateMulti as AccountUpdateMultiProto, ) +from ..protobuf.AccountUpdatesMultiRequest_pb2 import ( + AccountUpdatesMultiRequest as AccountUpdatesMultiRequestProto, +) +from ..protobuf.AccountValue_pb2 import AccountValue as AccountValueProto from ..protobuf.CancelAccountUpdatesMulti_pb2 import ( CancelAccountUpdatesMulti as CancelAccountUpdatesMultiProto, ) +from ..protobuf.IdsRequest_pb2 import IdsRequest as IdsRequestProto from ..protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto from ..protobuf.Position_pb2 import Position as PositionProto from .contract_converters import createContract @@ -169,3 +172,10 @@ def createPortfolioItem(portfolioValueProto: PortfolioValueProto) -> PortfolioIt realizedPNL=portfolioValueProto.realizedPNL, account=portfolioValueProto.accountName, ) + + +def createUserInfoRequestProto(reqId: int) -> IdsRequestProto: + idsRequestProto = IdsRequestProto() + if isValidIntValue(reqId): + idsRequestProto.numIds = reqId + return idsRequestProto diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py index 43e80003..8b622864 100644 --- a/ib_async/protobuf_converters/contract_converters.py +++ b/ib_async/protobuf_converters/contract_converters.py @@ -7,9 +7,10 @@ ContractDetails, DeltaNeutralContract, FundAssetType, - FundDistributionPolicyIndicator, IneligibilityReason + FundDistributionPolicyIndicator, + IneligibilityReason, ) -from ..objects import OptionChain +from ..objects import OptionChain, SmartComponent from ..order import Order from ..protobuf.ComboLeg_pb2 import ComboLeg as ComboLegProto from ..protobuf.Contract_pb2 import Contract as ContractProto @@ -31,10 +32,15 @@ from ..protobuf.SecDefOptParamsRequest_pb2 import ( SecDefOptParamsRequest as SecDefOptParamsRequestProto, ) +from ..protobuf.SmartComponentsRequest_pb2 import ( + SmartComponentsRequest as SmartComponentsRequestProto, +) +from ..protobuf.SmartComponents_pb2 import SmartComponents as SmartComponentsProto from ..util import ( UNSET_DOUBLE, floatMaxString, getEnumTypeFromString, + isValidIntValue, ) @@ -494,7 +500,7 @@ def createComboLegProtoList( for i, comboLeg in enumerate(comboLegs): perLegPrice = UNSET_DOUBLE if orderComboLegs and i < len(orderComboLegs): - perLegPrice:float = orderComboLegs[i].price + perLegPrice: float = orderComboLegs[i].price comboLegProto = createComboLegProto(comboLeg, perLegPrice) if comboLegProto is not None: comboLegProtoList.append(comboLegProto) @@ -522,3 +528,41 @@ def createComboLegProto(comboLeg: ComboLeg, perLegPrice: float) -> ComboLegProto if comboLegProto.perLegPrice is not None: comboLegProto.perLegPrice = perLegPrice return comboLegProto + + +def createSmartComponentsRequestProto( + reqId: int, bboExchange: str +) -> SmartComponentsRequestProto: + smartComponentsRequestProto = SmartComponentsRequestProto() + if isValidIntValue(reqId): + smartComponentsRequestProto.reqId = reqId + if bboExchange: + smartComponentsRequestProto.bboExchange = bboExchange + return smartComponentsRequestProto + + +def createSmartComponents( + smartComponentsProto: SmartComponentsProto, +) -> list[SmartComponent]: + smartComponents = [] + if smartComponentsProto and smartComponentsProto.smartComponents: + for smartComponentProto in smartComponentsProto.smartComponents: + bitNumber = ( + smartComponentProto.bitNumber + if smartComponentProto.HasField("bitNumber") + else 0 + ) + exchange = ( + smartComponentProto.exchange + if smartComponentProto.HasField("exchange") + else "" + ) + exchangeLetter = ( + smartComponentProto.exchangeLetter + if smartComponentProto.HasField("exchangeLetter") + else " " + ) + smartComponents.append( + SmartComponent(bitNumber, exchange, exchangeLetter) + ) + return smartComponents diff --git a/ib_async/protobuf_converters/historical_data_converters.py b/ib_async/protobuf_converters/historical_data_converters.py index afcde89a..3a4e9de9 100644 --- a/ib_async/protobuf_converters/historical_data_converters.py +++ b/ib_async/protobuf_converters/historical_data_converters.py @@ -1,6 +1,6 @@ """Historical data protobuf converters""" -from datetime import datetime, tzinfo +from datetime import tzinfo from ..contract import Contract, TagValue from ..objects import ( @@ -11,9 +11,17 @@ HistoricalTick, HistoricalTickBidAsk, HistoricalTickLast, + IBDefaults, + RealTimeBar, TickAttribBidAsk, TickAttribLast, ) +from ..protobuf.CancelHistoricalData_pb2 import ( + CancelHistoricalData as CancelHistoricalDataProto, +) +from ..protobuf.FundamentalsDataRequest_pb2 import ( + FundamentalsDataRequest as FundamentalsDataRequestProto, +) from ..protobuf.HeadTimestampRequest_pb2 import ( HeadTimestampRequest as HeadTimestampRequestProto, ) @@ -32,7 +40,6 @@ from ..protobuf.HistoricalSchedule_pb2 import ( HistoricalSchedule as HistoricalScheduleProto, ) -from ..protobuf.HistoricalSession_pb2 import HistoricalSession as HistoricalSessionProto from ..protobuf.HistoricalTick_pb2 import HistoricalTick as HistoricalTickProto from ..protobuf.HistoricalTickBidAsk_pb2 import ( HistoricalTickBidAsk as HistoricalTickBidAskProto, @@ -43,9 +50,17 @@ from ..protobuf.HistoricalTicksRequest_pb2 import ( HistoricalTicksRequest as HistoricalTicksRequestProto, ) -from ..protobuf.TickAttribBidAsk_pb2 import TickAttribBidAsk as TickAttribBidAskProto -from ..protobuf.TickAttribLast_pb2 import TickAttribLast as TickAttribLastProto -from ..util import NO_VALID_ID, isValidIntValue, parseIBDatetime +from ..protobuf.RealTimeBarsRequest_pb2 import ( + RealTimeBarsRequest as RealTimeBarsRequestProto, +) +from ..protobuf.RealTimeBarTick_pb2 import RealTimeBarTick as RealTimeBarTickProto +from ..util import ( + EPOCH, + UNSET_DOUBLE, + isValidIntValue, + parseIBDatetime, + parseIBTimeStamp, +) from .contract_converters import createContractProto @@ -108,33 +123,69 @@ def createHistoricalDataRequestProto( fillTagValueList(chartOptionsList, historicalDataRequestProto.chartOptions) return historicalDataRequestProto +def createCancelHistoricalDataProto(reqId: int) -> CancelHistoricalDataProto: + cancelHistoricalDataProto = CancelHistoricalDataProto() + if isValidIntValue(reqId): cancelHistoricalDataProto.reqId = reqId + return cancelHistoricalDataProto + + +def createRealTimeBarsRequestProto( + reqId: int, + contract: Contract, + barSize: int, + whatToShow: str, + useRTH: bool, + realTimeBarsOptionsList: list[TagValue], +) -> RealTimeBarsRequestProto: + realTimeBarsRequestProto = RealTimeBarsRequestProto() + if isValidIntValue(reqId): + realTimeBarsRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + if contractProto is not None: + realTimeBarsRequestProto.contract.CopyFrom(contractProto) + if isValidIntValue(barSize): + realTimeBarsRequestProto.barSize = barSize + if whatToShow: + realTimeBarsRequestProto.whatToShow = whatToShow + if useRTH: + realTimeBarsRequestProto.useRTH = useRTH + fillTagValueList( + realTimeBarsOptionsList, realTimeBarsRequestProto.realTimeBarsOptions + ) + return realTimeBarsRequestProto + def createBarDataList( historicalDataBarsProto: list[HistoricalDataBarProto], ) -> list[BarData]: bars = [] for barProto in historicalDataBarsProto: - bar = BarData() - if barProto.HasField("date"): - bar.date = parseIBDatetime(barProto.date) - if barProto.HasField("open"): - bar.open = barProto.open - if barProto.HasField("high"): - bar.high = barProto.high - if barProto.HasField("low"): - bar.low = barProto.low - if barProto.HasField("close"): - bar.close = barProto.close - if barProto.HasField("volume"): - bar.volume = float(barProto.volume) - if barProto.HasField("WAP"): - bar.average = float(barProto.WAP) - if barProto.HasField("barCount"): - bar.barCount = barProto.barCount + bar = createBarData(barProto) bars.append(bar) return bars +def createBarData(historicalDataBarProto: HistoricalDataBarProto) -> BarData: + if historicalDataBarProto.HasField("date"): + date = parseIBDatetime(historicalDataBarProto.date) + if historicalDataBarProto.HasField("open"): + open_ = historicalDataBarProto.open + if historicalDataBarProto.HasField("high"): + high = historicalDataBarProto.high + if historicalDataBarProto.HasField("low"): + low = historicalDataBarProto.low + if historicalDataBarProto.HasField("close"): + close = historicalDataBarProto.close + if historicalDataBarProto.HasField("volume"): + volume = float(historicalDataBarProto.volume) + if historicalDataBarProto.HasField("WAP"): + average = float(historicalDataBarProto.WAP) + if historicalDataBarProto.HasField("barCount"): + barCount = historicalDataBarProto.barCount + bar = BarData(date, open_, high, low, close, volume, average, barCount) + return bar + + def createHistoricalTicksRequestProto( reqId: int, contract: Contract, @@ -171,9 +222,13 @@ def createHistoricalTicksRequestProto( def createHistoricalTick( historicalTickProto: HistoricalTickProto, tz: tzinfo ) -> HistoricalTick: - time = datetime.fromtimestamp(historicalTickProto.time, tz) + time = parseIBTimeStamp(historicalTickProto.time, tz) price = historicalTickProto.price - size = float(historicalTickProto.size) + size = ( + float(historicalTickProto.size) + if historicalTickProto.size + else IBDefaults.emptySize + ) historicalTick = HistoricalTick(time, price, size) return historicalTick @@ -181,7 +236,7 @@ def createHistoricalTick( def createHistoricalTickBidAsk( historicalTickBidAskProto: HistoricalTickBidAskProto, tz: tzinfo ) -> HistoricalTickBidAsk: - time = datetime.fromtimestamp(historicalTickBidAskProto.time, tz) + time = parseIBTimeStamp(historicalTickBidAskProto.time, tz) tickAttribBidAskProto = historicalTickBidAskProto.tickAttribBidAsk bidPastLow = tickAttribBidAskProto.bidPastLow @@ -203,8 +258,7 @@ def createHistoricalTickBidAsk( def createHistoricalTickLast( historicalTickLastProto: HistoricalTickLastProto, tz: tzinfo ) -> HistoricalTickLast: - time = datetime.fromtimestamp(historicalTickLastProto.time, tz) - + time = parseIBTimeStamp(historicalTickLastProto.time, tz) tickAttribLastProto = historicalTickLastProto.tickAttribLast pastLimit = tickAttribLastProto.pastLimit unreported = tickAttribLastProto.unreported @@ -252,17 +306,17 @@ def createHistogramDataEntry( def createHistoricalSchedule( historicalScheduleProto: HistoricalScheduleProto, ) -> HistoricalSchedule: - startDateTime = ( + _startDateTime = ( historicalScheduleProto.startDateTime if historicalScheduleProto.HasField("startDateTime") else "" ) - endDateTime = ( + _endDateTime = ( historicalScheduleProto.endDateTime if historicalScheduleProto.HasField("endDateTime") else "" ) - timeZone = ( + _timeZone = ( historicalScheduleProto.timeZone if historicalScheduleProto.HasField("timeZone") else "" @@ -271,25 +325,79 @@ def createHistoricalSchedule( sessions = [] if historicalScheduleProto.historicalSessions: for historicalSessionProto in historicalScheduleProto.historicalSessions: - historicalSession = HistoricalSession() - historicalSession.startDateTime = ( + startDateTime = ( historicalSessionProto.startDateTime if historicalSessionProto.HasField("startDateTime") else "" ) - historicalSession.endDateTime = ( + endDateTime = ( historicalSessionProto.endDateTime if historicalSessionProto.HasField("endDateTime") else "" ) - historicalSession.refDate = ( + refDate = ( historicalSessionProto.refDate if historicalSessionProto.HasField("refDate") else "" ) + historicalSession = HistoricalSession( + startDateTime, + endDateTime, + refDate, + ) sessions.append(historicalSession) historicalSchedule = HistoricalSchedule( - startDateTime, endDateTime, timeZone, sessions + _startDateTime, _endDateTime, _timeZone, sessions ) return historicalSchedule + + +def createRealTimeBarTick( + realTimeBarTickProto: RealTimeBarTickProto, tz: tzinfo +) -> RealTimeBar: + time = ( + parseIBTimeStamp(realTimeBarTickProto.time, tz) + if realTimeBarTickProto.HasField("time") + else EPOCH + ) + open_ = realTimeBarTickProto.open if realTimeBarTickProto.HasField("open") else 0.0 + high = realTimeBarTickProto.high if realTimeBarTickProto.HasField("high") else 0.0 + low = realTimeBarTickProto.low if realTimeBarTickProto.HasField("low") else 0.0 + close = ( + realTimeBarTickProto.close if realTimeBarTickProto.HasField("close") else 0.0 + ) + volume = ( + float(realTimeBarTickProto.volume) + if realTimeBarTickProto.HasField("volume") + else UNSET_DOUBLE + ) + wap = ( + float(realTimeBarTickProto.WAP) + if realTimeBarTickProto.HasField("WAP") + else UNSET_DOUBLE + ) + count = realTimeBarTickProto.count if realTimeBarTickProto.HasField("count") else 0 + realTimeBar = RealTimeBar(time, -1, open_, high, low, close, volume, wap, count) + return realTimeBar + + +def createFundamentalsDataRequestProto( + reqId: int, + contract: Contract, + reportType: str, + fundamentalsDataOptionsList: list[TagValue], +) -> FundamentalsDataRequestProto: + fundamentalsDataRequestProto = FundamentalsDataRequestProto() + if isValidIntValue(reqId): + fundamentalsDataRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + if contractProto is not None: + fundamentalsDataRequestProto.contract.CopyFrom(contractProto) + if reportType: + fundamentalsDataRequestProto.reportType = reportType + fillTagValueList( + fundamentalsDataOptionsList, + fundamentalsDataRequestProto.fundamentalsDataOptions, + ) + return fundamentalsDataRequestProto diff --git a/ib_async/protobuf_converters/market_data_converters.py b/ib_async/protobuf_converters/market_data_converters.py index 3fbc9d89..fa3e79db 100644 --- a/ib_async/protobuf_converters/market_data_converters.py +++ b/ib_async/protobuf_converters/market_data_converters.py @@ -2,50 +2,53 @@ Market data protobuf converters """ -from dataclasses import dataclass - from ..objects import ( Contract, OptionComputation, TagValue, TickAttrib, - TickAttribBidAsk, - TickAttribLast, - TickByTickAllLastData, - TickByTickBidAskData, - TickByTickMidPointData, + TickComputationData, TickGenericData, TickParams, TickPriceData, TickSizeData, TickStringData, TickType, - TickComputationData, ) +from ..protobuf.CalculateImpliedVolatilityRequest_pb2 import ( + CalculateImpliedVolatilityRequest as CalculateImpliedVolatilityRequestProto, +) +from ..protobuf.CalculateOptionPriceRequest_pb2 import ( + CalculateOptionPriceRequest as CalculateOptionPriceRequestProto, +) +from ..protobuf.CancelCalculateImpliedVolatility_pb2 import ( + CancelCalculateImpliedVolatility as CancelCalculateImpliedVolatilityProto, +) +from ..protobuf.CancelCalculateOptionPrice_pb2 import ( + CancelCalculateOptionPrice as CancelCalculateOptionPriceProto, +) +from ..protobuf.CancelMarketData_pb2 import CancelMarketData as CancelMarketDataProto from ..protobuf.MarketDataRequest_pb2 import ( MarketDataRequest as MarketDataRequestProto, ) -from ..protobuf.MarketDataType_pb2 import MarketDataType as MarketDataTypeProto -from ..protobuf.TickPrice_pb2 import TickPrice as TickPriceProto -from ..protobuf.TickSize_pb2 import TickSize as TickSizeProto -from ..protobuf.TickString_pb2 import TickString as TickStringProto +from ..protobuf.MarketDataTypeRequest_pb2 import ( + MarketDataTypeRequest as MarketDataTypeRequestProto, +) +from ..protobuf.TickByTickRequest_pb2 import TickByTickRequest as TickByTickRequestProto from ..protobuf.TickGeneric_pb2 import TickGeneric as TickGenericProto from ..protobuf.TickOptionComputation_pb2 import ( TickOptionComputation as TickOptionComputationProto, ) +from ..protobuf.TickPrice_pb2 import TickPrice as TickPriceProto from ..protobuf.TickReqParams_pb2 import TickReqParams as TickReqParamsProto -from ..protobuf.HistoricalTickLast_pb2 import ( - HistoricalTickLast as HistoricalTickLastProto, -) -from ..protobuf.HistoricalTickBidAsk_pb2 import ( - HistoricalTickBidAsk as HistoricalTickBidAskProto, -) -from ..protobuf.MarketDataTypeRequest_pb2 import ( - MarketDataTypeRequest as MarketDataTypeRequestProto, +from ..protobuf.TickSize_pb2 import TickSize as TickSizeProto +from ..protobuf.TickString_pb2 import TickString as TickStringProto +from ..util import ( + NO_VALID_ID, + UNSET_DOUBLE, + UNSET_INTEGER, + isValidIntValue, ) -from ..protobuf.HistoricalTick_pb2 import HistoricalTick as HistoricalTickProt -from ..protobuf.CancelMarketData_pb2 import CancelMarketData as CancelMarketDataProto -from ..util import NO_VALID_ID, UNSET_DOUBLE, UNSET_INTEGER, isValidIntValue from .contract_converters import createContractProto from .historical_data_converters import fillTagValueList @@ -88,6 +91,24 @@ def cancelMarketDataProto(reqId: int) -> CancelMarketDataProto: return cancelMarketDataProto +def createTickByTickRequestProto( + reqId: int, contract: Contract, tickType: str, numberOfTicks: int, ignoreSize: bool +) -> TickByTickRequestProto: + tickByTickRequestProto = TickByTickRequestProto() + if isValidIntValue(reqId): + tickByTickRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + if contractProto is not None: + tickByTickRequestProto.contract.CopyFrom(contractProto) + if tickType: + tickByTickRequestProto.tickType = tickType + if isValidIntValue(numberOfTicks): + tickByTickRequestProto.numberOfTicks = numberOfTicks + if ignoreSize: + tickByTickRequestProto.ignoreSize = ignoreSize + return tickByTickRequestProto + + def createTickParams(msg: TickReqParamsProto) -> TickParams: """Create a TickParams object from a TickReqParamsProto message.""" reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID @@ -183,9 +204,7 @@ def createTickGenericData(msg: TickGenericProto) -> TickGenericData: def createTickOptionComputation(msg: TickOptionComputationProto) -> TickComputationData: """Create a OptionComputation object from a TickOptionCompuationProto message.""" - tickType = ( - TickType(msg.tickType) if msg.HasField("tickType") else TickType.NOT_SET - ) + tickType = TickType(msg.tickType) if msg.HasField("tickType") else TickType.NOT_SET tickAttrib = msg.tickAttrib if msg.HasField("tickAttrib") else UNSET_INTEGER impliedVol = msg.impliedVol if msg.HasField("impliedVol") else None if impliedVol and impliedVol < 0: # -1 is the "not computed" indicator @@ -223,7 +242,7 @@ def createTickOptionComputation(msg: TickOptionComputationProto) -> TickComputat theta if theta != -2 else theta, undPrice if undPrice != -1 else None, ) - + tick_comp = TickComputationData( reqId=msg.reqId, tickType=tickType, @@ -232,61 +251,66 @@ def createTickOptionComputation(msg: TickOptionComputationProto) -> TickComputat return tick_comp -# def create_tick_by_tick_all_last_data( -# msg: TickByTickAllLastProto, -# ) -> TickByTickAllLastData: -# """Create a TickByTickAllLastData object from a TickByTickAllLastProto message.""" -# if msg.HasField("reqId"): -# reqId = msg.reqId -# if msg.HasField("tickType"): -# tickType = msg.tickType -# if msg.HasField("historicalTickLast"): -# historicalTickLast = msg.historicalTickLast -# if msg.HasField("historicalTickBidAsk"): -# historicalTickBidAsk = msg.historicalTickBidAsk -# if msg.HasField("historicalTickMidPoint"): -# historicalTickMidPoint = msg.historicalTickMidPoint - -# tickByTickAllLastData = TickByTickAllLastData( -# reqId=reqId, -# tickType=tickType, -# time=msg.time, -# price=msg.price, -# size=msg.size, -# tickAttribLast=TickAttribLast( -# pastLimit=msg.tickAttribLast.pastLimit, -# unreported=msg.tickAttribLast.unreported, -# ), -# exchange=msg.exchange, -# specialConditions=msg.specialConditions, -# ) -# return tickByTickAllLastData - - -# def create_tick_by_tick_bid_ask_data( -# msg: TickByTickBidAskProto, -# ) -> TickByTickBidAskData: -# """Create a TickByTickBidAskData object from a TickByTickBidAskProto message.""" -# return TickByTickBidAskData( -# reqId=msg.reqId, -# time=msg.time, -# bidPrice=msg.bidPrice, -# askPrice=msg.askPrice, -# bidSize=msg.bidSize, -# askSize=msg.askSize, -# tickAttribBidAsk=TickAttribBidAsk( -# bidPastLow=msg.tickAttribBidAsk.bidPastLow, -# askPastHigh=msg.tickAttribBidAsk.askPastHigh, -# ), -# ) - - -# def create_tick_by_tick_mid_point_data( -# msg: TickByTickMidPointProto, -# ) -> TickByTickMidPointData: -# """Create a TickByTickMidPointData object from a TickByTickMidPointProto message.""" -# return TickByTickMidPointData( -# reqId=msg.reqId, -# time=msg.time, -# midPoint=msg.midPoint, -# ) +def createCalculateImpliedVolatilityRequestProto( + reqId: int, + contract: Contract, + optionPrice: float, + underPrice: float, + impliedVolatilityOptionsList: list[TagValue], +) -> CalculateImpliedVolatilityRequestProto: + calculateImpliedVolatilityRequestProto = CalculateImpliedVolatilityRequestProto() + if isValidIntValue(reqId): + calculateImpliedVolatilityRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + if contractProto is not None: + calculateImpliedVolatilityRequestProto.contract.CopyFrom(contractProto) + if optionPrice != UNSET_DOUBLE: + calculateImpliedVolatilityRequestProto.optionPrice = optionPrice + if underPrice != UNSET_DOUBLE: + calculateImpliedVolatilityRequestProto.underPrice = underPrice + fillTagValueList( + impliedVolatilityOptionsList, + calculateImpliedVolatilityRequestProto.impliedVolatilityOptions, + ) + return calculateImpliedVolatilityRequestProto + + +def createCalculateOptionPriceRequestProto( + reqId: int, + contract: Contract, + volatility: float, + underPrice: float, + optionPriceOptionsList: list[TagValue], +) -> CalculateOptionPriceRequestProto: + calculateOptionPriceRequestProto = CalculateOptionPriceRequestProto() + if isValidIntValue(reqId): + calculateOptionPriceRequestProto.reqId = reqId + contractProto = createContractProto(contract, None) + if contractProto is not None: + calculateOptionPriceRequestProto.contract.CopyFrom(contractProto) + if volatility != UNSET_DOUBLE: + calculateOptionPriceRequestProto.volatility = volatility + if underPrice != UNSET_DOUBLE: + calculateOptionPriceRequestProto.underPrice = underPrice + fillTagValueList( + optionPriceOptionsList, calculateOptionPriceRequestProto.optionPriceOptions + ) + return calculateOptionPriceRequestProto + + +def createCancelCalculateImpliedVolatilityProto( + reqId: int, +) -> CancelCalculateImpliedVolatilityProto: + cancelCalculateImpliedVolatilityProto = CancelCalculateImpliedVolatilityProto() + if isValidIntValue(reqId): + cancelCalculateImpliedVolatilityProto.reqId = reqId + return cancelCalculateImpliedVolatilityProto + + +def createCancelCalculateOptionPriceProto( + reqId: int, +) -> CancelCalculateOptionPriceProto: + cancelCalculateOptionPriceProto = CancelCalculateOptionPriceProto() + if isValidIntValue(reqId): + cancelCalculateOptionPriceProto.reqId = reqId + return cancelCalculateOptionPriceProto diff --git a/ib_async/protobuf_converters/subscription_converters.py b/ib_async/protobuf_converters/subscription_converters.py new file mode 100644 index 00000000..9de5292d --- /dev/null +++ b/ib_async/protobuf_converters/subscription_converters.py @@ -0,0 +1,193 @@ +import logging +from ib_async.objects import ScannerSubscription, TagValue +from ib_async.util import UNSET_DOUBLE, isValidIntValue +from ib_async.contract import ScanData, ContractDetails + +from ..protobuf.CancelPnL_pb2 import CancelPnL as CancelPnLProto +from ..protobuf.CancelPnLSingle_pb2 import CancelPnLSingle as CancelPnLSingleProto +from ..protobuf.CancelScannerSubscription_pb2 import ( + CancelScannerSubscription as CancelScannerSubscriptionProto, +) +from ..protobuf.PnLRequest_pb2 import PnLRequest as PnLRequestProto +from ..protobuf.PnLSingleRequest_pb2 import PnLSingleRequest as PnLSingleRequestProto +from ..protobuf.ScannerParametersRequest_pb2 import ( + ScannerParametersRequest as ScannerParametersRequestProto, +) +from ..protobuf.ScannerSubscription_pb2 import ( + ScannerSubscription as ScannerSubscriptionProto, +) +from ..protobuf.ScannerData_pb2 import ScannerData as ScannerDataProto +from ..protobuf.ScannerSubscriptionRequest_pb2 import ( + ScannerSubscriptionRequest as ScannerSubscriptionRequestProto, +) +from .historical_data_converters import fillTagValueList +from .contract_converters import createContract + + +def createScannerParametersRequestProto() -> ScannerParametersRequestProto: + scannerParametersRequestProto = ScannerParametersRequestProto() + return scannerParametersRequestProto + + +def createScannerSubscriptionRequestProto( + reqId: int, + subscription: ScannerSubscription, + scannerSubscriptionOptionsList: list[TagValue], + scannerSubscriptionFilterOptionsList: list[TagValue], +) -> ScannerSubscriptionRequestProto: + scannerSubscriptionRequestProto = ScannerSubscriptionRequestProto() + if isValidIntValue(reqId): + scannerSubscriptionRequestProto.reqId = reqId + scannerSubscriptionProto = createScannerSubscriptionProto( + subscription, + scannerSubscriptionOptionsList, + scannerSubscriptionFilterOptionsList, + ) + if scannerSubscriptionProto is not None: + scannerSubscriptionRequestProto.scannerSubscription.CopyFrom( + scannerSubscriptionProto + ) + return scannerSubscriptionRequestProto + + +def createScannerSubscriptionProto( + subscription: ScannerSubscription, + scannerSubscriptionOptionsList: list[TagValue], + scannerSubscriptionFilterOptionsList: list[TagValue], +) -> ScannerSubscriptionProto: + if subscription is None: + return None + scannerSubscriptionProto = ScannerSubscriptionProto() + if isValidIntValue(subscription.numberOfRows): + scannerSubscriptionProto.numberOfRows = subscription.numberOfRows + if subscription.instrument: + scannerSubscriptionProto.instrument = subscription.instrument + if subscription.locationCode: + scannerSubscriptionProto.locationCode = subscription.locationCode + if subscription.scanCode: + scannerSubscriptionProto.scanCode = subscription.scanCode + if subscription.abovePrice != UNSET_DOUBLE: + scannerSubscriptionProto.abovePrice = subscription.abovePrice + if subscription.belowPrice != UNSET_DOUBLE: + scannerSubscriptionProto.belowPrice = subscription.belowPrice + if isValidIntValue(subscription.aboveVolume): + scannerSubscriptionProto.aboveVolume = subscription.aboveVolume + if isValidIntValue(subscription.averageOptionVolumeAbove): + scannerSubscriptionProto.averageOptionVolumeAbove = ( + subscription.averageOptionVolumeAbove + ) + if subscription.marketCapAbove != UNSET_DOUBLE: + scannerSubscriptionProto.marketCapAbove = subscription.marketCapAbove + if subscription.marketCapBelow != UNSET_DOUBLE: + scannerSubscriptionProto.marketCapBelow = subscription.marketCapBelow + if subscription.moodyRatingAbove: + scannerSubscriptionProto.moodyRatingAbove = subscription.moodyRatingAbove + if subscription.moodyRatingBelow: + scannerSubscriptionProto.moodyRatingBelow = subscription.moodyRatingBelow + if subscription.spRatingAbove: + scannerSubscriptionProto.spRatingAbove = subscription.spRatingAbove + if subscription.spRatingBelow: + scannerSubscriptionProto.spRatingBelow = subscription.spRatingBelow + if subscription.maturityDateAbove: + scannerSubscriptionProto.maturityDateAbove = subscription.maturityDateAbove + if subscription.maturityDateBelow: + scannerSubscriptionProto.maturityDateBelow = subscription.maturityDateBelow + if subscription.couponRateAbove != UNSET_DOUBLE: + scannerSubscriptionProto.couponRateAbove = subscription.couponRateAbove + if subscription.couponRateBelow != UNSET_DOUBLE: + scannerSubscriptionProto.couponRateBelow = subscription.couponRateBelow + if subscription.excludeConvertible: + scannerSubscriptionProto.excludeConvertible = subscription.excludeConvertible + if subscription.scannerSettingPairs: + scannerSubscriptionProto.scannerSettingPairs = subscription.scannerSettingPairs + if subscription.stockTypeFilter: + scannerSubscriptionProto.stockTypeFilter = subscription.stockTypeFilter + fillTagValueList( + scannerSubscriptionOptionsList, + scannerSubscriptionProto.scannerSubscriptionOptions, + ) + fillTagValueList( + scannerSubscriptionFilterOptionsList, + scannerSubscriptionProto.scannerSubscriptionFilterOptions, + ) + return scannerSubscriptionProto + + +def createCancelScannerSubscriptionProto(reqId: int) -> CancelScannerSubscriptionProto: + cancelScannerSubscriptionProto = CancelScannerSubscriptionProto() + if isValidIntValue(reqId): + cancelScannerSubscriptionProto.reqId = reqId + return cancelScannerSubscriptionProto + + +def createPnLRequestProto(reqId: int, account: str, modelCode: str) -> PnLRequestProto: + pnlRequestProto = PnLRequestProto() + if isValidIntValue(reqId): + pnlRequestProto.reqId = reqId + if account: + pnlRequestProto.account = account + if modelCode: + pnlRequestProto.modelCode = modelCode + return pnlRequestProto + + +def createCancelPnLProto(reqId: int) -> CancelPnLProto: + cancelPnLProto = CancelPnLProto() + if isValidIntValue(reqId): + cancelPnLProto.reqId = reqId + return cancelPnLProto + + +def createPnLSingleRequestProto( + reqId: int, account: str, modelCode: str, conId: int +) -> PnLSingleRequestProto: + pnlSingleRequestProto = PnLSingleRequestProto() + if isValidIntValue(reqId): + pnlSingleRequestProto.reqId = reqId + if account: + pnlSingleRequestProto.account = account + if modelCode: + pnlSingleRequestProto.modelCode = modelCode + if isValidIntValue(conId): + pnlSingleRequestProto.conId = conId + return pnlSingleRequestProto + + +def createCancelPnLSingleProto(reqId: int) -> CancelPnLSingleProto: + cancelPnLSingleProto = CancelPnLSingleProto() + if isValidIntValue(reqId): + cancelPnLSingleProto.reqId = reqId + return cancelPnLSingleProto + + +def createScannerDataList(scannerDataProto: ScannerDataProto) -> list[ScanData]: + dataList = [] + if scannerDataProto.scannerDataElement: + for element in scannerDataProto.scannerDataElement: + rank = element.rank if element.HasField("rank") else 0 + + # Set contract details + if element.HasField("contract"): + contract = createContract(element.contract) + marketName = ( + element.marketName if element.HasField("marketName") else "" + ) + contractDetails = ContractDetails( + contract=contract, marketName=marketName + ) + + distance = element.distance if element.HasField("distance") else "" + benchmark = element.benchmark if element.HasField("benchmark") else "" + projection = element.projection if element.HasField("projection") else "" + comboKey = element.comboKey if element.HasField("comboKey") else "" + scanData = ScanData( + rank=rank, + contractDetails=contractDetails, + distance=distance, + benchmark=benchmark, + projection=projection, + legsStr=comboKey, + ) + dataList.append(scanData) + return dataList + diff --git a/ib_async/protobuf_converters/trade_converter.py b/ib_async/protobuf_converters/trade_converter.py index 845bf2d6..85847070 100644 --- a/ib_async/protobuf_converters/trade_converter.py +++ b/ib_async/protobuf_converters/trade_converter.py @@ -2,6 +2,7 @@ Converters for trade-related Protobuf messages. """ +from datetime import datetime from decimal import Decimal from ib_async.contract import ComboLeg, Contract, DeltaNeutralContract @@ -19,8 +20,10 @@ MarginCondition, Order, OrderAllocation, + OrderCancel, OrderComboLeg, OrderCondition, + OrderConditionType, OrderState, OrderStatus, PercentChangeCondition, @@ -31,12 +34,16 @@ ) from ib_async.util import ( UNSET_INTEGER, + UNSET_DOUBLE, decimalMaxString, getEnumTypeFromString, isValidIntValue, parseIBDatetime, ) +from ..protobuf.CancelOrderRequest_pb2 import ( + CancelOrderRequest as CancelOrderRequestProto, +) from ..protobuf.CommissionAndFeesReport_pb2 import ( CommissionAndFeesReport as CommissionReportProto, ) @@ -47,16 +54,362 @@ ) from ..protobuf.ExecutionFilter_pb2 import ExecutionFilter as ExecutionFilterProto from ..protobuf.ExecutionRequest_pb2 import ExecutionRequest as ExecutionRequestProto +from ..protobuf.ExerciseOptionsRequest_pb2 import ( + ExerciseOptionsRequest as ExerciseOptionsRequestProto, +) +from ..protobuf.GlobalCancelRequest_pb2 import ( + GlobalCancelRequest as GlobalCancelRequestProto, +) from ..protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto from ..protobuf.Order_pb2 import Order as OrderProto from ..protobuf.OrderAllocation_pb2 import ( OrderAllocation as OrderAllocationProto, ) +from ..protobuf.OrderCancel_pb2 import OrderCancel as OrderCancelProto from ..protobuf.OrderCondition_pb2 import OrderCondition as OrderConditionProto from ..protobuf.OrderState_pb2 import OrderState as OrderStateProto from ..protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto from ..protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto -from ..protobuf_converters.contract_converters import createContract +from ..protobuf.PlaceOrderRequest_pb2 import PlaceOrderRequest as PlaceOrderRequestProto +from ..protobuf_converters.contract_converters import ( + createContract, + createContractProto, +) +from ..protobuf_converters.historical_data_converters import fillTagValueList + + +class ClientException(Exception): + def __init__(self, code, message, text): + super().__init__(f"Client request error: {code}: {message}, {text}") + self.code = code + self.message = message + self.text = text + + +def createPlaceOrderRequestProto( + orderId: int, contract: Contract, order: Order +) -> PlaceOrderRequestProto: + placeOrderRequestProto = PlaceOrderRequestProto() + if isValidIntValue(orderId): + placeOrderRequestProto.orderId = orderId + contractProto = createContractProto(contract, order) + if contractProto is not None: + placeOrderRequestProto.contract.CopyFrom(contractProto) + orderProto = createOrderProto(order) + if orderProto is not None: + placeOrderRequestProto.order.CopyFrom(orderProto) + return placeOrderRequestProto + + +def createOrderProto(order: Order) -> OrderProto: + orderProto = OrderProto() + if isValidIntValue(order.clientId): + order.clientId = order.clientId + if isValidIntValue(order.permId): + orderProto.permId = order.permId + if isValidIntValue(order.parentId): + orderProto.parentId = order.parentId + if order.action: + orderProto.action = order.action + if order.totalQuantity != UNSET_DOUBLE: + orderProto.totalQuantity = decimalMaxString(order.totalQuantity) + if isValidIntValue(order.displaySize): + orderProto.displaySize = order.displaySize + if order.orderType: + orderProto.orderType = order.orderType + if order.lmtPrice != UNSET_DOUBLE: + orderProto.lmtPrice = float(order.lmtPrice) + if order.auxPrice != UNSET_DOUBLE: + orderProto.auxPrice = float(order.auxPrice) + if order.tif: + orderProto.tif = order.tif + if order.account: + orderProto.account = order.account + if order.settlingFirm: + orderProto.settlingFirm = order.settlingFirm + if order.clearingAccount: + orderProto.clearingAccount = order.clearingAccount + if order.clearingIntent: + orderProto.clearingIntent = order.clearingIntent + if order.allOrNone: + orderProto.allOrNone = order.allOrNone + if order.blockOrder: + orderProto.blockOrder = order.blockOrder + if order.hidden: + orderProto.hidden = order.hidden + if order.outsideRth: + orderProto.outsideRth = order.outsideRth + if order.sweepToFill: + orderProto.sweepToFill = order.sweepToFill + if order.percentOffset != UNSET_DOUBLE: + orderProto.percentOffset = float(order.percentOffset) + if order.trailingPercent: + orderProto.trailingPercent = float(order.trailingPercent) + if order.trailStopPrice != UNSET_DOUBLE: + orderProto.trailStopPrice = float(order.trailStopPrice) + if isValidIntValue(order.minQty): + orderProto.minQty = order.minQty + if order.goodAfterTime: + orderProto.goodAfterTime = order.goodAfterTime + if order.goodTillDate: + orderProto.goodTillDate = order.goodTillDate + if order.ocaGroup: + orderProto.ocaGroup = order.ocaGroup + if order.orderRef: + orderProto.orderRef = order.orderRef + if order.rule80A: + orderProto.rule80A = order.rule80A + if isValidIntValue(order.ocaType): + orderProto.ocaType = order.ocaType + if isValidIntValue(order.triggerMethod): + orderProto.triggerMethod = order.triggerMethod + if order.activeStartTime: + orderProto.activeStartTime = order.activeStartTime + if order.activeStopTime: + orderProto.activeStopTime = order.activeStopTime + if order.faGroup: + orderProto.faGroup = order.faGroup + if order.faMethod: + orderProto.faMethod = order.faMethod + if order.faPercentage: + orderProto.faPercentage = order.faPercentage + if order.volatility != UNSET_DOUBLE: + orderProto.volatility = float(order.volatility) + if isValidIntValue(order.volatilityType): + orderProto.volatilityType = order.volatilityType + if order.continuousUpdate: + orderProto.continuousUpdate = order.continuousUpdate + if isValidIntValue(order.referencePriceType): + orderProto.referencePriceType = order.referencePriceType + if order.deltaNeutralOrderType: + orderProto.deltaNeutralOrderType = order.deltaNeutralOrderType + if order.deltaNeutralAuxPrice != UNSET_DOUBLE: + orderProto.deltaNeutralAuxPrice = float(order.deltaNeutralAuxPrice) + if isValidIntValue(order.deltaNeutralConId): + orderProto.deltaNeutralConId = order.deltaNeutralConId + if order.deltaNeutralOpenClose: + orderProto.deltaNeutralOpenClose = order.deltaNeutralOpenClose + if order.deltaNeutralShortSale: + orderProto.deltaNeutralShortSale = order.deltaNeutralShortSale + if isValidIntValue(order.deltaNeutralShortSaleSlot): + orderProto.deltaNeutralShortSaleSlot = order.deltaNeutralShortSaleSlot + if order.deltaNeutralDesignatedLocation: + orderProto.deltaNeutralDesignatedLocation = order.deltaNeutralDesignatedLocation + if isValidIntValue(order.scaleInitLevelSize): + orderProto.scaleInitLevelSize = order.scaleInitLevelSize + if isValidIntValue(order.scaleSubsLevelSize): + orderProto.scaleSubsLevelSize = order.scaleSubsLevelSize + if order.scalePriceIncrement != UNSET_DOUBLE: + orderProto.scalePriceIncrement = float(order.scalePriceIncrement) + if order.scalePriceAdjustValue != UNSET_DOUBLE: + orderProto.scalePriceAdjustValue = float(order.scalePriceAdjustValue) + if isValidIntValue(order.scalePriceAdjustInterval): + orderProto.scalePriceAdjustInterval = order.scalePriceAdjustInterval + if order.scaleProfitOffset != UNSET_DOUBLE: + orderProto.scaleProfitOffset = float(order.scaleProfitOffset) + if order.scaleAutoReset: + orderProto.scaleAutoReset = order.scaleAutoReset + if isValidIntValue(order.scaleInitPosition): + orderProto.scaleInitPosition = order.scaleInitPosition + if isValidIntValue(order.scaleInitFillQty): + orderProto.scaleInitFillQty = order.scaleInitFillQty + if order.scaleRandomPercent: + orderProto.scaleRandomPercent = order.scaleRandomPercent + if order.scaleTable: + orderProto.scaleTable = order.scaleTable + if order.hedgeType: + orderProto.hedgeType = order.hedgeType + if order.hedgeParam: + orderProto.hedgeParam = order.hedgeParam + + if order.algoStrategy: + orderProto.algoStrategy = order.algoStrategy + fillTagValueList(order.algoParams, orderProto.algoParams) + if order.algoId: + orderProto.algoId = order.algoId + + fillTagValueList(order.smartComboRoutingParams, orderProto.smartComboRoutingParams) + + if order.whatIf: + orderProto.whatIf = order.whatIf + if order.transmit: + orderProto.transmit = order.transmit + if order.overridePercentageConstraints: + orderProto.overridePercentageConstraints = order.overridePercentageConstraints + if order.openClose: + orderProto.openClose = order.openClose + if isValidIntValue(order.origin): + orderProto.origin = order.origin + if isValidIntValue(order.shortSaleSlot): + orderProto.shortSaleSlot = order.shortSaleSlot + if order.designatedLocation: + orderProto.designatedLocation = order.designatedLocation + if isValidIntValue(order.exemptCode): + orderProto.exemptCode = order.exemptCode + if order.deltaNeutralSettlingFirm: + orderProto.deltaNeutralSettlingFirm = order.deltaNeutralSettlingFirm + if order.deltaNeutralClearingAccount: + orderProto.deltaNeutralClearingAccount = order.deltaNeutralClearingAccount + if order.deltaNeutralClearingIntent: + orderProto.deltaNeutralClearingIntent = order.deltaNeutralClearingIntent + if order.discretionaryAmt != UNSET_DOUBLE: + orderProto.discretionaryAmt = order.discretionaryAmt + if order.optOutSmartRouting: + orderProto.optOutSmartRouting = order.optOutSmartRouting + if isValidIntValue(order.exemptCode): + orderProto.exemptCode = order.exemptCode + if order.startingPrice != UNSET_DOUBLE: + orderProto.startingPrice = float(order.startingPrice) + if order.stockRefPrice != UNSET_DOUBLE: + orderProto.stockRefPrice = float(order.stockRefPrice) + if order.delta != UNSET_DOUBLE: + orderProto.delta = float(order.delta) + if order.stockRangeLower != UNSET_DOUBLE: + orderProto.stockRangeLower = float(order.stockRangeLower) + if order.stockRangeUpper != UNSET_DOUBLE: + orderProto.stockRangeUpper = float(order.stockRangeUpper) + if order.notHeld: + orderProto.notHeld = order.notHeld + + fillTagValueList(order.orderMiscOptions, orderProto.orderMiscOptions) + + if order.solicited: + orderProto.solicited = order.solicited + if order.randomizeSize: + orderProto.randomizeSize = order.randomizeSize + if order.randomizePrice: + orderProto.randomizePrice = order.randomizePrice + if isValidIntValue(order.referenceContractId): + orderProto.referenceContractId = order.referenceContractId + if order.peggedChangeAmount != UNSET_DOUBLE: + orderProto.peggedChangeAmount = order.peggedChangeAmount + if order.isPeggedChangeAmountDecrease: + orderProto.isPeggedChangeAmountDecrease = order.isPeggedChangeAmountDecrease + if order.referenceChangeAmount != UNSET_DOUBLE: + orderProto.referenceChangeAmount = order.referenceChangeAmount + if order.referenceExchangeId: + orderProto.referenceExchangeId = order.referenceExchangeId + if order.adjustedOrderType: + orderProto.adjustedOrderType = order.adjustedOrderType + if order.triggerPrice != UNSET_DOUBLE: + orderProto.triggerPrice = order.triggerPrice + if order.adjustedStopPrice != UNSET_DOUBLE: + orderProto.adjustedStopPrice = order.adjustedStopPrice + if order.adjustedStopLimitPrice != UNSET_DOUBLE: + orderProto.adjustedStopLimitPrice = order.adjustedStopLimitPrice + if order.adjustedTrailingAmount != UNSET_DOUBLE: + orderProto.adjustedTrailingAmount = order.adjustedTrailingAmount + if isValidIntValue(order.adjustableTrailingUnit): + orderProto.adjustableTrailingUnit = order.adjustableTrailingUnit + if order.lmtPriceOffset != UNSET_DOUBLE: + orderProto.lmtPriceOffset = order.lmtPriceOffset + + orderConditionList = createConditionsProto(order) + if orderConditionList is not None and orderConditionList: + orderProto.conditions.extend(orderConditionList) + if order.conditionsCancelOrder: + orderProto.conditionsCancelOrder = order.conditionsCancelOrder + if order.conditionsIgnoreRth: + orderProto.conditionsIgnoreRth = order.conditionsIgnoreRth + + if order.modelCode: + orderProto.modelCode = order.modelCode + if order.extOperator: + orderProto.extOperator = order.extOperator + + softDollarTier = createSoftDollarTierProto(order) + if softDollarTier is not None: + orderProto.softDollarTier.CopyFrom(softDollarTier) + + if order.cashQty != UNSET_DOUBLE: + orderProto.cashQty = order.cashQty + if order.mifid2DecisionMaker: + orderProto.mifid2DecisionMaker = order.mifid2DecisionMaker + if order.mifid2DecisionAlgo: + orderProto.mifid2DecisionAlgo = order.mifid2DecisionAlgo + if order.mifid2ExecutionTrader: + orderProto.mifid2ExecutionTrader = order.mifid2ExecutionTrader + if order.mifid2ExecutionAlgo: + orderProto.mifid2ExecutionAlgo = order.mifid2ExecutionAlgo + if order.dontUseAutoPriceForHedge: + orderProto.dontUseAutoPriceForHedge = order.dontUseAutoPriceForHedge + if order.isOmsContainer: + orderProto.isOmsContainer = order.isOmsContainer + if order.discretionaryUpToLimitPrice: + orderProto.discretionaryUpToLimitPrice = order.discretionaryUpToLimitPrice + if order.usePriceMgmtAlgo is not None: + orderProto.usePriceMgmtAlgo = 1 if order.usePriceMgmtAlgo else 0 + if isValidIntValue(order.duration): + orderProto.duration = order.duration + if isValidIntValue(order.postToAts): + orderProto.postToAts = order.postToAts + if order.advancedErrorOverride: + orderProto.advancedErrorOverride = order.advancedErrorOverride + if order.manualOrderTime: + orderProto.manualOrderTime = order.manualOrderTime + if isValidIntValue(order.minTradeQty): + orderProto.minTradeQty = order.minTradeQty + if isValidIntValue(order.minCompeteSize): + orderProto.minCompeteSize = order.minCompeteSize + if order.competeAgainstBestOffset != UNSET_DOUBLE: + orderProto.competeAgainstBestOffset = order.competeAgainstBestOffset + if order.midOffsetAtWhole != UNSET_DOUBLE: + orderProto.midOffsetAtWhole = order.midOffsetAtWhole + if order.midOffsetAtHalf != UNSET_DOUBLE: + orderProto.midOffsetAtHalf = order.midOffsetAtHalf + if order.customerAccount: + orderProto.customerAccount = order.customerAccount + if order.professionalCustomer: + orderProto.professionalCustomer = order.professionalCustomer + if order.bondAccruedInterest: + orderProto.bondAccruedInterest = order.bondAccruedInterest + if order.includeOvernight: + orderProto.includeOvernight = order.includeOvernight + if isValidIntValue(order.manualOrderIndicator): + orderProto.manualOrderIndicator = order.manualOrderIndicator + if order.submitter: + orderProto.submitter = order.submitter + if order.autoCancelParent: + orderProto.autoCancelParent = order.autoCancelParent + if order.imbalanceOnly: + orderProto.imbalanceOnly = order.imbalanceOnly + + return orderProto + + +def createConditionsProto(order: Order) -> list[OrderConditionProto]: + orderConditionProtoList = [] + try: + if order.conditions is not None and order.conditions: + for orderCondition in order.conditions: + conditionType = getattr(orderCondition, "condType", None) + + if PriceCondition.condType == conditionType: + orderConditionProto = createPriceConditionProto(orderCondition) + elif TimeCondition.condType == conditionType: + orderConditionProto = createTimeConditionProto(orderCondition) + elif MarginCondition.condType == conditionType: + orderConditionProto = createMarginConditionProto(orderCondition) + elif ExecutionCondition.condType == conditionType: + orderConditionProto = createExecutionConditionProto(orderCondition) + elif VolumeCondition.condType == conditionType: + orderConditionProto = createVolumeConditionProto(orderCondition) + elif PercentChangeCondition.condType == conditionType: + orderConditionProto = createPercentChangeConditionProto( + orderCondition + ) + + if orderConditionProto is not None: + orderConditionProtoList.append(orderConditionProto) + + except Exception: + raise ClientException( + 588, + "Error encoding protobuf - ", + "Error encoding conditions", + ) + + return orderConditionProtoList def createOrderComboLegs(contractProto: ContractProto) -> list[OrderComboLeg]: @@ -72,6 +425,121 @@ def createOrderComboLegs(contractProto: ContractProto) -> list[OrderComboLeg]: return orderComboLegs +def createOrderConditionProto( + orderCondition: OrderConditionType, +) -> OrderConditionProto: + conditionType = orderCondition.condType + # Returns True if conjuction is AND, False if OR + isConjunctionConnection = orderCondition.conjunction == "a" + orderConditionProto = OrderConditionProto() + if isValidIntValue(conditionType): + orderConditionProto.type = conditionType + orderConditionProto.isConjunctionConnection = isConjunctionConnection + return orderConditionProto + + +def createOperatorConditionProto( + operatorCondition: OrderConditionType, +) -> OrderConditionProto: + orderConditionProto = createOrderConditionProto(operatorCondition) + operatorConditionProto = OrderConditionProto() + operatorConditionProto.MergeFrom(orderConditionProto) + if hasattr(operatorCondition, "isMore"): + operatorConditionProto.isMore = operatorCondition.isMore + return operatorConditionProto + + +def createContractConditionProto( + contractCondition: OrderConditionType, +) -> OrderConditionProto: + operatorConditionProto = createOperatorConditionProto(contractCondition) + contractConditionProto = OrderConditionProto() + contractConditionProto.MergeFrom(operatorConditionProto) + if hasattr(contractCondition, "conId") and isValidIntValue(contractCondition.conId): + contractConditionProto.conId = contractCondition.conId + if hasattr(contractCondition, "exch"): + contractConditionProto.exchange = contractCondition.exch + return contractConditionProto + + +def createPriceConditionProto(priceCondition: PriceCondition) -> OrderConditionProto: + contractConditionProto = createContractConditionProto(priceCondition) + priceConditionProto = OrderConditionProto() + priceConditionProto.MergeFrom(contractConditionProto) + if priceCondition.price != UNSET_DOUBLE: + priceConditionProto.price = priceCondition.price + if isValidIntValue(priceCondition.triggerMethod): + priceConditionProto.triggerMethod = priceCondition.triggerMethod + return priceConditionProto + + +def createTimeConditionProto(timeCondition: TimeCondition) -> OrderConditionProto: + operatorConditionProto = createOperatorConditionProto(timeCondition) + timeConditionProto = OrderConditionProto() + timeConditionProto.MergeFrom(operatorConditionProto) + if timeCondition.time: + timeConditionProto.time = timeCondition.time + return timeConditionProto + + +def createMarginConditionProto(marginCondition: MarginCondition) -> OrderConditionProto: + operatorConditionProto = createOperatorConditionProto(marginCondition) + marginConditionProto = OrderConditionProto() + marginConditionProto.MergeFrom(operatorConditionProto) + if marginCondition.percent != UNSET_DOUBLE: + marginConditionProto.percent = marginCondition.percent + return marginConditionProto + + +def createExecutionConditionProto( + executionCondition: ExecutionCondition, +) -> OrderConditionProto: + orderConditionProto = createOrderConditionProto(executionCondition) + executionConditionProto = OrderConditionProto() + executionConditionProto.MergeFrom(orderConditionProto) + if executionCondition.secType: + executionConditionProto.secType = executionCondition.secType + if executionCondition.exch: + executionConditionProto.exchange = executionCondition.exch + if executionCondition.symbol: + executionConditionProto.symbol = executionCondition.symbol + return executionConditionProto + + +def createVolumeConditionProto(volumeCondition: VolumeCondition) -> OrderConditionProto: + contractConditionProto = createContractConditionProto(volumeCondition) + volumeConditionProto = OrderConditionProto() + volumeConditionProto.MergeFrom(contractConditionProto) + if isValidIntValue(volumeCondition.volume): + volumeConditionProto.volume = volumeCondition.volume + return volumeConditionProto + + +def createPercentChangeConditionProto( + percentChangeCondition: PercentChangeCondition, +) -> OrderConditionProto: + contractConditionProto = createContractConditionProto(percentChangeCondition) + percentChangeConditionProto = OrderConditionProto() + percentChangeConditionProto.MergeFrom(contractConditionProto) + if percentChangeCondition.changePercent != UNSET_DOUBLE: + percentChangeConditionProto.changePercent = percentChangeCondition.changePercent + return percentChangeConditionProto + + +def createSoftDollarTierProto(order: Order) -> SoftDollarTierProto: + softDollarTierProto = None + tier = order.softDollarTier + if tier is not None: + softDollarTierProto = SoftDollarTierProto() + if tier.name: + softDollarTierProto.name = tier.name + if tier.val: + softDollarTierProto.value = tier.val + if tier.displayName: + softDollarTierProto.displayName = tier.displayName + return softDollarTierProto + + def createOrder( orderId: int, contractProto: ContractProto, orderProto: OrderProto ) -> Order: @@ -83,7 +551,7 @@ def createOrder( if orderProto.HasField("action"): order.action = orderProto.action if orderProto.HasField("totalQuantity"): - order.totalQuantity = orderProto.totalQuantity + order.totalQuantity = float(orderProto.totalQuantity) if orderProto.HasField("orderType"): order.orderType = orderProto.orderType if orderProto.HasField("lmtPrice"): @@ -348,8 +816,8 @@ def createOrder( return order -def createOrderConditions(orderProto: OrderProto) -> list[OrderCondition]: - orderConditions: list[OrderCondition] = [] +def createOrderConditions(orderProto: OrderProto) -> list[OrderConditionType]: + orderConditions: list[OrderConditionType] = [] orderConditionsProtoList = [] if orderProto.conditions is not None: orderConditionsProtoList = orderProto.conditions @@ -360,23 +828,19 @@ def createOrderConditions(orderProto: OrderProto) -> list[OrderCondition]: orderConditionProto.type if orderConditionProto.HasField("type") else 0 ) - condition = None + condition: OrderCondition | None = None if PriceCondition.condType == conditionType: - condition: PriceCondition = createPriceCondition(orderConditionProto) + condition = createPriceCondition(orderConditionProto) elif TimeCondition.condType == conditionType: - condition: TimeCondition = createTimeCondition(orderConditionProto) + condition = createTimeCondition(orderConditionProto) elif MarginCondition.condType == conditionType: - condition: MarginCondition = createMarginCondition(orderConditionProto) + condition = createMarginCondition(orderConditionProto) elif ExecutionCondition.condType == conditionType: - condition: ExecutionCondition = createExecutionCondition( - orderConditionProto - ) + condition = createExecutionCondition(orderConditionProto) elif VolumeCondition.condType == conditionType: - condition: VolumeCondition = createVolumeCondition(orderConditionProto) + condition = createVolumeCondition(orderConditionProto) elif PercentChangeCondition.condType == conditionType: - condition: PercentChangeCondition = createPercentChangeCondition( - orderConditionProto - ) + condition = createPercentChangeCondition(orderConditionProto) if condition is not None: orderConditions.append(condition) @@ -394,7 +858,7 @@ def setConditionFields( def setOperatorConditionFields( - orderConditionProto: OrderConditionProto, operatorCondition: PriceCondition + orderConditionProto: OrderConditionProto, operatorCondition: OrderConditionType ): setConditionFields(orderConditionProto, operatorCondition) if orderConditionProto.HasField("isMore"): @@ -402,7 +866,7 @@ def setOperatorConditionFields( def setContractConditionFields( - orderConditionProto: OrderConditionProto, contractCondition: PriceCondition + orderConditionProto: OrderConditionProto, contractCondition: OrderConditionType ): setOperatorConditionFields(orderConditionProto, contractCondition) if orderConditionProto.HasField("conId"): @@ -473,11 +937,14 @@ def createSoftDollarTierFromOrder(orderProto: OrderProto) -> SoftDollarTier | No softDollarTierProto = None if orderProto.softDollarTier is not None: softDollarTierProto = orderProto.softDollarTier - return ( - createSoftDollarTier(softDollarTierProto) - if softDollarTierProto is not None - else None - ) + + softDollarTier = None + if softDollarTierProto is not None: + created_tier = createSoftDollarTier(softDollarTierProto) + if created_tier: # Check if the created tier is not empty + softDollarTier = created_tier + + return softDollarTier def createSoftDollarTier(softDollarTierProto: SoftDollarTierProto) -> SoftDollarTier: @@ -511,7 +978,7 @@ def createOrderState(orderStateProto: OrderStateProto) -> OrderState: if orderStateProto.HasField("status"): orderState.status = orderStateProto.status if orderStateProto.HasField("initMarginBefore"): - orderState.initMarginBefore = orderStateProto.initMarginBefore + orderState.initMarginBefore = str(orderStateProto.initMarginBefore) if orderStateProto.HasField("maintMarginBefore"): orderState.maintMarginBefore = decimalMaxString( orderStateProto.maintMarginBefore @@ -539,13 +1006,13 @@ def createOrderState(orderStateProto: OrderStateProto) -> OrderState: orderStateProto.equityWithLoanAfter ) if orderStateProto.HasField("commissionAndFees"): - orderState.commissionAndFees = orderStateProto.commissionAndFees + orderState.commission = orderStateProto.commissionAndFees if orderStateProto.HasField("minCommissionAndFees"): - orderState.minCommissionAndFees = orderStateProto.minCommissionAndFees + orderState.minCommission = orderStateProto.minCommissionAndFees if orderStateProto.HasField("maxCommissionAndFees"): - orderState.maxCommissionAndFees = orderStateProto.maxCommissionAndFees + orderState.maxCommission = orderStateProto.maxCommissionAndFees if orderStateProto.HasField("commissionAndFeesCurrency"): - orderState.commissionAndFeesCurrency = orderStateProto.commissionAndFeesCurrency + orderState.commissionCurrency = orderStateProto.commissionAndFeesCurrency if orderStateProto.HasField("warningText"): orderState.warningText = orderStateProto.warningText if orderStateProto.HasField("marginCurrency"): @@ -608,7 +1075,7 @@ def createOrderAllocations(orderStateProto: OrderStateProto) -> list[OrderAlloca orderAllocationProtoList = orderStateProto.orderAllocations if orderAllocationProtoList: for orderAllocationProto in orderAllocationProtoList: - orderAllocation = OrderAllocationProto() + orderAllocation = OrderAllocation() if orderAllocationProto.HasField("account"): orderAllocation.account = orderAllocationProto.account if orderAllocationProto.HasField("position"): @@ -728,15 +1195,22 @@ def createFill(execDetailsProto: ExecutionDetailsProto) -> Fill: ) -def createTradeFromOpenOrder(openOrderProto: OpenOrderProto) -> "Trade": - from ib_async.order import Trade - +def createTradeFromOpenOrder( + openOrderProto: OpenOrderProto, +) -> tuple[Trade, OrderState] | None: + if not openOrderProto.HasField("contract"): + return None contract = createContract(openOrderProto.contract) + if not openOrderProto.HasField("order"): + return None order = createOrder( openOrderProto.order.orderId, openOrderProto.contract, openOrderProto.order ) - orderStatus = createOrderStatus(openOrderProto.order) - return Trade(contract, order, orderStatus, [], []) + if not openOrderProto.HasField("orderState"): + return None + orderState = createOrderState(openOrderProto.orderState) + orderStatus = OrderStatus(orderId=order.orderId, status=orderState.status) + return Trade(contract, order, orderStatus, [], []), orderState def createCommissionReport( @@ -806,3 +1280,72 @@ def createExecutionRequestProto( executionRequestProto.reqId = reqId executionRequestProto.executionFilter.CopyFrom(executionFilterProto) return executionRequestProto + + +def createOrderCancelProto(orderCancel: OrderCancel) -> OrderCancelProto: + if orderCancel is None: + return None + orderCancelProto = OrderCancelProto() + if orderCancel.manualOrderCancelTime: + orderCancelProto.manualOrderCancelTime = orderCancel.manualOrderCancelTime + if orderCancel.extOperator: + orderCancelProto.extOperator = orderCancel.extOperator + if isValidIntValue(orderCancel.manualOrderIndicator): + orderCancelProto.manualOrderIndicator = orderCancel.manualOrderIndicator + return orderCancelProto + + +def createGlobalCancelRequestProto( + orderCancel: OrderCancel, +) -> GlobalCancelRequestProto: + globalCancelRequestProto = GlobalCancelRequestProto() + orderCancelProto = createOrderCancelProto(orderCancel) + if orderCancelProto is not None: + globalCancelRequestProto.orderCancel.CopyFrom(orderCancelProto) + return globalCancelRequestProto + + +def createCancelOrderRequestProto( + orderId: int, orderCancel: OrderCancel +) -> CancelOrderRequestProto: + cancelOrderRequestProto = CancelOrderRequestProto() + if isValidIntValue(orderId): + cancelOrderRequestProto.orderId = orderId + orderCancelProto = createOrderCancelProto(orderCancel) + if orderCancelProto is not None: + cancelOrderRequestProto.orderCancel.CopyFrom(orderCancelProto) + return cancelOrderRequestProto + + +def createExerciseOptionsRequestProto( + orderId: int, + contract: Contract, + exerciseAction: int, + exerciseQuantity: int, + account: str, + override: bool, + manualOrderTime: str, + customerAccount: str, + professionalCustomer: bool, +) -> ExerciseOptionsRequestProto: + exerciseOptionsRequestProto = ExerciseOptionsRequestProto() + if isValidIntValue(orderId): + exerciseOptionsRequestProto.orderId = orderId + contractProto = createContractProto(contract, None) + if contractProto is not None: + exerciseOptionsRequestProto.contract.CopyFrom(contractProto) + if isValidIntValue(exerciseAction): + exerciseOptionsRequestProto.exerciseAction = exerciseAction + if isValidIntValue(exerciseQuantity): + exerciseOptionsRequestProto.exerciseQuantity = exerciseQuantity + if account: + exerciseOptionsRequestProto.account = account + if override: + exerciseOptionsRequestProto.override = override + if manualOrderTime: + exerciseOptionsRequestProto.manualOrderTime = manualOrderTime + if customerAccount: + exerciseOptionsRequestProto.customerAccount = customerAccount + if professionalCustomer: + exerciseOptionsRequestProto.professionalCustomer = professionalCustomer + return exerciseOptionsRequestProto diff --git a/ib_async/ticker.py b/ib_async/ticker.py index c28f4e93..1af30f6b 100644 --- a/ib_async/ticker.py +++ b/ib_async/ticker.py @@ -4,7 +4,7 @@ from contextlib import suppress from dataclasses import dataclass, field from datetime import datetime -from typing import ClassVar, Final, Optional, TypeAlias, Union +from typing import ClassVar, Final, TypeAlias from eventkit import Event, Op @@ -16,9 +16,9 @@ IBDefaults, MktDepthData, OptionComputation, - TickByTickAllLast, - TickByTickBidAsk, - TickByTickMidPoint, + HistoricalTickLast, + HistoricalTickBidAsk, + HistoricalTick, TickComputationData, TickData, TickDataType, @@ -170,7 +170,7 @@ class Ticker: rtHistVolatility: float = nan rtVolume: float = nan rtTradeVolume: float = nan - rtTime: Optional[datetime] = None + rtTime: None | datetime = None avVolume: float = nan tradeCount: float = nan tradeRate: float = nan @@ -189,21 +189,21 @@ class Ticker: avOptionVolume: float = nan histVolatility: float = nan impliedVolatility: float = nan - dividends: Optional[Dividends] = None - fundamentalRatios: Optional[FundamentalRatios] = None + dividends: None | Dividends = None + fundamentalRatios: None | FundamentalRatios = None ticks: list[TickData] = field(default_factory=list) - tickByTicks: list[ - Union[TickByTickAllLast, TickByTickBidAsk, TickByTickMidPoint] - ] = field(default_factory=list) + tickByTicks: list[HistoricalTickLast | HistoricalTickBidAsk | HistoricalTick] = ( + field(default_factory=list) + ) domBids: list[DOMLevel] = field(default_factory=list) domBidsDict: dict[int, DOMLevel] = field(default_factory=dict) domAsks: list[DOMLevel] = field(default_factory=list) domAsksDict: dict[int, DOMLevel] = field(default_factory=dict) domTicks: list[MktDepthData] = field(default_factory=list) - bidGreeks: Optional[OptionComputation] = None - askGreeks: Optional[OptionComputation] = None - lastGreeks: Optional[OptionComputation] = None - modelGreeks: Optional[OptionComputation] = None + bidGreeks: None | OptionComputation = None + askGreeks: None | OptionComputation = None + lastGreeks: None | OptionComputation = None + modelGreeks: None | OptionComputation = None auctionVolume: float = nan auctionPrice: float = nan auctionImbalance: float = nan @@ -220,6 +220,7 @@ def __post_init__(self): # everything with _another_ post_init clear. if not self.created: self.updateEvent = TickerUpdateEvent("updateEvent") + self.ticker_bus = Event("Ticker bus") self.minTick = self.defaults.unset self.bid = self.defaults.unset self.bidSize = self.defaults.unset @@ -303,6 +304,12 @@ def _on_ticker_data(self, tick_data: TickDataType, last_time: datetime): self._on_tick_generic(tick_data, last_time) elif isinstance(tick_data, TickComputationData): self._on_opt_computation(tick_data) + elif isinstance(tick_data, HistoricalTickLast): + self._on_tick_last(tick_data) + elif isinstance(tick_data, HistoricalTickBidAsk): + self._on_tick_bidask(tick_data) + elif isinstance(tick_data, HistoricalTick): + self._on_tick_midpoint(tick_data) else: _logger.error("Ticker %s. Unknown tick data: %s", self.contract, tick_data) @@ -558,6 +565,59 @@ def _on_opt_computation(self, tick_computation: TickComputationData): tick_computation.computation, ) + def _on_tick_last(self, historicalTickLast: HistoricalTickLast): + if historicalTickLast.price == -1 and historicalTickLast.size == 0: + price = self.defaults.emptyPrice + size = self.defaults.emptySize + else: + price = historicalTickLast.price + size = historicalTickLast.size + + self.prevLast = self.last + self.prevLastSize = self.lastSize + self.last = price + self.lastSize = size + + self.tickByTicks.append(historicalTickLast) + + def _on_tick_bidask(self, historicalTickBidAsk: HistoricalTickBidAsk): + if historicalTickBidAsk.priceBid != self.bid: + self.prevBid = self.bid + self.bid = ( + historicalTickBidAsk.priceBid + if historicalTickBidAsk.priceBid > 0 + else self.defaults.emptyPrice + ) + + if historicalTickBidAsk.sizeBid != self.bidSize: + self.prevBidSize = self.bidSize + self.bidSize = ( + historicalTickBidAsk.sizeBid + if historicalTickBidAsk.sizeBid > 0 + else self.defaults.emptySize + ) + + if historicalTickBidAsk.priceAsk != self.ask: + self.prevAsk = self.ask + self.ask = ( + historicalTickBidAsk.priceAsk + if historicalTickBidAsk.priceAsk > 0 + else self.defaults.emptyPrice + ) + + if historicalTickBidAsk.sizeAsk != self.askSize: + self.prevAskSize = self.askSize + self.askSize = ( + historicalTickBidAsk.sizeAsk + if historicalTickBidAsk.sizeAsk > 0 + else self.defaults.emptySize + ) + + self.tickByTicks.append(historicalTickBidAsk) + + def _on_tick_midpoint(self, historicalTickMidPoint: HistoricalTick): + self.tickByTicks.append(historicalTickMidPoint) + def isUnset(self, value) -> bool: # if default value is nan and value is nan, it is unset. # else, if value matches default value, it is unset. @@ -726,7 +786,7 @@ def on_source(self, ticker): @dataclass class Bar: - time: Optional[datetime] + time: None | datetime open: float = nan high: float = nan low: float = nan @@ -777,7 +837,7 @@ def on_source(self, time, price, size): def _on_timer(self, time): if self.bars: bar = self.bars[-1] - if self.isUnset(bar.close) and len(self.bars) > 1: + if isNan(bar.close) and len(self.bars) > 1: bar.open = bar.high = bar.low = bar.close = self.bars[-2].close self.bars.updateEvent.emit(self.bars, True) diff --git a/ib_async/util.py b/ib_async/util.py index 98f67c25..0c8f32fb 100644 --- a/ib_async/util.py +++ b/ib_async/util.py @@ -136,10 +136,20 @@ def dataclassUpdate(obj, *srcObjs, **kwargs) -> object: if not is_dataclass(obj): raise TypeError(f"Object {obj} is not a dataclass") + valid_fields = {f.name for f in fields(obj)} + for srcObj in srcObjs: - obj.__dict__.update(dataclassAsDict(srcObj)) # type: ignore + if not is_dataclass(srcObj): + continue + for f in fields(srcObj): + if f.name in valid_fields: + value = getattr(srcObj, f.name) + setattr(obj, f.name, value) + + for key, value in kwargs.items(): + if key in valid_fields: + setattr(obj, key, value) - obj.__dict__.update(**kwargs) # type: ignore return obj @@ -603,6 +613,9 @@ def parseIBDatetime(s: str) -> Union[dt.date, dt.datetime]: return t +def parseIBTimeStamp(t:int, tz:dt.tzinfo=dt.timezone.utc) -> dt.datetime: + return dt.datetime.fromtimestamp(t, tz) + def decimalMaxString(val: Decimal): val = Decimal(val) diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index d8f653c9..b145446d 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -1,12 +1,25 @@ """Wrapper to handle incoming messages.""" +from __future__ import annotations + import asyncio import logging import time from collections import defaultdict from dataclasses import dataclass, field from datetime import datetime -from typing import TYPE_CHECKING, Any, TypeAlias, Union, cast +from typing import ( + TYPE_CHECKING, + Any, + Generic, + Hashable, + Optional, + TypeAlias, + TypeVar, + Union, + cast, +) +from weakref import WeakKeyDictionary import eventkit as ev @@ -29,7 +42,6 @@ HistogramData, HistoricalNews, HistoricalSchedule, - HistoricalSession, HistoricalTick, HistoricalTickBidAsk, HistoricalTickLast, @@ -47,25 +59,19 @@ PriceIncrement, RealTimeBar, RealTimeBarList, + ScanDataList, SoftDollarTier, - TickAttribBidAsk, - TickAttribLast, - TickByTickAllLast, - TickByTickBidAsk, - TickByTickMidPoint, TickComputationData, TickGenericData, TickParams, TickPriceData, TickSizeData, TickStringData, - TickType, TradeLogEntry, ) -from ib_async.order import Order, OrderStatus, Trade +from ib_async.order import Order, OrderState, OrderStatus, Trade from ib_async.ticker import Ticker from ib_async.util import ( - UNSET_DOUBLE, dataclassUpdate, getLoop, globalErrorEvent, @@ -77,6 +83,208 @@ OrderKeyType: TypeAlias = int | tuple[int, int] +SubscriptionType: TypeAlias = ScanDataList | RealTimeBarList | BarDataList + +# K = any hashable thing: str, int, UUID, tuple, frozenset, etc. +K = TypeVar("K", bound=Hashable) +V = TypeVar("V") + + +class BiDict(Generic[K, V]): + """ + Bidirectional mapping with full support for composite keys (tuples, etc.) + reqId (int) ↔ object + object_id (hashable) ↔ request_id + + - O(1) lookup in both directions + - Automatically keeps both indexes in sync + - Does NOT hold strong reference to `obj` via object_id → prevents leaks + - Optionally tracks objects weakly if you ever need obj → request_id lookup + + Usage: + ===== + + tickers = BiDict[int,Ticker]() + # add an entry with keys reqId and hash(ticker.contract), value ticker + tickers.add(reqId,hash(ticker.contract),ticker) + + # reqId -> object + tickers.get_by_request_id(reqId) # return ticker for reqId + # object_id -> object + # object_id could be a tuple ie. (order.clientId, order.orderId, order.permId) + tickers.get_by_object_id(hash(ticker.contract)) # return ticker for contract + tickers.get_request_id(hash(ticker.contract)) # return reqId for contract + # + # requires tickers = BiDict[int,Ticker](track_objects_weakly=True) + tickers.get_request_id_by_object(ticker) # return reqId for object + # remove + tickers.remove_by_request_id(reqId) # remove ticker for reqId + tickers.remove_by_object_id(hash(ticker.contract)) # remove ticker for contract + # contains, by reqId or object_id + reqId in tickers + hash(ticker.contract) in tickers + """ + + def __init__(self, *, track_objects_weakly: bool = False) -> None: + """Bidirectional mapping + + Args: + track_objects_weakly (bool, optional): enable get_request_id_by_object, using weakreferences. Defaults to False. + """ + # Primary storage: request_id → (object_id, object) + self._by_request: dict[int, tuple[K, V]] = {} + # Reverse index: object_id → request_id (object_id is usually str/int) + self._request_by_object_id: dict[K, int] = {} + # Optional: weak mapping from object instance → request_id + self._track_objects_weakly = track_objects_weakly + if track_objects_weakly: + self._request_by_object: WeakKeyDictionary[Any, int] = WeakKeyDictionary() + else: + self._request_by_object = None # type: ignore + + def __repr__(self) -> str: + return repr(self._by_request) + + def values(self) -> list[V]: + return [v for _, v in self._by_request.values()] + + def add(self, request_id: int, object_id: K, obj: V) -> None: + """ + Add values to the map. + request_id (int): identifies ie reqId + object_id (K): object identifier, ie (account, tag, currency, "") for + accountValue. + obj (V): the object to store, Ticker, AccountValue, etc. + """ + self._remove_existing(request_id, object_id, obj) + + self._by_request[request_id] = (object_id, obj) + self._request_by_object_id[object_id] = request_id + if self._track_objects_weakly and self._request_by_object is not None: + self._request_by_object[obj] = request_id + + def get_by_request_id(self, request_id: int) -> V | None: + """ + Get value by request id. + request_id (int): identifies ie reqId, permId + + Returns: + Value stored, or None if not found. + """ + entry = self._by_request.get(request_id) + return entry[1] if entry else None + + def get_by_object_id(self, object_id: K) -> V | None: + """Get value by object id, ie hash(contract) + + Args: + object_id (K): ie. hash(contract), (account, tag, currency, "") + + Returns: + V | None: value stored, or None if not found. + """ + request_id = self._request_by_object_id.get(object_id) + if request_id is not None: + return self._by_request[request_id][1] + return None + + def get_request_id(self, object_id: K) -> int | None: + """Get request id by object id + + Args: + object_id (K): ie. hash(contract), (account, tag, currency, "") + + Returns: + int | None: request_id, or None if not found. + """ + return self._request_by_object_id.get(object_id) + + def get_request_id_by_object(self, obj: V) -> Optional[int]: + """Get reqquest_od by object. + Only works if track_objects_weakly=True + + Args: + obj (V): value + + Returns: + int | None: request_id, or None if not found. + """ + if self._request_by_object is not None: + return self._request_by_object.get(obj) + return None + + def remove_by_request_id(self, request_id: int) -> None: + if request_id not in self._by_request: + return + object_id, obj = self._by_request.pop(request_id) + self._request_by_object_id.pop(object_id, None) + if self._request_by_object is not None: + self._request_by_object.pop(obj, None) + + def remove_by_object_id(self, object_id: K) -> None: + request_id = self._request_by_object_id.pop(object_id, None) + if request_id is not None: + entry = self._by_request.pop(request_id, None) + if entry and self._request_by_object is not None: + _, obj = entry + self._request_by_object.pop(obj, None) + + def update_request_id(self, object_id: K, new_request_id: int): + """ + Updates the request_id associated with an existing entry, identified by its + object_id. + + This method is used when the primary request_id (e.g., permId for a Trade) + is not known at the time of initial creation and is later assigned by the API. + It re-indexes the object within the BiDict using the new request_id while + preserving the object's identity and its association with the object_id. + + Args: + object_id (K): The unique identifier for the object (e.g., (clientId, + orderId) for a Trade). + new_request_id (int): The new, permanent request ID to associate with the + object (e.g., permId). + """ + old_request_id = self._request_by_object_id.get(object_id) + if old_request_id is None or old_request_id == new_request_id: + # Entry doesn't exist or is already correct, nothing to do. + return + + # Pop the old entry and add it back with the new request_id + entry = self._by_request.pop(old_request_id, None) + if entry: + self._by_request[new_request_id] = entry + # Update the reverse mapping to point to the new request_id + self._request_by_object_id[object_id] = new_request_id + # Update the weak object mapping if it exists + if self._track_objects_weakly and self._request_by_object: + _, obj = entry + self._request_by_object[obj] = new_request_id + + def _remove_existing(self, request_id: int, object_id: K, obj: V) -> None: + """Clean up old request_id and object_id mapping.""" + + if request_id in self._by_request: + old_id, old_obj = self._by_request[request_id] + self._request_by_object_id.pop(old_id, None) + if self._request_by_object is not None: + self._request_by_object.pop(old_obj, None) + + # Clean up any previous mapping using same object_id + if object_id in self._request_by_object_id: + old_req_id = self._request_by_object_id[object_id] + old_entry = self._by_request.pop(old_req_id, None) + if old_entry and self._request_by_object is not None: + _, old_obj = old_entry + self._request_by_object.pop(old_obj, None) + + def __len__(self) -> int: + return len(self._by_request) + + def __contains__(self, key: object) -> bool: + if isinstance(key, int): + return key in self._by_request + return key in self._request_by_object_id class RequestError(Exception): @@ -100,7 +308,10 @@ def __init__(self, reqId: int, code: int, message: str): @dataclass class Wrapper: - """Wrapper implementation for use with the IB class.""" + """Wrapper implementation for use with the IB class. + + Wrapper keeps track of `state`, accounts, accoutn values, positions, etc. and respond to requests, subscriptions and streming data. + """ # reference back to IB so wrapper can access API methods ib: "IB" @@ -114,14 +325,15 @@ class Wrapper: portfolio: dict[str, dict[int, PortfolioItem]] = field(init=False) """ account -> conId -> PortfolioItem """ - positions: defaultdict[str, dict[int, Position]] = field(init=False) + positions: dict[str, dict[int, Position]] = field(init=False) """ account -> conId -> Position """ - trades: dict[OrderKeyType, Trade] = field(init=False) - """ (client, orderId) or permId -> Trade """ + trades: BiDict[OrderKeyType, Trade] = field(init=False) + """ + by request id: permId -> Trade + by object id: (client, orderId) or permId -> Trade + """ - permId2Trade: dict[int, Trade] = field(init=False) - """ permId -> Trade """ _isReady: bool = field(init=False, default=False) """ wrapper initial status state """ @@ -133,31 +345,27 @@ class Wrapper: msgId2NewsBulletin: dict[int, NewsBulletin] = field(init=False) """ msgId -> NewsBulletin """ - tickers: dict[int, Ticker] = field(init=False) - """ hash(Contract) -> Ticker """ + tickers: BiDict[int, Ticker] = field(init=False) + """ + reqId -> Ticker + hash(Contract) -> Ticker + Ticker -> reqId + """ pendingTickers: set[Ticker] = field(init=False) - reqId2Ticker: dict[int, Ticker] = field(init=False) - """ reqId -> Ticker """ - - ticker2ReqId: dict[Union[int, str], dict[Ticker, int]] = field(init=False) - """ tickType -> Ticker -> reqId """ - - reqId2Subscriber: dict[int, Any] = field(init=False) + subscriptions: BiDict[int, SubscriptionType] = field(init=False) """ live bars or live scan data """ - reqId2PnL: dict[int, PnL] = field(init=False) - """ reqId -> PnL """ - - reqId2PnlSingle: dict[int, PnLSingle] = field(init=False) - """ reqId -> PnLSingle """ - - pnlKey2ReqId: dict[tuple, int] = field(init=False) - """ (account, modelCode) -> reqId """ + Pnl: BiDict[tuple[str, str], PnL] = field(init=False) + """ reqId -> PnL + (account, modelCode) -> reqId + """ - pnlSingleKey2ReqId: dict[tuple, int] = field(init=False) - """ (account, modelCode, conId) -> reqId """ + pnlSingles: BiDict[tuple[str, str, int], PnLSingle] = field(init=False) + """ reqId -> PnLSingle + (account, modelCode, conId) -> reqId + """ lastTime: datetime = field(init=False) """ UTC time of last network packet arrival. """ @@ -171,15 +379,8 @@ class Wrapper: clientId: int = field(init=False) wshMetaReqId: int = field(init=False) wshEventReqId: int = field(init=False) - _reqId2Contract: dict[int, Contract] = field(init=False) _timeout: float = field(init=False) - _futures: dict[Any, asyncio.Future] = field(init=False) - """ _futures and _results are linked by key. """ - - _results: dict[Any, Any] = field(init=False) - """ _futures and _results are linked by key. """ - _logger: logging.Logger = field( default_factory=lambda: logging.getLogger("ib_async.wrapper") ) @@ -194,9 +395,6 @@ def __post_init__(self): self.defaultEmptyPrice = self.defaults.emptyPrice self.defaultEmptySize = self.defaults.emptySize self.response_bus = ev.Event("Response bus") - self.ticker_bus = ev.Event("Ticker bus") - self.bar_bus = ev.Event("Bar bus") - self.reset() def reset(self): @@ -204,40 +402,32 @@ def reset(self): self.acctSummary = {} self.portfolio = defaultdict(dict) self.positions = defaultdict(dict) - self.trades = {} - self.permId2Trade = {} + self.trades = BiDict[OrderKeyType, Trade]() + self.tickers = BiDict[int, Ticker]() + self.subscriptions = BiDict[int, SubscriptionType]() self._isReady = False self.fills = {} self.newsTicks = [] self.msgId2NewsBulletin = {} - self.tickers = {} + self.tickers = BiDict[int, Ticker](track_objects_weakly=True) self.pendingTickers = set() - self.reqId2Ticker = {} - self.ticker2ReqId = defaultdict(dict) self.reqId2Subscriber = {} - self.reqId2PnL = {} - self.reqId2PnlSingle = {} - self.pnlKey2ReqId = {} - self.pnlSingleKey2ReqId = {} + self.Pnl = BiDict[tuple[str, str], PnL]() + self.pnlSingles = BiDict[tuple[str, str, int], PnLSingle]() self.lastTime = datetime.min self.time = -1 self.accounts = [] self.clientId = -1 self.wshMetaReqId = 0 self.wshEventReqId = 0 - self._reqId2Contract = {} self._timeout = 0 - self._futures = {} - self._results = {} - self.response_bus.clear() - self.ticker_bus.clear() - self.bar_bus.clear() self.setTimeout(0) def setEventsDone(self): """Set all subscription-type events as done.""" + self._logger.debug("wrapper.setEventsDone") events = [ticker.updateEvent for ticker in self.tickers.values()] - events += [sub.updateEvent for sub in self.reqId2Subscriber.values()] + events += [sub.updateEvent for sub in self.subscriptions.values()] for trade in self.trades.values(): events += [ trade.statusEvent, @@ -248,94 +438,61 @@ def setEventsDone(self): trade.cancelEvent, trade.cancelledEvent, ] + events += [self.response_bus] for event in events: event.set_done() def connectionClosed(self): error = ConnectionError("Socket disconnect") - for future in self._futures.values(): - if not future.done(): - future.set_exception(error) globalErrorEvent.emit(error) self.reset() - def startReq(self, key, contract=None, container=None): - """ - Start a new request and return the future that is associated - with the key and container. The container is a list by default. - """ - future: asyncio.Future = asyncio.Future() - self._futures[key] = future - self._results[key] = container if container is not None else [] - - if contract: - self._reqId2Contract[key] = contract - - return future - - def _endReq(self, key, result=None, success=True): - """ - Finish the future of corresponding key with the given result. - If no result is given then it will be popped of the general results. - """ - future = self._futures.pop(key, None) - self._reqId2Contract.pop(key, None) - if future: - if result is None: - result = self._results.pop(key, []) - - if not future.done(): - if success: - future.set_result(result) - else: - future.set_exception(result) + def _endReq(self, reqId: int | str): + self.response_bus.emit(reqId, None) def startTicker(self, reqId: int, contract: Contract, tickType: Union[int, str]): """ Start a tick request that has the reqId associated with the contract. Return the ticker. """ - ticker = self.reqId2Ticker.get(reqId) + ticker = self.tickers.get_by_request_id(reqId) if not ticker: ticker = Ticker(contract=contract, defaults=self.defaults) - self.reqId2Ticker[reqId] = ticker - self.ticker_bus.filter(lambda r, d, t: r == reqId).takewhile( - lambda r, data, t: data is not None - ).pluck(1, 2).connect(ticker._on_ticker_data) + self.tickers.add(reqId, hash(ticker.contract), ticker) + ticker.ticker_bus.takewhile(lambda r, data, t: data is not None).pluck( + 1, 2 + ).connect(ticker._on_ticker_data) return ticker def endTicker(self, ticker: Ticker, tickType: Union[int, str]): - reqId = None - for r, t in self.reqId2Ticker.items(): - if hash(t.contract) == hash(ticker.contract): - reqId = r - break - + reqId = self.tickers.get_request_id(hash(ticker.contract)) if reqId: - self.reqId2Ticker.pop(reqId, 0) - self.ticker_bus.emit(reqId, None, None) + self.tickers.remove_by_request_id(reqId) + ticker.ticker_bus.emit(reqId, None, None) return reqId - def startSubscription(self, reqId, subscriber, contract=None): + def startSubscription( + self, reqId: int, subscriber: SubscriptionType, contract: Contract | None = None + ): """Register a live subscription.""" - self._reqId2Contract[reqId] = contract - self.reqId2Subscriber[reqId] = subscriber + if contract: + # ib.reqRealTimeBars or ib.reqHistoricalDataAsync + self.subscriptions.add(reqId, hash(contract), subscriber) + + else: + # ib.reqScannerSubscription + self.subscriptions.add(reqId, reqId, subscriber) + # start subscription + subscriber.subscription_bus.takewhile(lambda r, data: data is not None).pluck( + 1 + ).map(self.ib._raise_if_error).partial(self.ib).connect(subscriber._on_data) def endSubscription(self, subscriber): """Unregister a live subscription.""" - self._reqId2Contract.pop(subscriber.reqId, None) - self.reqId2Subscriber.pop(subscriber.reqId, None) - - def orderKey(self, clientId: int, orderId: int, permId: int) -> OrderKeyType: - key: OrderKeyType - if orderId <= 0: - # order is placed manually from TWS - key = permId - else: - key = (clientId, orderId) - return key + subscriber.subscription_bus.emit(subscriber.reqId, None) + self.subscriptions.remove_by_request_id(subscriber.reqId) def setTimeout(self, timeout: float): self.lastTime = datetime.now(self.defaultTimezone) @@ -388,8 +545,8 @@ def updateAccountValue(self, value: AccountValue): self.ib.accountValueEvent.emit(value) def accountDownloadEnd(self, _account: str): - # sent after updateAccountValue and updatePortfolio both finished - self.response_bus.emit("accountValues", None) + """sent after updateAccountValue and updatePortfolio both finished""" + self._endReq("accountValues") def accountUpdateMulti(self, reqId: int, value: AccountValue): key = (value.account, value.tag, value.currency, value.modelCode) @@ -399,7 +556,7 @@ def accountUpdateMulti(self, reqId: int, value: AccountValue): self.response_bus.emit(reqId, value) def accountUpdateMultiEnd(self, reqId: int): - self.response_bus.emit(reqId, None) + self._endReq(reqId) def accountSummary(self, reqId: int, accountValue: AccountValue): key = (accountValue.account, accountValue.tag, accountValue.currency) @@ -409,7 +566,7 @@ def accountSummary(self, reqId: int, accountValue: AccountValue): self.response_bus.emit(reqId, accountValue) def accountSummaryEnd(self, reqId: int): - self.response_bus.emit(reqId, None) + self._endReq(reqId) def updatePortfolio(self, portfolioItem: PortfolioItem): account_portfolio = self.portfolio[portfolioItem.account] @@ -432,8 +589,9 @@ def position(self, position: Position): self.ib.positionEvent.emit(position) self.response_bus.emit("position", position) + def positionEnd(self): - self.response_bus.emit("position", None) + self._endReq("position") def positionMulti( self, @@ -452,8 +610,9 @@ def positionMultiEnd(self, reqId: int): def pnl( self, reqId: int, dailyPnL: float, unrealizedPnL: float, realizedPnL: float ): - pnl = self.reqId2PnL.get(reqId) + pnl = self.Pnl.get_by_request_id(reqId) if not pnl: + self._logger.error("pnl: No pnl found for reqId %s",reqId) return pnl.dailyPnL = dailyPnL @@ -470,8 +629,9 @@ def pnlSingle( realizedPnL: float, value: float, ): - pnlSingle = self.reqId2PnlSingle.get(reqId) + pnlSingle = self.pnlSingles.get_by_request_id(reqId) if not pnlSingle: + self._logger.error("pnlSingle: No pnlSingle found for reqId %s",reqId) return pnlSingle.position = pos @@ -481,35 +641,57 @@ def pnlSingle( pnlSingle.value = value self.ib.pnlSingleEvent.emit(pnlSingle) - def openOrder(self, trade: Trade): + def orderKey(self, clientId: int, orderId: int, permId: int) -> OrderKeyType: + key: OrderKeyType + if orderId <= 0: + # order is placed manually from TWS + key = permId + else: + key = (clientId, orderId) + return key + + def openOrder(self, trade: Trade,orderState: OrderState): + """ + This wrapper is called to: + + * feed in open orders at startup; + * feed in open orders or order updates from other clients and TWS + if clientId=master id; + * feed in manual orders and order updates from TWS if clientId=0; + * handle openOrders and allOpenOrders responses. + """ if trade.order.whatIf: - self.openOrderEnd() + self.response_bus.emit(trade.order.orderId,orderState) return + + key = self.orderKey( + trade.order.clientId, trade.order.orderId, trade.order.permId + ) + existing_trade = self.trades.get_by_object_id(key) + + if existing_trade: + # Trade is already known (from placeOrder), so update it + # 1. Mutate the existing trade object in-place + dataclassUpdate(existing_trade.order, trade.order) + dataclassUpdate(existing_trade.orderStatus, trade.orderStatus) + + # 2. Re-index the BiDict with the final permId + self.trades.update_request_id(key, existing_trade.order.permId) + else: - key = self.orderKey( - trade.order.clientId, trade.order.orderId, trade.order.permId - ) - existing_trade = self.trades.get(key) - if existing_trade: - # trade is already known, update it - existing_trade.order.permId = trade.order.permId - existing_trade.order.totalQuantity = trade.order.totalQuantity - existing_trade.order.lmtPrice = trade.order.lmtPrice - existing_trade.order.auxPrice = trade.order.auxPrice - existing_trade.order.orderType = trade.order.orderType - existing_trade.order.orderRef = trade.order.orderRef - else: # new trade - self.trades[key] = trade - self._logger.info(f"openOrder: {trade}") - - self.permId2Trade.setdefault(trade.order.permId, trade) + # This is a new trade, likely placed manually from TWS + self.trades.add(trade.order.permId, key, trade) + self._logger.info(f"openOrder: {trade}") + + final_trade = existing_trade or trade if self._isReady: - self.ib.newOrderEvent.emit(trade) - self.ib.client.updateReqId(trade.order.orderId + 1) - self.response_bus.emit("openOrders", trade) + self.ib.openOrderEvent.emit(final_trade) + if final_trade.order.orderId > 0: + self.ib.client.updateReqId(final_trade.order.orderId + 1) + self.response_bus.emit("openOrders", final_trade) def openOrderEnd(self): - self.response_bus.emit("openOrders", None) + self._endReq("openOrders") def completedOrder( self, contract: Contract, order: Order, orderStatus: OrderStatus @@ -517,71 +699,103 @@ def completedOrder( contract = Contract.recreate(contract) trade = Trade(contract, order, orderStatus, [], []) - if order.permId not in self.permId2Trade: - self.trades[order.permId] = trade - self.permId2Trade[order.permId] = trade + if order.permId not in self.trades: + key = self.orderKey(order.clientId, order.orderId, order.permId) + self.trades.add(order.permId, key, trade) self._logger.debug(f"completedOrders: {trade}") self.response_bus.emit("completedOrders", trade) def completedOrdersEnd(self): - self.response_bus.emit("completedOrders", None) + self._endReq("completedOrders") def orderStatus(self, orderStatus: OrderStatus): - permId = orderStatus.permId - trade = self.permId2Trade.get(permId) - if not trade: - # trade is not found, create a placeholder - trade = Trade(Contract(), Order(permId=permId), orderStatus, [], []) - self.permId2Trade[permId] = trade + key = self.orderKey( + orderStatus.clientId, orderStatus.orderId, orderStatus.permId + ) + trade = self.trades.get_by_object_id(key) + if trade: + if trade.orderStatus != orderStatus: + msg = "" + trade.orderStatus = orderStatus + elif ( + orderStatus.status == "Submitted" + and trade.log + and trade.log[-1].message == "Modify" + ): + # order modifications are acknowledged + msg = "Modified" + else: + msg = None - trade.orderStatus = orderStatus - logEntry = TradeLogEntry(self.lastTime, orderStatus.status) - trade.log.append(logEntry) - self._logger.info(f"orderStatus: {trade}") + if msg: + logEntry = TradeLogEntry(self.lastTime, orderStatus.status, msg) + trade.log.append(logEntry) + self._logger.info(f"orderStatus: {trade}") - if self._isReady: - trade.statusEvent.emit(trade) - self.ib.orderStatusEvent.emit(trade) + if self._isReady: + trade.statusEvent.emit(trade) + self.ib.orderStatusEvent.emit(trade) - if orderStatus.status == OrderStatus.Cancelled: - trade.cancelledEvent.emit(trade) - elif orderStatus.status == OrderStatus.Filled: - trade.filledEvent.emit(trade) + if orderStatus.status == OrderStatus.Cancelled: + trade.cancelledEvent.emit(trade) + elif orderStatus.status == OrderStatus.Filled: + trade.filledEvent.emit(trade) + else: + self._logger.error( + "orderStatus: No order found for orderId %s and clientId %s", + orderStatus.orderId, + orderStatus.clientId, + ) def execDetails(self, reqId: int, fill: Fill): + """ + This wrapper handles both live fills and responses to + reqExecutions. + """ permId = fill.execution.permId - trade = self.permId2Trade.get(permId) + trade = self.trades.get_by_request_id(permId) if not trade: - # trade is not found, create a placeholder - trade = Trade(fill.contract, Order(permId=permId), OrderStatus(), [], []) - self.permId2Trade[permId] = trade + key = self.orderKey(fill.execution.clientId, fill.execution.orderId, permId) + trade = self.trades.get_by_object_id(key) + # TODO: debug why spread contracts aren't fully detailed here. They have no + # legs in execDetails, but they do in orderStatus? + if trade and fill.contract == trade.contract: + fill.contract = trade.contract + else: + fill.contract = Contract.recreate(fill.contract) + + if fill.execution.execId not in self.fills: + self.fills[fill.execution.execId] = fill + if trade: + trade.fills.append(fill) + time = self.lastTime if self._isReady else fill.execution.time + logEntry = TradeLogEntry( + time, + trade.orderStatus.status, + f"Fill {fill.execution.shares}@{fill.execution.price}", + ) + trade.log.append(logEntry) + if self._isReady: + self._logger.info("execDetails: %s", fill) + self.ib.execDetailsEvent.emit(trade, fill) + trade.fillEvent.emit(trade, fill) - self.fills[fill.execution.execId] = fill - trade.fills.append(fill) - if self._isReady: - self.ib.execDetailsEvent.emit(trade, fill) - trade.fillEvent.emit(trade, fill) - if fill.commissionReport: - self.ib.commissionReportEvent.emit(trade, fill, fill.commissionReport) - trade.commissionReportEvent.emit(trade, fill, fill.commissionReport) self.response_bus.emit(reqId, fill) def execDetailsEnd(self, reqId: int): - self.response_bus.emit(reqId, None) + self._endReq(reqId) def commissionReport(self, commissionReport: CommissionReport): - if commissionReport.yield_ == UNSET_DOUBLE: - commissionReport.yield_ = 0.0 - - if commissionReport.realizedPNL == UNSET_DOUBLE: - commissionReport.realizedPNL = 0.0 + """After a fill TWS API will send us the final commission report. + commissionReportEvent emits final values + """ fill: Fill | None = self.fills.get(commissionReport.execId) if not fill: # commission report is not for this client return - trade = self.permId2Trade.get(fill.execution.permId) + trade = self.trades.get_by_request_id(fill.execution.permId) if not trade: return @@ -596,8 +810,17 @@ def commissionReport(self, commissionReport: CommissionReport): # before this connection started pass - def orderBound(self, reqId: int, apiClientId: int, apiOrderId: int): - pass + def orderBound(self, permId: int, clientId: int, orderId: int): + """ + https://www.interactivebrokers.com/campus/ibkr-api-page/twsapi-doc/#order-bound-notification + """ + self._logger.info( + "Bound order with permId: %s, clientId: %s, orderId: %s", + permId, + clientId, + orderId, + ) + self.ib.orderBoundEvent.emit(permId, clientId, orderId) def contractDetails(self, reqId: int, contractDetails: ContractDetails): self.response_bus.emit(reqId, contractDetails) @@ -605,7 +828,7 @@ def contractDetails(self, reqId: int, contractDetails: ContractDetails): bondContractDetails = contractDetails def contractDetailsEnd(self, reqId: int): - self.response_bus.emit(reqId, None) + self._endReq(reqId) def symbolSamples( self, reqId: int, contractDescriptions: list[ContractDescription] @@ -616,57 +839,27 @@ def marketRule(self, marketRuleId: int, priceIncrements: list[PriceIncrement]): self.response_bus.emit(f"marketRule-{marketRuleId}", priceIncrements) def marketDataType(self, reqId: int, marketDataId: int): - ticker = self.reqId2Ticker.get(reqId) + ticker = self.tickers.get_by_request_id(reqId) if ticker: ticker.marketDataType = marketDataId - def realtimeBar( - self, - reqId: int, - time: int, - open_: float, - high: float, - low: float, - close: float, - volume: float, - wap: float, - count: int, - ): - dt = datetime.fromtimestamp(time, self.defaultTimezone) - bar = RealTimeBar(dt, -1, open_, high, low, close, volume, wap, count) - bars = self.reqId2Subscriber.get(reqId) - if bars is not None: - bars.append(bar) - self.ib.barUpdateEvent.emit(bars, True) - bars.updateEvent.emit(bars, True) + def realtimeBar(self, reqId: int, bar: RealTimeBar): + if bar is not None: + bars_subscription = self.subscriptions.get_by_request_id(reqId) + if bars_subscription is not None: + bars_subscription.subscription_bus.emit(reqId, bar) def historicalData(self, reqId: int, bars: list[BarData]): if bars is not None: self.response_bus.emit(reqId, bars) def historicalDataEnd(self, reqId, _start: str, _end: str): - self.response_bus.emit(reqId, None) - + self._endReq(reqId) + def historicalDataUpdate(self, reqId: int, bar: BarData): - bars = self.reqId2Subscriber.get(reqId) - if not bars: - return - - bar.date = parseIBDatetime(bar.date) # type: ignore - lastDate = bars[-1].date - if bar.date < lastDate: - return - - hasNewBar = len(bars) == 0 or bar.date > lastDate - if hasNewBar: - bars.append(bar) - elif bars[-1] != bar: - bars[-1] = bar - else: - return - - self.ib.barUpdateEvent.emit(bars, hasNewBar) - bars.updateEvent.emit(bars, hasNewBar) + subscription = self.subscriptions.get_by_request_id(reqId) + if subscription: + subscription.subscription_bus.emit(reqId, bar) def headTimestamp(self, reqId: int, headTimestamp: str): try: @@ -678,149 +871,96 @@ def headTimestamp(self, reqId: int, headTimestamp: str): def historicalTicks(self, reqId: int, ticks: list[HistoricalTick], done: bool): self.response_bus.emit(reqId, ticks) if done: - self.response_bus.emit(reqId, None) + self._endReq(reqId) def historicalTicksBidAsk( self, reqId: int, ticks: list[HistoricalTickBidAsk], done: bool ): self.response_bus.emit(reqId, ticks) if done: - self.response_bus.emit(reqId, None) + self._endReq(reqId) def historicalTicksLast( self, reqId: int, ticks: list[HistoricalTickLast], done: bool ): self.response_bus.emit(reqId, ticks) if done: - self.response_bus.emit(reqId, None) + self._endReq(reqId) - # additional wrapper method provided by Client def priceSizeTick(self, reqId: int, tick_price: TickPriceData): - ticker = self.reqId2Ticker.get(reqId) + ticker = self.tickers.get_by_request_id(reqId) if not ticker: self._logger.error(f"priceSizeTick: Unknown reqId: {reqId}") return - self.ticker_bus.emit(reqId, tick_price, self.lastTime) + ticker.ticker_bus.emit(reqId, tick_price, self.lastTime) self.pendingTickers.add(ticker) def tickSize(self, reqId: int, tick_size: TickSizeData): - ticker = self.reqId2Ticker.get(reqId) + ticker = self.tickers.get_by_request_id(reqId) if not ticker: self._logger.error(f"tickSize: Unknown reqId: {reqId}") return - self.ticker_bus.emit(reqId, tick_size, self.lastTime) + ticker.ticker_bus.emit(reqId, tick_size, self.lastTime) self.pendingTickers.add(ticker) def tickString(self, reqId: int, tick_string: TickStringData): - if not (ticker := self.reqId2Ticker.get(reqId)): + if not (ticker := self.tickers.get_by_request_id(reqId)): return - self.ticker_bus.emit(reqId, tick_string, self.lastTime) + ticker.ticker_bus.emit(reqId, tick_string, self.lastTime) self.pendingTickers.add(ticker) def tickGeneric(self, reqId: int, tick_generic: TickGenericData): - if not (ticker := self.reqId2Ticker.get(reqId)): + if not (ticker := self.tickers.get_by_request_id(reqId)): return - self.ticker_bus.emit(reqId, tick_generic, self.lastTime) + ticker.ticker_bus.emit(reqId, tick_generic, self.lastTime) self.pendingTickers.add(ticker) def tickReqParams(self, reqId: int, tickParams: TickParams): - if not (ticker := self.reqId2Ticker.get(reqId)): + if not (ticker := self.tickers.get_by_request_id(reqId)): return ticker.minTick = tickParams.minTick ticker.bboExchange = tickParams.bboExchange ticker.snapshotPermissions = tickParams.snapshotPermissions def tickSnapshotEnd(self, reqId: int): - self._endReq(reqId) - - def tickByTickAllLast( - self, - reqId: int, - tickType: int, - time: int, - price: float, - size: float, - tickAttribLast: TickAttribLast, - exchange, - specialConditions, - ): - ticker = self.reqId2Ticker.get(reqId) - if not ticker: - self._logger.error(f"tickByTickAllLast: Unknown reqId: {reqId}") + ticker = self.tickers.get_by_request_id(reqId) + if ticker: + self.endTicker(ticker, "") return - if price == -1 and size == 0: - price = self.defaultEmptyPrice - size = self.defaultEmptySize - - ticker.prevLast = ticker.last - ticker.prevLastSize = ticker.lastSize - ticker.last = price - ticker.lastSize = size - - tick = TickByTickAllLast( - tickType, - self.lastTime, - price, - size, - tickAttribLast, - exchange, - specialConditions, - ) + self._logger.error(f"tickSnapshotEnd: Unknown reqId: {reqId}") - ticker.tickByTicks.append(tick) + def tickByTickAllLast(self, reqId: int, historicalTickLast: HistoricalTickLast): + ticker = self.tickers.get_by_request_id(reqId) + if not ticker: + self._logger.error(f"tickByTickBidAsk: Unknown reqId: {reqId}") + return + ticker.ticker_bus.emit(reqId, historicalTickLast, None) self.pendingTickers.add(ticker) def tickByTickBidAsk( self, reqId: int, - time: int, - bidPrice: float, - askPrice: float, - bidSize: float, - askSize: float, - tickAttribBidAsk: TickAttribBidAsk, + historicalTickBidAsk: HistoricalTickBidAsk, ): - ticker = self.reqId2Ticker.get(reqId) + ticker = self.tickers.get_by_request_id(reqId) if not ticker: self._logger.error(f"tickByTickBidAsk: Unknown reqId: {reqId}") return - - if bidPrice != ticker.bid: - ticker.prevBid = ticker.bid - ticker.bid = bidPrice if bidPrice > 0 else self.defaultEmptyPrice - - if bidSize != ticker.bidSize: - ticker.prevBidSize = ticker.bidSize - ticker.bidSize = bidSize if bidSize > 0 else self.defaultEmptySize - - if askPrice != ticker.ask: - ticker.prevAsk = ticker.ask - ticker.ask = askPrice if askPrice > 0 else self.defaultEmptyPrice - - if askSize != ticker.askSize: - ticker.prevAskSize = ticker.askSize - ticker.askSize = askSize if askSize > 0 else self.defaultEmptySize - - tick = TickByTickBidAsk( - self.lastTime, bidPrice, askPrice, bidSize, askSize, tickAttribBidAsk - ) - - ticker.tickByTicks.append(tick) + ticker.ticker_bus.emit(reqId, historicalTickBidAsk, None) self.pendingTickers.add(ticker) - def tickByTickMidPoint(self, reqId: int, time: int, midPoint: float): - ticker = self.reqId2Ticker.get(reqId) + def tickByTickMidPoint(self, reqId: int, historicalTick: HistoricalTick): + ticker = self.tickers.get_by_request_id(reqId) if not ticker: self._logger.error(f"tickByTickMidPoint: Unknown reqId: {reqId}") return - tick = TickByTickMidPoint(self.lastTime, midPoint) - ticker.tickByTicks.append(tick) + ticker.ticker_bus.emit(reqId, historicalTick, None) self.pendingTickers.add(ticker) def smartComponents(self, reqId, components): - self._endReq(reqId, components) + self.response_bus.emit(reqId, components) def mktDepthExchanges( self, depthMktDataDescriptions: list[DepthMktDataDescription] @@ -851,7 +991,10 @@ def updateMktDepthL2( ): # operation: 0 = insert, 1 = update, 2 = delete # side: 0 = ask, 1 = bid - ticker = self.reqId2Ticker[reqId] + ticker = self.tickers.get_by_request_id(reqId) + if not ticker: + self._logger.error(f"updateMktDepthL2: Unknown reqId: {reqId}") + return # 'dom' is a dict so we can address position updates directly dom = ticker.domBidsDict if side else ticker.domAsksDict @@ -898,17 +1041,15 @@ def updateMktDepthL2( ticker.domTicks.append(tick) self.pendingTickers.add(ticker) - def tickOptionComputation( - self, reqId: int, tick_computation: TickComputationData - ): - ticker = self.reqId2Ticker.get(reqId) + def tickOptionComputation(self, reqId: int, tick_computation: TickComputationData): + ticker = self.tickers.get_by_request_id(reqId) if ticker: # reply from reqMktData # https://interactivebrokers.github.io/tws-api/tick_types.html self.ticker_bus.emit(reqId, tick_computation, self.lastTime) self.pendingTickers.add(ticker) - elif reqId and tick_computation.tickType == TickType.NOT_SET: + elif reqId: # reply from calculateImpliedVolatility or calculateOptionPrice self.response_bus.emit(reqId, tick_computation.computation) else: @@ -918,59 +1059,27 @@ def deltaNeutralValidation(self, reqId: int, dnc: DeltaNeutralContract): pass def fundamentalData(self, reqId: int, data: str): - self._endReq(reqId, data) + self.response_bus.emit(reqId, data) def scannerParameters(self, xml: str): - self._endReq("scannerParams", xml) + self.response_bus.emit("scannerParams", xml) - def scannerData( - self, - reqId: int, - rank: int, - contractDetails: ContractDetails, - distance: str, - benchmark: str, - projection: str, - legsStr: str, - ): - data = ScanData(rank, contractDetails, distance, benchmark, projection, legsStr) - dataList = self.reqId2Subscriber.get(reqId) - if dataList is None: - dataList = self._results.get(reqId) + def scannerData(self, reqId: int, scanData: list[ScanData]): + dataList = self.subscriptions.get_by_request_id(reqId) if dataList is not None: - if rank == 0: - dataList.clear() - dataList.append(data) + dataList.subscription_bus.emit(reqId, scanData) def scannerDataEnd(self, reqId: int): - dataList = self._results.get(reqId) + dataList = self.subscriptions.get_by_request_id(reqId) if dataList is not None: - self._endReq(reqId) - else: - dataList = self.reqId2Subscriber.get(reqId) - - if dataList is not None: - self.ib.scannerDataEvent.emit(dataList) - dataList.updateEvent.emit(dataList) + self.endSubscription(dataList) def histogramData(self, reqId: int, items: list[HistogramData]): self.response_bus.emit(reqId, items) - def securityDefinitionOptionParameter( - self, - reqId: int, - exchange: str, - underlyingConId: int, - tradingClass: str, - multiplier: str, - expirations: list[str], - strikes: list[float], - ): - chain = OptionChain( - exchange, underlyingConId, tradingClass, multiplier, expirations, strikes - ) - self._results[reqId].append(chain) + def securityDefinitionOptionParameter(self, reqId: int, optionChain: OptionChain): + self.response_bus.emit(reqId, optionChain) def securityDefinitionOptionParameterEnd(self, reqId: int): self._endReq(reqId) @@ -1042,7 +1151,7 @@ def tickEFP( def historicalSchedule( self, reqId: int, - schedule:HistoricalSchedule, + schedule: HistoricalSchedule, ): self.response_bus.emit(reqId, schedule) @@ -1055,7 +1164,7 @@ def wshEventData(self, reqId: int, dataJson: str): self._endReq(reqId, dataJson) def userInfo(self, reqId: int, whiteBrandingId: str): - self._endReq(reqId) + self.response_bus.emit(reqId, whiteBrandingId) def softDollarTiers(self, reqId: int, tiers: list[SoftDollarTier]): pass @@ -1082,21 +1191,25 @@ def error( # reqId is a local orderId, but is delivered as -1 if this is a # non-order-related error if reqId != -1: - trade = self.trades.get((self.clientId, reqId)) + trade = self.trades.get_by_object_id((self.clientId, reqId)) # Warnings are currently: # 105 - Order being modified does not match the original order. (?) - # 110 - The price does not conform to the minimum price variation for this contract. + # 110 - The price does not conform to the minimum price variation for this + # contract. # 165 - Historical market Data Service query message. # 321 - Server error when validating an API client request. # 329 - Order modify failed. Cannot change to the new order type. # 399 - Order message error - # 404 - Shares for this order are not immediately available for short sale. The order will be held while we attempt to locate the shares. + # 404 - Shares for this order are not immediately available for short sale. The + # order will be held while we attempt to locate the shares. # 434 - The order size cannot be zero. # 492 - ? not listed # 10167 ? not listed - # Note: error 321 means error validing, but if the message is the result of a MODIFY, the order _is still live_ and we must not delete it. - # TODO: investigate if error 321 happens on _new_ order placement with incorrect parameters too, then we should probably delete the order. + # Note: error 321 means error validing, but if the message is the result of a + # MODIFY, the order _is still live_ and we must not delete it. + # TODO: investigate if error 321 happens on _new_ order placement with + # incorrect parameters too, then we should probably delete the order. # Previously this was included as a Warning condition, but 202 is literally # "Order Canceled" error status, so now it is an order-delete error: @@ -1118,14 +1231,22 @@ def error( isWarning = False msg = f"{'Warning' if isWarning else 'Error'} {errorCode}, reqId {reqId}: {errorString}" - - contract = self._reqId2Contract.get(reqId) + # get contract + if reqId in self.subscriptions: + subscription = self.subscriptions.get_by_request_id(reqId) + contract = getattr(subscription, "contract", None) + elif reqId in self.tickers: + ticker = self.tickers.get_by_request_id(reqId) + contract = getattr(ticker, "contract", None) + else: + contract = None if contract: msg += f", contract: {contract}" if isWarning: # Record warnings into the trade object, but unlike the _error_ case, - # DO NOT delete the trade object because the order is STILL LIVE at the broker. + # DO NOT delete the trade object because the order is STILL LIVE at the + # broker. if trade: status = trade.orderStatus.status = OrderStatus.ValidationError logEntry = TradeLogEntry(self.lastTime, status, msg, errorCode) @@ -1184,7 +1305,7 @@ def error( dataList.updateEvent.emit(dataList) elif errorCode == 317: # Market depth data has been RESET - ticker = self.reqId2Ticker.get(reqId) + ticker = self.tickers.get_by_request_id(reqId) if ticker: # clear all DOM levels ticker.domTicks += [ diff --git a/tests/conftest.py b/tests/conftest.py index 7d04a04a..618b3e90 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,10 @@ +import asyncio +from unittest.mock import Mock, patch + import pytest import ib_async as ibi +from ib_async import IB @pytest.fixture(scope="session") @@ -16,3 +20,12 @@ async def ib(): await ib.connectAsync() yield ib ib.disconnect() + +@pytest.fixture +def mock_ib(): + """Fixture for a mocked IB instance.""" + ib_instance = IB() + ib_instance.client.isConnected = Mock(return_value=True) + ib_instance.client.isReady = Mock(return_value=True) + ib_instance.client.serverVersion = Mock(return_value=201) + return ib_instance diff --git a/tests/test_account_converters.py b/tests/test_account_converters.py new file mode 100644 index 00000000..650e0dd8 --- /dev/null +++ b/tests/test_account_converters.py @@ -0,0 +1,188 @@ +import pytest +from unittest.mock import Mock + +from ib_async.objects import AccountValue, Position, PortfolioItem +from ib_async.contract import Contract +from ib_async.protobuf.AccountDataRequest_pb2 import ( + AccountDataRequest as AccountDataRequestProto, +) +from ib_async.protobuf.AccountUpdatesMultiRequest_pb2 import ( + AccountUpdatesMultiRequest as AccountUpdatesMultiRequestProto, +) +from ib_async.protobuf.AccountUpdateMulti_pb2 import ( + AccountUpdateMulti as AccountUpdateMultiProto, +) +from ib_async.protobuf.AccountSummary_pb2 import AccountSummary as AccountSummaryProto +from ib_async.protobuf.AccountValue_pb2 import AccountValue as AccountValueProto +from ib_async.protobuf.CancelAccountUpdatesMulti_pb2 import ( + CancelAccountUpdatesMulti as CancelAccountUpdatesMultiProto, +) +from ib_async.protobuf.Position_pb2 import Position as PositionProto +from ib_async.protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto +from ib_async.protobuf.Contract_pb2 import Contract as ContractProto +from ib_async.protobuf.IdsRequest_pb2 import IdsRequest as IdsRequestProto + +from ib_async.protobuf_converters.account_converters import ( + createPosition, + createAccountDataRequestProto, + createAccountMultiRequestProto, + createCancelAccMultiRequestProto, + createAccountValueFromUpdateMulti, + createAccountValue, + createAccountSummary, + createPortfolioItem, + createUserInfoRequestProto, +) + + +class TestAccountConverters: + def test_createPosition(self): + mock_contract_proto = ContractProto(conId=123, symbol="SPY") + position_proto = PositionProto( + account="DU12345", + contract=mock_contract_proto, + position="100.0", + avgCost=150.0, + ) + position = createPosition(position_proto) + + assert isinstance(position, Position) + assert position.account == "DU12345" + assert position.contract.conId == 123 + assert position.contract.symbol == "SPY" + assert position.position == 100.0 + assert position.avgCost == 150.0 + + def test_createAccountDataRequestProto(self): + proto = createAccountDataRequestProto(True, "U12345") + assert isinstance(proto, AccountDataRequestProto) + assert proto.subscribe is True + assert proto.acctCode == "U12345" + + def test_createAccountMultiRequestProto(self): + proto = createAccountMultiRequestProto(1, "U12345", "Cash", True) + assert isinstance(proto, AccountUpdatesMultiRequestProto) + assert proto.reqId == 1 + assert proto.account == "U12345" + assert proto.modelCode == "Cash" + assert proto.ledgerAndNLV is True + + proto_minimal = createAccountMultiRequestProto(2, "", "", False) + assert proto_minimal.reqId == 2 + assert not proto_minimal.HasField("account") + assert not proto_minimal.HasField("modelCode") + assert not proto_minimal.HasField("ledgerAndNLV") + + def test_createCancelAccMultiRequestProto(self): + proto = createCancelAccMultiRequestProto(1) + assert isinstance(proto, CancelAccountUpdatesMultiProto) + assert proto.reqId == 1 + + def test_createAccountValueFromUpdateMulti(self): + account_update_multi_proto = AccountUpdateMultiProto( + account="DU12345", key="NetLiquidation", value="100000.00", currency="USD" + ) + account_value = createAccountValueFromUpdateMulti(account_update_multi_proto) + + assert isinstance(account_value, AccountValue) + assert account_value.account == "DU12345" + assert account_value.tag == "NetLiquidation" + assert account_value.value == "100000.00" + assert account_value.currency == "USD" + assert account_value.modelCode == "" + + # Test with missing fields + account_update_multi_proto_minimal = AccountUpdateMultiProto() + account_value_minimal = createAccountValueFromUpdateMulti( + account_update_multi_proto_minimal + ) + assert account_value_minimal.account == "" + assert account_value_minimal.tag == "" + assert account_value_minimal.value == "" + assert account_value_minimal.currency == "" + + def test_createAccountValue(self): + account_value_proto = AccountValueProto( + accountName="DU12345", key="CashBalance", value="50000.00", currency="USD" + ) + account_value = createAccountValue(account_value_proto) + + assert isinstance(account_value, AccountValue) + assert account_value.account == "DU12345" + assert account_value.tag == "CashBalance" + assert account_value.value == "50000.00" + assert account_value.currency == "USD" + assert account_value.modelCode == "" + + # Test with missing fields + account_value_proto_minimal = AccountValueProto() + account_value_minimal = createAccountValue(account_value_proto_minimal) + assert account_value_minimal.account == "" + assert account_value_minimal.tag == "" + assert account_value_minimal.value == "" + assert account_value_minimal.currency == "" + + def test_createAccountSummary(self): + account_summary_proto = AccountSummaryProto( + account="DU12345", tag="TotalCashValue", value="150000.00", currency="USD" + ) + account_value = createAccountSummary(account_summary_proto) + + assert isinstance(account_value, AccountValue) + assert account_value.account == "DU12345" + assert account_value.tag == "TotalCashValue" + assert account_value.value == "150000.00" + assert account_value.currency == "USD" + assert account_value.modelCode == "" + + # Test with missing fields + account_summary_proto_minimal = AccountSummaryProto() + account_value_minimal = createAccountSummary(account_summary_proto_minimal) + assert account_value_minimal.account == "" + assert account_value_minimal.tag == "" + assert account_value_minimal.value == "" + assert account_value_minimal.currency == "" + + def test_createPortfolioItem(self): + mock_contract_proto = ContractProto(conId=456, symbol="AAPL") + portfolio_value_proto = PortfolioValueProto( + contract=mock_contract_proto, + position="50.0", + marketPrice=170.0, + marketValue=8500.0, + averageCost=160.0, + unrealizedPNL=500.0, + realizedPNL=100.0, + accountName="DU12345", + ) + portfolio_item = createPortfolioItem(portfolio_value_proto) + + assert isinstance(portfolio_item, PortfolioItem) + assert portfolio_item.contract.conId == 456 + assert portfolio_item.contract.symbol == "AAPL" + assert portfolio_item.position == 50.0 + assert portfolio_item.marketPrice == 170.0 + assert portfolio_item.marketValue == 8500.0 + assert portfolio_item.averageCost == 160.0 + assert portfolio_item.unrealizedPNL == 500.0 + assert portfolio_item.realizedPNL == 100.0 + assert portfolio_item.account == "DU12345" + + # Test with zero position + portfolio_value_proto_zero_pos = PortfolioValueProto( + contract=mock_contract_proto, + position="0.0", + marketPrice=170.0, + marketValue=0.0, + averageCost=0.0, + unrealizedPNL=0.0, + realizedPNL=0.0, + accountName="DU12345", + ) + portfolio_item_zero_pos = createPortfolioItem(portfolio_value_proto_zero_pos) + assert portfolio_item_zero_pos.position == 0.0 + + def test_createUserInfoRequestProto(self): + proto = createUserInfoRequestProto(10) + assert isinstance(proto, IdsRequestProto) + assert proto.numIds == 10 diff --git a/tests/test_bidict.py b/tests/test_bidict.py new file mode 100644 index 00000000..b3151d49 --- /dev/null +++ b/tests/test_bidict.py @@ -0,0 +1,157 @@ +"""Test wrapper.BiDict.""" + +from ib_async.wrapper import BiDict + + +class TestBiDict: + def test_add_and_get(self): + bidict = BiDict[str, object]() + bidict.add(1, "obj_a", "Value A") + bidict.add(2, "obj_b", "Value B") + + assert bidict.get_by_request_id(1) == "Value A" + assert bidict.get_by_object_id("obj_b") == "Value B" + assert bidict.get_request_id("obj_a") == 1 + assert len(bidict) == 2 + + def test_add_overwrite_request_id(self): + bidict = BiDict[str, object]() + bidict.add(1, "obj_a", "Value A") + bidict.add(1, "obj_c", "Value C") # Overwrite request_id 1 + + assert bidict.get_by_request_id(1) == "Value C" + assert ( + bidict.get_by_object_id("obj_a") is None + ) # Old object_id should be removed + assert bidict.get_request_id("obj_c") == 1 + assert len(bidict) == 1 + + def test_add_overwrite_object_id(self): + bidict = BiDict[str, object]() + bidict.add(1, "obj_a", "Value A") + bidict.add(2, "obj_a", "Value B") # Overwrite object_id "obj_a" + + assert bidict.get_by_object_id("obj_a") == "Value B" + assert bidict.get_by_request_id(1) is None # Old request_id should be removed + assert bidict.get_request_id("obj_a") == 2 + assert len(bidict) == 1 + + def test_remove_by_request_id(self): + bidict = BiDict[str, object]() + bidict.add(1, "obj_a", "Value A") + bidict.remove_by_request_id(1) + + assert bidict.get_by_request_id(1) is None + assert bidict.get_by_object_id("obj_a") is None + assert bidict.get_request_id("obj_a") is None + assert len(bidict) == 0 + + def test_remove_by_object_id(self): + bidict = BiDict[str, object]() + bidict.add(1, "obj_a", "Value A") + bidict.remove_by_object_id("obj_a") + + assert bidict.get_by_request_id(1) is None + assert bidict.get_by_object_id("obj_a") is None + assert bidict.get_request_id("obj_a") is None + assert len(bidict) == 0 + + def test_contains(self): + bidict = BiDict[str, object]() + bidict.add(1, "obj_a", "Value A") + + assert 1 in bidict + assert "obj_a" in bidict + assert 2 not in bidict + assert "obj_b" not in bidict + + def test_track_objects_weakly(self): + class MyObject: + def __init__(self, name): + self.name = name + + def __hash__(self): + return hash(self.name) + + def __eq__(self, other): + return isinstance(other, MyObject) and self.name == other.name + + bidict = BiDict[str, MyObject](track_objects_weakly=True) + + obj_a = MyObject("A") + obj_b = MyObject("B") + + bidict.add(1, "id_a", obj_a) + bidict.add(2, "id_b", obj_b) + + assert bidict.get_request_id_by_object(obj_a) == 1 + assert bidict.get_request_id_by_object(obj_b) == 2 + + # Test weak reference: if obj_a is no longer referenced, it should be removed + del obj_a + # The weakref might not be cleared immediately, but eventually it should be + # gc.collect is not reliable to assert in tests + # so just do a simple smoke test + assert bidict.get_request_id_by_object(obj_b) == 2 + + # Test removal with weak references + obj_c = MyObject("C") + bidict.add(3, "id_c", obj_c) + assert bidict.get_request_id_by_object(obj_c) == 3 + bidict.remove_by_request_id(3) + assert bidict.get_request_id_by_object(obj_c) is None + + obj_d = MyObject("D") + bidict.add(4, "id_d", obj_d) + assert bidict.get_request_id_by_object(obj_d) == 4 + bidict.remove_by_object_id("id_d") + assert bidict.get_request_id_by_object(obj_d) is None + + def test_values(self): + bidict = BiDict[str, object]() + bidict.add(1, "obj_a", "Value A") + bidict.add(2, "obj_b", "Value B") + # get all values + values = bidict.values() + assert len(values) == 2 + assert "Value A" in values + assert "Value B" in values + # get values after delete + bidict.remove_by_request_id(1) + values = bidict.values() + assert len(values) == 1 + assert "Value B" in values + assert "Value A" not in values + + def test_update_request_id(self): + bidict = BiDict[str, object]() + obj_value = "My Test Object" + object_id = "temp_key" + initial_request_id = 100 # Represents an orderId + final_request_id = 5000 # Represents a permId + + # Add the object with initial (dummy) request_id + bidict.add(initial_request_id, object_id, obj_value) + + # Verify initial state + assert bidict.get_by_request_id(initial_request_id) is obj_value + assert bidict.get_by_object_id(object_id) is obj_value + assert bidict.get_request_id(object_id) == initial_request_id + assert len(bidict) == 1 + + # Perform the update + bidict.update_request_id(object_id, final_request_id) + + # Verify updated state + assert bidict.get_by_request_id(initial_request_id) is None # Old request_id should no longer work + assert bidict.get_by_request_id(final_request_id) is obj_value # New request_id should work + assert bidict.get_by_object_id(object_id) is obj_value # object_id should still work + assert bidict.get_request_id(object_id) == final_request_id # object_id maps to new request_id + assert len(bidict) == 1 # Size should remain the same + + # Verify object identity (it's the same object, just re-indexed) + retrieved_obj_by_new_req_id = bidict.get_by_request_id(final_request_id) + retrieved_obj_by_obj_id = bidict.get_by_object_id(object_id) + assert retrieved_obj_by_new_req_id is obj_value + assert retrieved_obj_by_obj_id is obj_value + assert retrieved_obj_by_new_req_id is retrieved_obj_by_obj_id diff --git a/tests/test_contract_converters.py b/tests/test_contract_converters.py new file mode 100644 index 00000000..911a668f --- /dev/null +++ b/tests/test_contract_converters.py @@ -0,0 +1,267 @@ +import pytest +from unittest.mock import Mock +from ib_async.contract import ( + ComboLeg, + Contract, + ContractDescription, + ContractDetails, + DeltaNeutralContract, + FundAssetType, + FundDistributionPolicyIndicator, + IneligibilityReason, +) +from ib_async.objects import OptionChain, SmartComponent +from ib_async.order import Order +from ib_async.protobuf.ComboLeg_pb2 import ComboLeg as ComboLegProto +from ib_async.protobuf.Contract_pb2 import Contract as ContractProto +from ib_async.protobuf.ContractData_pb2 import ContractData as ContractDataProto +from ib_async.protobuf.ContractDescription_pb2 import ( + ContractDescription as ContractDescriptionProto, +) +from ib_async.protobuf.ContractDetails_pb2 import ContractDetails as ContractDetailsProto +from ib_async.protobuf.DeltaNeutralContract_pb2 import ( + DeltaNeutralContract as DeltaNeutralContractProto, +) +from ib_async.protobuf.MarketRuleRequest_pb2 import MarketRuleRequest as MarketRuleRequestProto +from ib_async.protobuf.MatchingSymbolsRequest_pb2 import ( + MatchingSymbolsRequest as MatchingSymbolsRequestProto, +) +from ib_async.protobuf.SecDefOptParameter_pb2 import ( + SecDefOptParameter as SecDefOptParameterProto, +) +from ib_async.protobuf.SecDefOptParamsRequest_pb2 import ( + SecDefOptParamsRequest as SecDefOptParamsRequestProto, +) +from ib_async.protobuf.SmartComponentsRequest_pb2 import ( + SmartComponentsRequest as SmartComponentsRequestProto, +) +from ib_async.protobuf.SmartComponents_pb2 import SmartComponents as SmartComponentsProto +from ib_async.protobuf_converters.contract_converters import ( + createSecDefOptParamsRequestProto, + createOptionChain, + createContract, + createComboLegs, + createDeltaNeutralContract, + createIneligibilityReasonList, + setLastTradeDate, + createContractDetails, + createContractDescription, + createMatchingSymbolsRequestProto, + createMarketRuleRequestProto, + createContractProto, + createDeltaNeutralContractProto, + createComboLegProtoList, + createComboLegProto, + createSmartComponentsRequestProto, + createSmartComponents, +) + +class TestContractConverters: + + def test_createSecDefOptParamsRequestProto(self): + proto = createSecDefOptParamsRequestProto(1, "SPX", "SMART", "IND", 123) + assert isinstance(proto, SecDefOptParamsRequestProto) + assert proto.reqId == 1 + assert proto.underlyingSymbol == "SPX" + assert proto.futFopExchange == "SMART" + assert proto.underlyingSecType == "IND" + assert proto.underlyingConId == 123 + + def test_createOptionChain(self): + proto = SecDefOptParameterProto( + exchange="CBOE", + underlyingConId=456, + tradingClass="SPXW", + multiplier="100", + expirations=["202512", "202603"], + strikes=[4000.0, 4100.0] + ) + option_chain = createOptionChain(proto) + assert isinstance(option_chain, OptionChain) + assert option_chain.exchange == "CBOE" + assert option_chain.underlyingConId == 456 + assert option_chain.tradingClass == "SPXW" + assert option_chain.multiplier == "100" + assert option_chain.expirations == ["202512", "202603"] + assert option_chain.strikes == [4000.0, 4100.0] + + def test_createContract(self): + proto = ContractProto( + conId=1, + symbol="AAPL", + secType="STK", + exchange="SMART", + currency="USD" + ) + contract = createContract(proto) + assert isinstance(contract, Contract) + assert contract.conId == 1 + assert contract.symbol == "AAPL" + assert contract.secType == "STK" + assert contract.exchange == "SMART" + assert contract.currency == "USD" + + def test_createComboLegs(self): + proto = ContractProto() + leg1 = proto.comboLegs.add() + leg1.conId = 1 + leg1.ratio = 1 + leg1.action = "BUY" + leg1.exchange = "SMART" + + combo_legs = createComboLegs(proto) + assert len(combo_legs) == 1 + leg = combo_legs[0] + assert isinstance(leg, ComboLeg) + assert leg.conId == 1 + assert leg.ratio == 1 + assert leg.action == "BUY" + assert leg.exchange == "SMART" + + def test_createDeltaNeutralContract(self): + proto = ContractProto() + dn_proto = proto.deltaNeutralContract + dn_proto.conId = 123 + dn_proto.delta = 0.5 + dn_proto.price = 10.0 + + dn_contract = createDeltaNeutralContract(proto) + assert isinstance(dn_contract, DeltaNeutralContract) + assert dn_contract.conId == 123 + assert dn_contract.delta == 0.5 + assert dn_contract.price == 10.0 + + def test_createIneligibilityReasonList(self): + proto = ContractDetailsProto() + reason1 = proto.ineligibilityReasonList.add() + reason1.id = "1" + reason1.description = "Reason 1" + + reasons = createIneligibilityReasonList(proto) + assert len(reasons) == 1 + reason = reasons[0] + assert isinstance(reason, IneligibilityReason) + assert reason.id_ == "1" + assert reason.description == "Reason 1" + + def test_setLastTradeDate(self): + cd = ContractDetails(contract=Contract()) + setLastTradeDate("20251219", cd, isBond=False) + assert cd.contract.lastTradeDateOrContractMonth == "20251219" + + cd_bond = ContractDetails(contract=Contract()) + setLastTradeDate("20300101", cd_bond, isBond=True) + assert cd_bond.maturity == "20300101" + + def test_createContractDetails(self): + contract_proto = ContractProto(symbol="TSLA", secType="STK") + details_proto = ContractDetailsProto(marketName="Tesla", longName="Tesla Inc.") + msg = ContractDataProto(contract=contract_proto, contractDetails=details_proto) + + cd = createContractDetails(msg) + assert isinstance(cd, ContractDetails) + assert cd.contract.symbol == "TSLA" + assert cd.marketName == "Tesla" + assert cd.longName == "Tesla Inc." + + def test_createContractDescription(self): + contract_proto = ContractProto(symbol="GOOG", secType="STK") + desc_proto = ContractDescriptionProto(contract=contract_proto) + desc_proto.derivativeSecTypes.extend(["OPT", "FUT"]) + + desc = createContractDescription(desc_proto) + assert isinstance(desc, ContractDescription) + assert desc.contract.symbol == "GOOG" + assert desc.derivativeSecTypes == ["OPT", "FUT"] + + def test_createMatchingSymbolsRequestProto(self): + proto = createMatchingSymbolsRequestProto(1, "IBKR") + assert isinstance(proto, MatchingSymbolsRequestProto) + assert proto.reqId == 1 + assert proto.pattern == "IBKR" + + def test_createMarketRuleRequestProto(self): + proto = createMarketRuleRequestProto(123) + assert isinstance(proto, MarketRuleRequestProto) + assert proto.marketRuleId == 123 + + def test_createContractProto(self): + contract = Contract(symbol="MSFT", secType="STK", exchange="SMART", currency="USD") + proto = createContractProto(contract, None) + assert isinstance(proto, ContractProto) + assert proto.symbol == "MSFT" + assert proto.secType == "STK" + assert proto.exchange == "SMART" + assert proto.currency == "USD" + + def test_createDeltaNeutralContractProto(self): + dn_contract = DeltaNeutralContract(conId=456, delta=0.8, price=20.0) + contract = Contract(deltaNeutralContract=dn_contract) + proto = createDeltaNeutralContractProto(contract) + assert isinstance(proto, DeltaNeutralContractProto) + assert proto.conId == 456 + assert proto.delta == 0.8 + assert proto.price == 20.0 + + def test_createComboLegProtoList(self): + leg1 = ComboLeg(conId=1, ratio=1, action="BUY") + leg2 = ComboLeg(conId=2, ratio=2, action="SELL") + contract = Contract(comboLegs=[leg1, leg2]) + order = Order(orderComboLegs=[Mock(price=10.0), Mock(price=20.0)]) + + proto_list = createComboLegProtoList(contract, order) + assert len(proto_list) == 2 + assert proto_list[0].conId == 1 + assert proto_list[0].ratio == 1 + assert proto_list[0].perLegPrice == 10.0 + assert proto_list[1].conId == 2 + assert proto_list[1].ratio == 2 + assert proto_list[1].perLegPrice == 20.0 + + def test_createComboLegProto(self): + leg = ComboLeg(conId=1, ratio=1, action="BUY", exchange="SMART") + proto = createComboLegProto(leg, 15.0) + assert isinstance(proto, ComboLegProto) + assert proto.conId == 1 + assert proto.ratio == 1 + assert proto.action == "BUY" + assert proto.exchange == "SMART" + assert proto.perLegPrice == 15.0 + + def test_createSmartComponentsRequestProto(self): + proto = createSmartComponentsRequestProto(1, "SMART") + assert isinstance(proto, SmartComponentsRequestProto) + assert proto.reqId == 1 + assert proto.bboExchange == "SMART" + + def test_createSmartComponents(self): + smart_components_proto = SmartComponentsProto() + comp1 = smart_components_proto.smartComponents.add() + comp1.bitNumber = 1 + comp1.exchange = "SMART" + comp1.exchangeLetter = "A" + + comp2 = smart_components_proto.smartComponents.add() + comp2.bitNumber = 2 + comp2.exchange = "NYSE" + comp2.exchangeLetter = "B" + + smart_components = createSmartComponents(smart_components_proto) + assert isinstance(smart_components, list) + assert len(smart_components) == 2 + + assert isinstance(smart_components[0], SmartComponent) + assert smart_components[0].bitNumber == 1 + assert smart_components[0].exchange == "SMART" + assert smart_components[0].exchangeLetter == "A" + + assert isinstance(smart_components[1], SmartComponent) + assert smart_components[1].bitNumber == 2 + assert smart_components[1].exchange == "NYSE" + assert smart_components[1].exchangeLetter == "B" + + def test_createSmartComponents_empty(self): + smart_components_proto = SmartComponentsProto() + smart_components = createSmartComponents(smart_components_proto) + assert isinstance(smart_components, list) + assert len(smart_components) == 0 \ No newline at end of file diff --git a/tests/test_contract_requests.py b/tests/test_contract_requests.py new file mode 100644 index 00000000..664ad372 --- /dev/null +++ b/tests/test_contract_requests.py @@ -0,0 +1,179 @@ + +import asyncio +import pytest +from unittest.mock import Mock, patch + +from ib_async import IB, Contract, ContractDetails, ContractDescription, OptionChain +from ib_async.objects import ConnectionStats +from ib_async.protobuf.Contract_pb2 import Contract as ContractProto +from ib_async.protobuf.ContractDescription_pb2 import ContractDescription as ContractDescriptionProto +from ib_async.protobuf.SymbolSamples_pb2 import SymbolSamples as SymbolSamplesProto +from ib_async.protobuf_converters.contract_converters import createContractDescription, createOptionChain +from ib_async.protobuf.SecDefOptParameter_pb2 import SecDefOptParameter as SecDefOptParameterProto + + +@pytest.mark.asyncio +async def test_reqContractDetailsAsync(): + """ + Test the end-to-end flow of reqContractDetailsAsync, including the eventkit stream handling. + """ + ib = IB() + ib.client.isConnected = Mock(return_value=True) + ib.client.serverVersion = Mock(return_value=201) + ib.client.getReqId = Mock(return_value=1) + ib.client.reqContractDetails = Mock() + ib.client.connectionStats = Mock(return_value=ConnectionStats(0, 0, 0, 0, 0, 0)) + + contract = Contract(symbol="AAPL", secType="STK", exchange="SMART", currency="USD") + + # Call the async method + event = ib.reqContractDetailsAsync(contract) + + async def emitter(): + await asyncio.sleep(0.01) # give consumer time to subscribe + # Simulate the response from TWS + cd1 = ContractDetails(contract=Contract(symbol="AAPL", conId=1)) + cd2 = ContractDetails(contract=Contract(symbol="AAPL", conId=2)) + + ib.wrapper.response_bus.emit(1, cd1) + ib.wrapper.response_bus.emit(1, cd2) + ib.wrapper.response_bus.emit(1, None) + + results, _ = await asyncio.gather( + event, + emitter() + ) + assert len(results) == 2 + assert results[0].contract.conId == 1 + assert results[1].contract.conId == 2 + + # Check that the underlying client method was called + ib.client.reqContractDetails.assert_called_once_with(1, contract) + +@pytest.mark.asyncio +async def test_reqContractDetails_protobuf_not_supported(): + """ + Test that reqContractDetails raises ConnectionError if protobuf is not supported by the server. + """ + ib = IB() + ib.client.isConnected = Mock(return_value=True) + ib.client.serverVersion = Mock(return_value=100) # Simulate old server version + ib.client.getReqId = Mock(return_value=1) + ib.client.reqContractDetails = Mock() + ib.client.connectionStats = Mock(return_value=ConnectionStats(0, 0, 0, 0, 0, 0)) + ib.client.connectAsync = Mock(return_value=asyncio.Future()) + ib.client.connectAsync.return_value.set_result(None) + + contract = Contract(symbol="AAPL", secType="STK", exchange="SMART", currency="USD") + + with pytest.raises(ConnectionError) as exc_info: + await ib.connectAsync("127.0.0.1", 7497, 1) + + assert "Protobuf not supported by server." in str(exc_info.value) + ib.client.reqContractDetails.assert_not_called() + +@pytest.mark.asyncio +async def test_reqMatchingSymbolsAsync(): + """ + Test the end-to-end flow of reqMatchingSymbolsAsync, including eventkit stream handling. + """ + ib = IB() + ib.client.isConnected = Mock(return_value=True) + ib.client.serverVersion = Mock(return_value=201) + ib.client.getReqId = Mock(return_value=1) + ib.client.reqMatchingSymbols = Mock() + ib.client.connectionStats = Mock(return_value=ConnectionStats(0, 0, 0, 0, 0, 0)) + + pattern = "AAPL" + + # Call the async method + event = ib.reqMatchingSymbolsAsync(pattern) + + async def emitter(): + await asyncio.sleep(0.01) # give consumer time to subscribe + # Simulate the response from TWS + cd_proto1 = ContractDescriptionProto(contract=ContractProto(symbol="AAPL", conId=10)) + cd_proto2 = ContractDescriptionProto(contract=ContractProto(symbol="AAPL", conId=20)) + + # The Decoder.symbolSamples method emits a list of ContractDescription objects + contract_descriptions = [ + createContractDescription(cd_proto1), + createContractDescription(cd_proto2) + ] + + ib.wrapper.response_bus.emit(1, contract_descriptions) + + results, _ = await asyncio.gather( + event, + emitter() + ) + + assert len(results) == 2 + assert results[0].contract.conId == 10 + assert results[1].contract.conId == 20 + + # Check that the underlying client method was called + ib.client.reqMatchingSymbols.assert_called_once_with(1, pattern) + + +@pytest.mark.asyncio +async def test_reqSecDefOptParamsAsync(): + """ + Test the end-to-end flow of reqSecDefOptParamsAsync, including eventkit stream handling. + """ + ib = IB() + ib.client.isConnected = Mock(return_value=True) + ib.client.serverVersion = Mock(return_value=201) + ib.client.getReqId = Mock(return_value=1) + ib.client.reqSecDefOptParams = Mock() + ib.client.connectionStats = Mock(return_value=ConnectionStats(0, 0, 0, 0, 0, 0)) + + underlyingSymbol = "SPX" + futFopExchange = "SMART" + underlyingSecType = "IND" + underlyingConId = 123 + + # Call the async method + event = ib.reqSecDefOptParamsAsync(underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId) + + async def emitter(): + await asyncio.sleep(0.1) # give consumer time to subscribe + # Simulate the response from TWS + sec_def_opt_parameter_proto1 = SecDefOptParameterProto( + reqId=1, + exchange="SMART", + underlyingConId=123, + tradingClass="SPX", + multiplier="100", + expirations=["202501", "202502"], + strikes=[1000.0, 1100.0], + ) + sec_def_opt_parameter_proto2 = SecDefOptParameterProto( + reqId=1, + exchange="SMART", + underlyingConId=123, + tradingClass="SPX", + multiplier="100", + expirations=["202503", "202504"], + strikes=[1200.0, 1300.0], + ) + + option_chain1 = createOptionChain(sec_def_opt_parameter_proto1) + option_chain2 = createOptionChain(sec_def_opt_parameter_proto2) + + ib.wrapper.response_bus.emit(1, option_chain1) + ib.wrapper.response_bus.emit(1, option_chain2) + ib.wrapper.response_bus.emit(1, None) + + results, _ = await asyncio.gather( + event, + emitter() + ) + + assert len(results) == 2 + assert results[0].exchange == "SMART" + assert results[0].expirations == ["202501", "202502"] + assert results[1].expirations == ["202503", "202504"] + + # Check that the underlying client method was called + ib.client.reqSecDefOptParams.assert_called_once_with(1, underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId) diff --git a/tests/test_historical_data_converters.py b/tests/test_historical_data_converters.py new file mode 100644 index 00000000..67eb4944 --- /dev/null +++ b/tests/test_historical_data_converters.py @@ -0,0 +1,324 @@ +from datetime import datetime, timezone + +import pytest + +from ib_async.contract import Contract, TagValue +from ib_async.objects import ( + BarData, + HistogramData, + HistoricalSchedule, + HistoricalSession, + HistoricalTick, + HistoricalTickBidAsk, + HistoricalTickLast, + RealTimeBar, + TickAttribBidAsk, + TickAttribLast, +) +from ib_async.protobuf.FundamentalsDataRequest_pb2 import ( + FundamentalsDataRequest as FundamentalsDataRequestProto, +) +from ib_async.protobuf.HeadTimestampRequest_pb2 import ( + HeadTimestampRequest as HeadTimestampRequestProto, +) +from ib_async.protobuf.HistogramDataEntry_pb2 import ( + HistogramDataEntry as HistogramDataEntryProto, +) +from ib_async.protobuf.HistogramDataRequest_pb2 import ( + HistogramDataRequest as HistogramDataRequestProto, +) +from ib_async.protobuf.HistoricalDataBar_pb2 import ( + HistoricalDataBar as HistoricalDataBarProto, +) +from ib_async.protobuf.HistoricalDataRequest_pb2 import ( + HistoricalDataRequest as HistoricalDataRequestProto, +) +from ib_async.protobuf.HistoricalSchedule_pb2 import ( + HistoricalSchedule as HistoricalScheduleProto, +) +from ib_async.protobuf.HistoricalSession_pb2 import ( + HistoricalSession as HistoricalSessionProto, +) +from ib_async.protobuf.HistoricalTick_pb2 import HistoricalTick as HistoricalTickProto +from ib_async.protobuf.HistoricalTickBidAsk_pb2 import ( + HistoricalTickBidAsk as HistoricalTickBidAskProto, +) +from ib_async.protobuf.HistoricalTickLast_pb2 import ( + HistoricalTickLast as HistoricalTickLastProto, +) +from ib_async.protobuf.HistoricalTicksRequest_pb2 import ( + HistoricalTicksRequest as HistoricalTicksRequestProto, +) +from ib_async.protobuf.RealTimeBarsRequest_pb2 import ( + RealTimeBarsRequest as RealTimeBarsRequestProto, +) +from ib_async.protobuf.RealTimeBarTick_pb2 import ( + RealTimeBarTick as RealTimeBarTickProto, +) +from ib_async.protobuf.TickAttribBidAsk_pb2 import ( + TickAttribBidAsk as TickAttribBidAskProto, +) +from ib_async.protobuf.TickAttribLast_pb2 import ( + TickAttribLast as TickAttribLastProto, +) +from ib_async.protobuf.CancelHistoricalData_pb2 import ( + CancelHistoricalData as CancelHistoricalDataProto, +) +from ib_async.protobuf_converters.historical_data_converters import ( + createBarData, + createBarDataList, + createFundamentalsDataRequestProto, + createHeadTimestampRequestProto, + createHistogramDataEntry, + createHistogramDataRequestProto, + createHistoricalDataRequestProto, + createHistoricalSchedule, + createHistoricalTick, + createHistoricalTickBidAsk, + createHistoricalTickLast, + createHistoricalTicksRequestProto, + createRealTimeBarsRequestProto, + createRealTimeBarTick, + fillTagValueList, + createCancelHistoricalDataProto, +) + + +class TestHistoricalDataConverters: + def test_createHeadTimestampRequestProto(self): + contract = Contract(symbol="AAPL", secType="STK") + proto = createHeadTimestampRequestProto(1, contract, "TRADES", True, 1) + assert isinstance(proto, HeadTimestampRequestProto) + assert proto.reqId == 1 + assert proto.contract.symbol == "AAPL" + assert proto.whatToShow == "TRADES" + assert proto.useRTH is True + assert proto.formatDate == 1 + + def test_fillTagValueList(self): + tag_values = [TagValue("tag1", "val1"), TagValue("tag2", "val2")] + proto_map: dict[str, str] = {} + fillTagValueList(tag_values, proto_map) + assert proto_map == {"tag1": "val1", "tag2": "val2"} + + def test_createHistoricalDataRequestProto(self): + contract = Contract(symbol="TSLA", secType="STK") + proto = createHistoricalDataRequestProto( + 2, + contract, + "20250101 12:00:00", + "1 D", + "1 min", + "TRADES", + True, + 1, + False, + [], + ) + assert isinstance(proto, HistoricalDataRequestProto) + assert proto.reqId == 2 + assert proto.contract.symbol == "TSLA" + assert proto.endDateTime == "20250101 12:00:00" + assert proto.duration == "1 D" + assert proto.barSizeSetting == "1 min" + assert proto.whatToShow == "TRADES" + assert proto.useRTH is True + assert proto.formatDate == 1 + assert proto.keepUpToDate is False + + def test_createRealTimeBarsRequestProto(self): + contract = Contract(symbol="GOOG", secType="STK") + proto = createRealTimeBarsRequestProto(3, contract, 5, "TRADES", True, []) + assert isinstance(proto, RealTimeBarsRequestProto) + assert proto.reqId == 3 + assert proto.contract.symbol == "GOOG" + assert proto.barSize == 5 + assert proto.whatToShow == "TRADES" + assert proto.useRTH is True + + def test_createBarData(self): + bar_proto = HistoricalDataBarProto( + date="2025-01-01 10:00:00", + open=100.0, + high=102.0, + low=99.0, + close=101.0, + volume="1000", + WAP="100.5", + barCount=50, + ) + bar = createBarData(bar_proto) + assert isinstance(bar, BarData) + assert bar.date == datetime(2025, 1, 1, 10, 0) + assert bar.open == 100.0 + assert bar.high == 102.0 + assert bar.low == 99.0 + assert bar.close == 101.0 + assert bar.volume == 1000 + assert bar.average == 100.5 + assert bar.barCount == 50 + + def test_createBarDataList(self): + bar_proto1 = HistoricalDataBarProto( + date="2025-01-01 10:00:00", + open=100, + high=102, + low=99, + close=101, + volume="1000", + WAP="100.5", + barCount=50, + ) + bar_proto2 = HistoricalDataBarProto( + date="2025-01-01 10:05:00", + open=101, + high=103, + low=100, + close=102, + volume="1000", + WAP="101.5", + barCount=50, + ) + bars = createBarDataList([bar_proto1, bar_proto2]) + assert len(bars) == 2 + assert bars[0].open == 100 + assert bars[1].open == 101 + + def test_createHistoricalTicksRequestProto(self): + contract = Contract(symbol="MSFT", secType="STK") + proto = createHistoricalTicksRequestProto( + 4, + contract, + "20250101 10:00:00", + "20250101 10:05:00", + 100, + "TRADES", + True, + False, + [], + ) + assert isinstance(proto, HistoricalTicksRequestProto) + assert proto.reqId == 4 + assert proto.contract.symbol == "MSFT" + assert proto.startDateTime == "20250101 10:00:00" + assert proto.endDateTime == "20250101 10:05:00" + assert proto.numberOfTicks == 100 + assert proto.whatToShow == "TRADES" + assert proto.useRTH is True + assert proto.ignoreSize is False + + def test_createHistoricalTick(self): + ts = int(datetime(2025, 1, 1, 10, 0, tzinfo=timezone.utc).timestamp()) + tick_proto = HistoricalTickProto(time=ts, price=150.0, size="10") + tick = createHistoricalTick(tick_proto, timezone.utc) + assert isinstance(tick, HistoricalTick) + assert tick.time == datetime(2025, 1, 1, 10, 0, tzinfo=timezone.utc) + assert tick.price == 150.0 + assert tick.size == 10 + + def test_createHistoricalTickBidAsk(self): + ts = int(datetime(2025, 1, 1, 10, 0, tzinfo=timezone.utc).timestamp()) + attrib_proto = TickAttribBidAskProto(bidPastLow=True, askPastHigh=False) + tick_proto = HistoricalTickBidAskProto( + time=ts, + tickAttribBidAsk=attrib_proto, + priceBid=149.9, + priceAsk=150.1, + sizeBid="5", + sizeAsk="8", + ) + tick = createHistoricalTickBidAsk(tick_proto, timezone.utc) + assert isinstance(tick, HistoricalTickBidAsk) + assert tick.time == datetime(2025, 1, 1, 10, 0, tzinfo=timezone.utc) + assert tick.tickAttribBidAsk.bidPastLow is True + assert tick.priceBid == 149.9 + assert tick.sizeAsk == 8 + + def test_createHistoricalTickLast(self): + ts = int(datetime(2025, 1, 1, 10, 0, tzinfo=timezone.utc).timestamp()) + attrib_proto = TickAttribLastProto(pastLimit=False, unreported=True) + tick_proto = HistoricalTickLastProto( + time=ts, + tickAttribLast=attrib_proto, + price=150.0, + size="12", + exchange="NYSE", + specialConditions="C", + ) + tick = createHistoricalTickLast(tick_proto, timezone.utc) + assert isinstance(tick, HistoricalTickLast) + assert tick.time == datetime(2025, 1, 1, 10, 0, tzinfo=timezone.utc) + assert tick.tickAttribLast.unreported is True + assert tick.price == 150.0 + assert tick.size == 12 + assert tick.exchange == "NYSE" + assert tick.specialConditions == "C" + + def test_createHistogramDataRequestProto(self): + contract = Contract(symbol="AMZN", secType="STK") + proto = createHistogramDataRequestProto(5, contract, True, "1 week") + assert isinstance(proto, HistogramDataRequestProto) + assert proto.reqId == 5 + assert proto.contract.symbol == "AMZN" + assert proto.useRTH is True + assert proto.timePeriod == "1 week" + + def test_createHistogramDataEntry(self): + entry_proto = HistogramDataEntryProto(price=2000.0, size="500") + entry = createHistogramDataEntry(entry_proto) + assert isinstance(entry, HistogramData) + assert entry.price == 2000.0 + assert entry.count == 500 + + def test_createHistoricalSchedule(self): + session_proto = HistoricalSessionProto( + startDateTime="20250101:090000", + endDateTime="20250101:160000", + refDate="20250101", + ) + schedule_proto = HistoricalScheduleProto( + startDateTime="20250101", + endDateTime="20250131", + timeZone="EST", + historicalSessions=[session_proto], + ) + schedule = createHistoricalSchedule(schedule_proto) + assert isinstance(schedule, HistoricalSchedule) + assert schedule.startDateTime == "20250101" + assert schedule.endDateTime == "20250131" + assert schedule.timeZone == "EST" + assert len(schedule.sessions) == 1 + assert schedule.sessions[0].refDate == "20250101" + assert schedule.sessions[0].startDateTime == "20250101:090000" + assert schedule.sessions[0].endDateTime == "20250101:160000" + + def test_createRealTimeBarTick(self): + ts = int(datetime(2025, 1, 1, 10, 0, tzinfo=timezone.utc).timestamp()) + bar_proto = RealTimeBarTickProto( + time=ts, + open=100, + high=102, + low=99, + close=101, + volume="1000", + WAP="100.5", + count=50, + ) + bar = createRealTimeBarTick(bar_proto, timezone.utc) + assert isinstance(bar, RealTimeBar) + assert bar.time == datetime(2025, 1, 1, 10, 0, tzinfo=timezone.utc) + assert bar.open_ == 100 + assert bar.volume == 1000 + + def test_createFundamentalsDataRequestProto(self): + contract = Contract(symbol="IBM", secType="STK") + proto = createFundamentalsDataRequestProto(6, contract, "ReportSnapshot", []) + assert isinstance(proto, FundamentalsDataRequestProto) + assert proto.reqId == 6 + assert proto.contract.symbol == "IBM" + assert proto.reportType == "ReportSnapshot" + + def test_createCancelHistoricalDataProto(self): + proto = createCancelHistoricalDataProto(1) + assert isinstance(proto, CancelHistoricalDataProto) + assert proto.reqId == 1 diff --git a/tests/test_market_data_converters.py b/tests/test_market_data_converters.py new file mode 100644 index 00000000..f3302f58 --- /dev/null +++ b/tests/test_market_data_converters.py @@ -0,0 +1,177 @@ +import pytest +from ib_async.contract import Contract, TagValue +from ib_async.objects import ( + OptionComputation, + TickAttrib, + TickComputationData, + TickGenericData, + TickParams, + TickPriceData, + TickSizeData, + TickStringData, + TickType, +) +from ib_async.protobuf.MarketDataTypeRequest_pb2 import ( + MarketDataTypeRequest as MarketDataTypeRequestProto, +) +from ib_async.protobuf.MarketDataRequest_pb2 import ( + MarketDataRequest as MarketDataRequestProto, +) +from ib_async.protobuf.CancelMarketData_pb2 import CancelMarketData as CancelMarketDataProto +from ib_async.protobuf.TickByTickRequest_pb2 import TickByTickRequest as TickByTickRequestProto +from ib_async.protobuf.TickReqParams_pb2 import TickReqParams as TickReqParamsProto +from ib_async.protobuf.TickPrice_pb2 import TickPrice as TickPriceProto +from ib_async.protobuf.TickSize_pb2 import TickSize as TickSizeProto +from ib_async.protobuf.TickString_pb2 import TickString as TickStringProto +from ib_async.protobuf.TickGeneric_pb2 import TickGeneric as TickGenericProto +from ib_async.protobuf.TickOptionComputation_pb2 import ( + TickOptionComputation as TickOptionComputationProto, +) +from ib_async.protobuf.CalculateImpliedVolatilityRequest_pb2 import ( + CalculateImpliedVolatilityRequest as CalculateImpliedVolatilityRequestProto, +) +from ib_async.protobuf.CalculateOptionPriceRequest_pb2 import ( + CalculateOptionPriceRequest as CalculateOptionPriceRequestProto, +) +from ib_async.protobuf.CancelCalculateImpliedVolatility_pb2 import ( + CancelCalculateImpliedVolatility as CancelCalculateImpliedVolatilityProto, +) +from ib_async.protobuf.CancelCalculateOptionPrice_pb2 import ( + CancelCalculateOptionPrice as CancelCalculateOptionPriceProto, +) +from ib_async.protobuf_converters.market_data_converters import ( + createMarketDataTypeRequestProto, + createMarketDataRequestProto, + cancelMarketDataProto, + createTickByTickRequestProto, + createTickParams, + createTickPriceData, + createTickSizeData, + createTickStringData, + createTickGenericData, + createTickOptionComputation, + createCalculateImpliedVolatilityRequestProto, + createCalculateOptionPriceRequestProto, + createCancelCalculateImpliedVolatilityProto, + createCancelCalculateOptionPriceProto, +) + +class TestMarketDataConverters: + + def test_createMarketDataTypeRequestProto(self): + proto = createMarketDataTypeRequestProto(1) + assert isinstance(proto, MarketDataTypeRequestProto) + assert proto.marketDataType == 1 + + def test_createMarketDataRequestProto(self): + contract = Contract(symbol="SPY", secType="STK") + proto = createMarketDataRequestProto(1, contract, "100,101", True, False, []) + assert isinstance(proto, MarketDataRequestProto) + assert proto.reqId == 1 + assert proto.contract.symbol == "SPY" + assert proto.genericTickList == "100,101" + assert proto.snapshot is True + assert proto.regulatorySnapshot is False + + def test_cancelMarketDataProto(self): + proto = cancelMarketDataProto(1) + assert isinstance(proto, CancelMarketDataProto) + assert proto.reqId == 1 + + def test_createTickByTickRequestProto(self): + contract = Contract(symbol="EURUSD", secType="CASH") + proto = createTickByTickRequestProto(2, contract, "Last", 0, False) + assert isinstance(proto, TickByTickRequestProto) + assert proto.reqId == 2 + assert proto.contract.symbol == "EURUSD" + assert proto.tickType == "Last" + assert proto.numberOfTicks == 0 + assert proto.ignoreSize is False + + def test_createTickParams(self): + proto = TickReqParamsProto(reqId=1, minTick="0.01", bboExchange="ISLAND", snapshotPermissions=3) + params = createTickParams(proto) + assert isinstance(params, TickParams) + assert params.reqId == 1 + assert params.minTick == 0.01 + assert params.bboExchange == "ISLAND" + assert params.snapshotPermissions == 3 + + def test_createTickPriceData(self): + proto = TickPriceProto(reqId=1, tickType=TickType.BID.value, price=1.2, size="100", attrMask=1) + price_data, size_data = createTickPriceData(proto) + + assert isinstance(price_data, TickPriceData) + assert price_data.reqId == 1 + assert price_data.tickType == TickType.BID + assert price_data.price == 1.2 + assert price_data.size == 100 + assert price_data.attribs.canAutoExecute is True + + assert isinstance(size_data, TickSizeData) + assert size_data.tickType == TickType.BID_SIZE + assert size_data.size == 100 + + def test_createTickSizeData(self): + proto = TickSizeProto(reqId=1, tickType=TickType.ASK_SIZE.value, size="200") + size_data = createTickSizeData(proto) + assert isinstance(size_data, TickSizeData) + assert size_data.reqId == 1 + assert size_data.tickType == TickType.ASK_SIZE + assert size_data.size == 200 + + def test_createTickStringData(self): + proto = TickStringProto(reqId=1, tickType=TickType.LAST_TIMESTAMP.value, value="1672531200") + string_data = createTickStringData(proto) + assert isinstance(string_data, TickStringData) + assert string_data.reqId == 1 + assert string_data.tickType == TickType.LAST_TIMESTAMP + assert string_data.value == "1672531200" + + def test_createTickGenericData(self): + proto = TickGenericProto(reqId=1, tickType=TickType.OPTION_IMPLIED_VOL.value, value=0.5) + generic_data = createTickGenericData(proto) + assert isinstance(generic_data, TickGenericData) + assert generic_data.reqId == 1 + assert generic_data.tickType == TickType.OPTION_IMPLIED_VOL + assert generic_data.value == 0.5 + + def test_createTickOptionComputation(self): + proto = TickOptionComputationProto( + reqId=1, tickType=TickType.BID_OPTION_COMPUTATION.value, impliedVol=0.25, delta=0.6 + ) + comp_data = createTickOptionComputation(proto) + assert isinstance(comp_data, TickComputationData) + assert comp_data.reqId == 1 + assert comp_data.tickType == TickType.BID_OPTION_COMPUTATION + assert isinstance(comp_data.computation, OptionComputation) + assert comp_data.computation.impliedVol == 0.25 + assert comp_data.computation.delta == 0.6 + + def test_createCalculateImpliedVolatilityRequestProto(self): + contract = Contract(symbol="AAPL", secType="OPT", right="C", strike=150) + proto = createCalculateImpliedVolatilityRequestProto(1, contract, 2.5, 145.0, []) + assert isinstance(proto, CalculateImpliedVolatilityRequestProto) + assert proto.reqId == 1 + assert proto.contract.symbol == "AAPL" + assert proto.optionPrice == 2.5 + assert proto.underPrice == 145.0 + + def test_createCalculateOptionPriceRequestProto(self): + contract = Contract(symbol="AAPL", secType="OPT", right="P", strike=140) + proto = createCalculateOptionPriceRequestProto(1, contract, 0.3, 145.0, []) + assert isinstance(proto, CalculateOptionPriceRequestProto) + assert proto.reqId == 1 + assert proto.contract.symbol == "AAPL" + assert proto.volatility == 0.3 + assert proto.underPrice == 145.0 + + def test_createCancelCalculateImpliedVolatilityProto(self): + proto = createCancelCalculateImpliedVolatilityProto(1) + assert isinstance(proto, CancelCalculateImpliedVolatilityProto) + assert proto.reqId == 1 + + def test_createCancelCalculateOptionPriceProto(self): + proto = createCancelCalculateOptionPriceProto(1) + assert isinstance(proto, CancelCalculateOptionPriceProto) + assert proto.reqId == 1 diff --git a/tests/test_subscription_converters.py b/tests/test_subscription_converters.py new file mode 100644 index 00000000..28c13d25 --- /dev/null +++ b/tests/test_subscription_converters.py @@ -0,0 +1,169 @@ +import pytest +from ib_async.objects import ScannerSubscription, TagValue, ScanData +from ib_async.contract import ContractDetails +from ib_async.protobuf.ScannerParametersRequest_pb2 import ( + ScannerParametersRequest as ScannerParametersRequestProto, +) +from ib_async.protobuf.ScannerSubscriptionRequest_pb2 import ( + ScannerSubscriptionRequest as ScannerSubscriptionRequestProto, +) +from ib_async.protobuf.ScannerSubscription_pb2 import ( + ScannerSubscription as ScannerSubscriptionProto, +) +from ib_async.protobuf.CancelScannerSubscription_pb2 import ( + CancelScannerSubscription as CancelScannerSubscriptionProto, +) +from ib_async.protobuf.PnLRequest_pb2 import PnLRequest as PnLRequestProto +from ib_async.protobuf.CancelPnL_pb2 import CancelPnL as CancelPnLProto +from ib_async.protobuf.PnLSingleRequest_pb2 import ( + PnLSingleRequest as PnLSingleRequestProto, +) +from ib_async.protobuf.CancelPnLSingle_pb2 import ( + CancelPnLSingle as CancelPnLSingleProto, +) +from ib_async.protobuf.ScannerData_pb2 import ScannerData as ScannerDataProto + +from ib_async.protobuf_converters.subscription_converters import ( + createScannerParametersRequestProto, + createScannerSubscriptionRequestProto, + createScannerSubscriptionProto, + createCancelScannerSubscriptionProto, + createPnLRequestProto, + createCancelPnLProto, + createPnLSingleRequestProto, + createCancelPnLSingleProto, + createScannerDataList, +) + + +class TestSubscriptionConverters: + def test_createScannerParametersRequestProto(self): + proto = createScannerParametersRequestProto() + assert isinstance(proto, ScannerParametersRequestProto) + + def test_createScannerSubscriptionRequestProto(self): + sub = ScannerSubscription(instrument="STK", locationCode="STK.US.MAJOR") + options = [TagValue("tag1", "val1")] + filters = [TagValue("tag2", "val2")] + proto = createScannerSubscriptionRequestProto(1, sub, options, filters) + + assert isinstance(proto, ScannerSubscriptionRequestProto) + assert proto.reqId == 1 + assert proto.scannerSubscription.instrument == "STK" + assert proto.scannerSubscription.locationCode == "STK.US.MAJOR" + assert "tag1" in proto.scannerSubscription.scannerSubscriptionOptions + assert "tag2" in proto.scannerSubscription.scannerSubscriptionFilterOptions + + def test_createScannerSubscriptionProto(self): + sub = ScannerSubscription( + instrument="STK", + locationCode="STK.US", + scanCode="TOP_PERC_GAIN", + abovePrice=10.0, + belowPrice=100.0, + aboveVolume=10000, + marketCapAbove=1e9, + marketCapBelow=1e11, + stockTypeFilter="ALL", + ) + options = [TagValue("opt1", "val1")] + filters = [] + proto = createScannerSubscriptionProto(sub, options, filters) + + assert isinstance(proto, ScannerSubscriptionProto) + assert proto.instrument == "STK" + assert proto.locationCode == "STK.US" + assert proto.scanCode == "TOP_PERC_GAIN" + assert proto.abovePrice == 10.0 + assert proto.belowPrice == 100.0 + assert proto.aboveVolume == 10000 + assert proto.marketCapAbove == 1e9 + assert proto.marketCapBelow == 1e11 + assert proto.stockTypeFilter == "ALL" + assert "opt1" in proto.scannerSubscriptionOptions + assert len(proto.scannerSubscriptionFilterOptions) == 0 + + def test_createScannerSubscriptionProto_none(self): + proto = createScannerSubscriptionProto(None, [], []) + assert proto is None + + def test_createCancelScannerSubscriptionProto(self): + proto = createCancelScannerSubscriptionProto(1) + assert isinstance(proto, CancelScannerSubscriptionProto) + assert proto.reqId == 1 + + def test_createPnLRequestProto(self): + proto = createPnLRequestProto(2, "DU12345", "MyModel") + assert isinstance(proto, PnLRequestProto) + assert proto.reqId == 2 + assert proto.account == "DU12345" + assert proto.modelCode == "MyModel" + + def test_createCancelPnLProto(self): + proto = createCancelPnLProto(2) + assert isinstance(proto, CancelPnLProto) + assert proto.reqId == 2 + + def test_createPnLSingleRequestProto(self): + proto = createPnLSingleRequestProto(3, "DU12345", "MyModel", 12345) + assert isinstance(proto, PnLSingleRequestProto) + assert proto.reqId == 3 + assert proto.account == "DU12345" + assert proto.modelCode == "MyModel" + assert proto.conId == 12345 + + def test_createCancelPnLSingleProto(self): + proto = createCancelPnLSingleProto(3) + assert isinstance(proto, CancelPnLSingleProto) + assert proto.reqId == 3 + + def test_createScannerDataList(self): + scanner_data_proto = ScannerDataProto() + + # Element 1 + elem1 = scanner_data_proto.scannerDataElement.add() + elem1.rank = 1 + elem1.contract.symbol = "AAPL" + elem1.contract.secType = "STK" + elem1.marketName = "NASDAQ" + elem1.distance = "dist1" + elem1.benchmark = "bench1" + elem1.projection = "proj1" + elem1.comboKey = "key1" + + # Element 2 + elem2 = scanner_data_proto.scannerDataElement.add() + elem2.rank = 2 + elem2.contract.symbol = "GOOG" + elem2.contract.secType = "STK" + elem2.marketName = "NASDAQ" + + data_list = createScannerDataList(scanner_data_proto) + + assert isinstance(data_list, list) + assert len(data_list) == 2 + + # Check item 1 + item1 = data_list[0] + assert isinstance(item1, ScanData) + assert item1.rank == 1 + assert isinstance(item1.contractDetails, ContractDetails) + assert item1.contractDetails.contract.symbol == "AAPL" + assert item1.contractDetails.marketName == "NASDAQ" + assert item1.distance == "dist1" + assert item1.benchmark == "bench1" + assert item1.projection == "proj1" + assert item1.legsStr == "key1" + + # Check item 2 + item2 = data_list[1] + assert isinstance(item2, ScanData) + assert item2.rank == 2 + assert item2.contractDetails.contract.symbol == "GOOG" + assert item2.distance == "" + + def test_createScannerDataList_empty(self): + scanner_data_proto = ScannerDataProto() + data_list = createScannerDataList(scanner_data_proto) + assert isinstance(data_list, list) + assert len(data_list) == 0 diff --git a/tests/test_trade_converter.py b/tests/test_trade_converter.py new file mode 100644 index 00000000..b654e460 --- /dev/null +++ b/tests/test_trade_converter.py @@ -0,0 +1,911 @@ +import pytest +from datetime import datetime +from decimal import Decimal +from unittest.mock import Mock + +from ib_async.contract import Contract, ComboLeg, DeltaNeutralContract +from ib_async.objects import ( + TagValue, + OptionExerciseType, + SoftDollarTier, + Execution, + Fill, + CommissionReport, + ExecutionFilter, +) +from ib_async.order import ( + Order, + OrderComboLeg, + OrderState, + OrderStatus, + Trade, + PriceCondition, + TimeCondition, + MarginCondition, + ExecutionCondition, + VolumeCondition, + PercentChangeCondition, + OrderCancel, + OrderAllocation, +) +from ib_async.protobuf.Order_pb2 import Order as OrderProto +from ib_async.protobuf.PlaceOrderRequest_pb2 import ( + PlaceOrderRequest as PlaceOrderRequestProto, +) +from ib_async.protobuf.Contract_pb2 import Contract as ContractProto +from ib_async.protobuf.DeltaNeutralContract_pb2 import ( + DeltaNeutralContract as DeltaNeutralContractProto, +) +from ib_async.protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto +from ib_async.protobuf.ComboLeg_pb2 import ComboLeg as ComboLegProto +from ib_async.protobuf.OrderCondition_pb2 import OrderCondition as OrderConditionProto +from ib_async.protobuf.OrderState_pb2 import OrderState as OrderStateProto +from ib_async.protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto +from ib_async.protobuf.Execution_pb2 import Execution as ExecutionProto +from ib_async.protobuf.ExecutionDetails_pb2 import ( + ExecutionDetails as ExecutionDetailsProto, +) +from ib_async.protobuf.CommissionAndFeesReport_pb2 import ( + CommissionAndFeesReport as CommissionReportProto, +) +from ib_async.protobuf.ExecutionFilter_pb2 import ( + ExecutionFilter as ExecutionFilterProto, +) +from ib_async.protobuf.ExecutionRequest_pb2 import ( + ExecutionRequest as ExecutionRequestProto, +) +from ib_async.protobuf.OrderCancel_pb2 import OrderCancel as OrderCancelProto +from ib_async.protobuf.GlobalCancelRequest_pb2 import ( + GlobalCancelRequest as GlobalCancelRequestProto, +) +from ib_async.protobuf.CancelOrderRequest_pb2 import ( + CancelOrderRequest as CancelOrderRequestProto, +) +from ib_async.protobuf.ExerciseOptionsRequest_pb2 import ( + ExerciseOptionsRequest as ExerciseOptionsRequestProto, +) +from ib_async.protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto +from ib_async.protobuf.OrderAllocation_pb2 import ( + OrderAllocation as OrderAllocationProtoProto, +) + + +from ib_async.protobuf_converters.trade_converter import ( + createPlaceOrderRequestProto, + createOrderProto, + createOrder, + createSoftDollarTierProto, + createTagValueList, + createOrderState, + createOrderAllocations, + createContractFromExecutionDetails, + createOrderStatus, + createExecution, + createFill, + createTradeFromOpenOrder, + createCommissionReport, + createExecutionRequestProto, + createOrderCancelProto, + createGlobalCancelRequestProto, + createCancelOrderRequestProto, + createExerciseOptionsRequestProto, + createConditionsProto, # Also need to import these helper functions + createOrderComboLegs, + createOrderConditionProto, + createOperatorConditionProto, + createContractConditionProto, + createPriceConditionProto, + createTimeConditionProto, + createMarginConditionProto, + createExecutionConditionProto, + createVolumeConditionProto, + createPercentChangeConditionProto, + createOrderConditions, + setConditionFields, + setOperatorConditionFields, + setContractConditionFields, + createPriceCondition, + createTimeCondition, + createMarginCondition, + createExecutionCondition, + createVolumeCondition, + createPercentChangeCondition, + createSoftDollarTierFromOrder, + createSoftDollarTier, +) + + +class TestTradeConverters: + def test_createPlaceOrderRequestProto(self): + contract = Contract(conId=123, symbol="SPY", secType="STK", exchange="SMART") + order = Order( + orderId=1, action="BUY", totalQuantity=100, orderType="LMT", lmtPrice=400.0 + ) + + proto = createPlaceOrderRequestProto(order.orderId, contract, order) + + assert isinstance(proto, PlaceOrderRequestProto) + assert proto.orderId == 1 + assert proto.contract.conId == 123 + assert proto.order.action == "BUY" + assert float(proto.order.totalQuantity) == 100 + assert proto.order.orderType == "LMT" + assert proto.order.lmtPrice == 400.0 + + def test_createOrderProto_simple_order(self): + order = Order( + action="SELL", + totalQuantity=50, + orderType="MKT", + tif="DAY", + account="DU12345", + outsideRth=True, + transmit=True, + ) + proto = createOrderProto(order) + assert isinstance(proto, OrderProto) + assert proto.action == "SELL" + assert proto.totalQuantity == "50" + assert proto.orderType == "MKT" + assert proto.tif == "DAY" + assert proto.account == "DU12345" + assert proto.outsideRth is True + assert proto.transmit is True + + def test_createOrderProto_with_conditions(self): + order = Order(action="BUY", totalQuantity=10, orderType="LMT", lmtPrice=100) + + price_cond = PriceCondition( + conId=123, + exch="SMART", + isMore=True, + price=101.0, + triggerMethod=1, + conjunction="a", + ) + time_cond = TimeCondition( + time="20251231 16:00:00", isMore=False, conjunction="a" + ) + order.conditions = [price_cond, time_cond] + + proto = createOrderProto(order) + + assert isinstance(proto, OrderProto) + assert len(proto.conditions) == 2 + + # Check price condition + price_cond_proto = proto.conditions[0] + assert price_cond_proto.type == PriceCondition.condType + assert price_cond_proto.isConjunctionConnection is True + assert price_cond_proto.isMore is True + assert price_cond_proto.conId == 123 + assert price_cond_proto.exchange == "SMART" + assert price_cond_proto.price == 101.0 + assert price_cond_proto.triggerMethod == 1 + + # Check time condition + time_cond_proto = proto.conditions[1] + assert time_cond_proto.type == TimeCondition.condType + assert time_cond_proto.isConjunctionConnection is True + assert time_cond_proto.isMore is False + assert time_cond_proto.time == "20251231 16:00:00" + + def test_createOrderProto_with_combo_legs(self): + leg1 = ComboLeg(conId=1, ratio=1, action="BUY", exchange="SMART") + leg2 = ComboLeg(conId=2, ratio=2, action="SELL", exchange="SMART") + contract = Contract(comboLegs=[leg1, leg2]) + order_combo_leg1 = OrderComboLeg(price=10.0) + order_combo_leg2 = OrderComboLeg(price=20.0) + order = Order( + action="BUY", + totalQuantity=1, + orderType="LMT", + lmtPrice=30.0, + orderComboLegs=[order_combo_leg1, order_combo_leg2], + ) + + # Mock createContractProto to return a ContractProto with comboLegs + # This is needed because createOrderProto internally calls createContractProto + # which needs the contract object to build its comboLegs + contract_proto_mock = ContractProto( + comboLegs=[ + ComboLegProto( + conId=1, ratio=1, action="BUY", exchange="SMART", perLegPrice=10.0 + ), + ComboLegProto( + conId=2, ratio=2, action="SELL", exchange="SMART", perLegPrice=20.0 + ), + ] + ) + with pytest.MonkeyPatch().context() as mp: + mp.setattr( + "ib_async.protobuf_converters.trade_converter.createContractProto", + lambda c, o: contract_proto_mock, + ) + place_order_proto = createPlaceOrderRequestProto(1, contract, order) + order_proto = place_order_proto.order + + assert isinstance(order_proto, OrderProto) + # The comboLegs are part of the ContractProto inside PlaceOrderRequestProto, not directly in OrderProto + # So we check if the internal mocked contractProto's comboLegs are correctly set + assert len(place_order_proto.contract.comboLegs) == 2 + assert place_order_proto.contract.comboLegs[0].perLegPrice == 10.0 + assert place_order_proto.contract.comboLegs[1].perLegPrice == 20.0 + + def test_createConditionsProto_price_condition(self): + order = Order() + order.conditions = [ + PriceCondition( + conId=1, + exch="SMART", + isMore=True, + price=100.0, + triggerMethod=1, + conjunction="a", + ) + ] + conditions_proto_list = createConditionsProto(order) + assert len(conditions_proto_list) == 1 + price_cond_proto = conditions_proto_list[0] + assert price_cond_proto.type == PriceCondition.condType + assert price_cond_proto.isConjunctionConnection is True + assert price_cond_proto.isMore is True + assert price_cond_proto.conId == 1 + assert price_cond_proto.exchange == "SMART" + assert price_cond_proto.price == 100.0 + assert price_cond_proto.triggerMethod == 1 + + def test_createConditionsProto_time_condition(self): + order = Order() + order.conditions = [ + TimeCondition(time="20250101 10:00:00", isMore=False, conjunction="o") + ] + conditions_proto_list = createConditionsProto(order) + assert len(conditions_proto_list) == 1 + time_cond_proto = conditions_proto_list[0] + assert time_cond_proto.type == TimeCondition.condType + assert time_cond_proto.isConjunctionConnection is False + assert time_cond_proto.isMore is False + assert time_cond_proto.time == "20250101 10:00:00" + + def test_createConditionsProto_margin_condition(self): + order = Order() + order.conditions = [MarginCondition(percent=150, isMore=True, conjunction="a")] + conditions_proto_list = createConditionsProto(order) + assert len(conditions_proto_list) == 1 + margin_cond_proto = conditions_proto_list[0] + assert margin_cond_proto.type == MarginCondition.condType + assert margin_cond_proto.isConjunctionConnection is True + assert margin_cond_proto.isMore is True + assert margin_cond_proto.percent == 150 + + def test_createConditionsProto_execution_condition(self): + order = Order() + order.conditions = [ + ExecutionCondition( + secType="STK", exch="SMART", symbol="AAPL", conjunction="o" + ) + ] + conditions_proto_list = createConditionsProto(order) + assert len(conditions_proto_list) == 1 + exec_cond_proto = conditions_proto_list[0] + assert exec_cond_proto.type == ExecutionCondition.condType + assert exec_cond_proto.isConjunctionConnection is False + assert exec_cond_proto.secType == "STK" + assert exec_cond_proto.exchange == "SMART" + assert exec_cond_proto.symbol == "AAPL" + + def test_createConditionsProto_volume_condition(self): + order = Order() + order.conditions = [ + VolumeCondition( + conId=2, exch="NASDAQ", volume=1000, isMore=True, conjunction="a" + ) + ] + conditions_proto_list = createConditionsProto(order) + assert len(conditions_proto_list) == 1 + volume_cond_proto = conditions_proto_list[0] + assert volume_cond_proto.type == VolumeCondition.condType + assert volume_cond_proto.isConjunctionConnection is True + assert volume_cond_proto.isMore is True + assert volume_cond_proto.conId == 2 + assert volume_cond_proto.exchange == "NASDAQ" + assert volume_cond_proto.volume == 1000 + + def test_createConditionsProto_percent_change_condition(self): + order = Order() + order.conditions = [ + PercentChangeCondition( + conId=3, + exch="IDEALPRO", + changePercent=5.0, + isMore=False, + conjunction="o", + ) + ] + conditions_proto_list = createConditionsProto(order) + assert len(conditions_proto_list) == 1 + percent_change_cond_proto = conditions_proto_list[0] + assert percent_change_cond_proto.type == PercentChangeCondition.condType + assert percent_change_cond_proto.isConjunctionConnection is False + assert percent_change_cond_proto.isMore is False + assert percent_change_cond_proto.conId == 3 + assert percent_change_cond_proto.exchange == "IDEALPRO" + assert percent_change_cond_proto.changePercent == 5.0 + + def test_createConditionsProto_empty_conditions(self): + order = Order() + order.conditions = [] + conditions_proto_list = createConditionsProto(order) + assert len(conditions_proto_list) == 0 + + def test_createOrderConditionProto(self): + cond = PriceCondition() + cond.conjunction = "a" + proto = createOrderConditionProto(cond) + assert proto.isConjunctionConnection is True + cond.conjunction = "o" + proto = createOrderConditionProto(cond) + assert proto.isConjunctionConnection is False + + def test_createOperatorConditionProto(self): + cond = PriceCondition() + cond.isMore = True + proto = createOperatorConditionProto(cond) + assert proto.isMore is True + + def test_createContractConditionProto(self): + cond = PriceCondition() + cond.conId = 123 + cond.exch = "SMART" + proto = createContractConditionProto(cond) + assert proto.conId == 123 + assert proto.exchange == "SMART" + + def test_createPriceConditionProto(self): + cond = PriceCondition() + cond.price = 100.0 + cond.triggerMethod = 1 + proto = createPriceConditionProto(cond) + assert proto.price == 100.0 + assert proto.triggerMethod == 1 + + def test_createTimeConditionProto(self): + cond = TimeCondition() + cond.time = "20250101 10:00:00" + proto = createTimeConditionProto(cond) + assert proto.time == "20250101 10:00:00" + + def test_createMarginConditionProto(self): + cond = MarginCondition() + cond.percent = 150 + proto = createMarginConditionProto(cond) + assert proto.percent == 150 + + def test_createExecutionConditionProto(self): + cond = ExecutionCondition() + cond.secType = "STK" + cond.exch = "SMART" + cond.symbol = "AAPL" + proto = createExecutionConditionProto(cond) + assert proto.secType == "STK" + assert proto.exchange == "SMART" + assert proto.symbol == "AAPL" + + def test_createVolumeConditionProto(self): + cond = VolumeCondition() + cond.volume = 1000 + proto = createVolumeConditionProto(cond) + assert proto.volume == 1000 + + def test_createPercentChangeConditionProto(self): + cond = PercentChangeCondition() + cond.changePercent = 5.0 + proto = createPercentChangeConditionProto(cond) + assert proto.changePercent == 5.0 + + def test_createOrder(self): + order_proto = OrderProto( + orderId=1, + action="BUY", + totalQuantity="100.0", + orderType="LMT", + lmtPrice=400.0, + tif="DAY", + account="U123456", + permId=1001, + clientId=10, + parentId=0, + outsideRth=True, + hidden=False, + discretionaryAmt=0.0, + goodAfterTime="20250101 10:00:00", + faGroup="Group1", + faMethod="EqualQuantity", + faPercentage="10", + modelCode="ModelA", + goodTillDate="20250101", + rule80A="Individual", + percentOffset=0.01, + settlingFirm="FirmX", + shortSaleSlot=1, + designatedLocation="US", + exemptCode=-1, + startingPrice=0.0, + stockRefPrice=0.0, + delta=0.0, + stockRangeLower=0.0, + stockRangeUpper=0.0, + displaySize=10, + blockOrder=False, + sweepToFill=True, + allOrNone=False, + minQty=1, + ocaType=0, + triggerMethod=0, + volatility=0.0, + volatilityType=0, + deltaNeutralOrderType="", + deltaNeutralAuxPrice=0.0, + deltaNeutralConId=0, + deltaNeutralSettlingFirm="", + deltaNeutralClearingAccount="", + deltaNeutralClearingIntent="", + deltaNeutralOpenClose="", + deltaNeutralShortSale=False, + deltaNeutralShortSaleSlot=0, + deltaNeutralDesignatedLocation="", + continuousUpdate=False, + referencePriceType=0, + trailStopPrice=0.0, + trailingPercent=0.0, + optOutSmartRouting=False, + clearingAccount="ClearAcc", + clearingIntent="IB", + notHeld=False, + algoStrategy="Vwap", + solicited=False, + whatIf=False, + randomizeSize=False, + randomizePrice=False, + referenceContractId=0, + isPeggedChangeAmountDecrease=False, + peggedChangeAmount=0.0, + referenceChangeAmount=0.0, + referenceExchangeId="", + conditionsIgnoreRth=False, + conditionsCancelOrder=False, + adjustedOrderType="", + triggerPrice=0.0, + lmtPriceOffset=0.0, + adjustedStopPrice=0.0, + adjustedStopLimitPrice=0.0, + adjustedTrailingAmount=0.0, + adjustableTrailingUnit=0, + cashQty=0.0, + dontUseAutoPriceForHedge=False, + isOmsContainer=False, + discretionaryUpToLimitPrice=False, + usePriceMgmtAlgo=0, + duration=0, + postToAts=0, + autoCancelParent=False, + minTradeQty=0, + minCompeteSize=0, + competeAgainstBestOffset=0.0, + midOffsetAtWhole=0.0, + midOffsetAtHalf=0.0, + customerAccount="", + professionalCustomer=False, + bondAccruedInterest="", + includeOvernight=False, + extOperator="", + manualOrderIndicator=0, + submitter="", + imbalanceOnly=False, + autoCancelDate="", + filledQuantity="0.0", + refFuturesConId=0, + shareholder="", + routeMarketableToBbo=False, + parentPermId=0, + ) + contract_proto = ContractProto(conId=123, symbol="SPY") + + order = createOrder(order_proto.orderId, contract_proto, order_proto) + + assert isinstance(order, Order) + assert order.orderId == 1 + assert order.action == "BUY" + assert order.totalQuantity == 100.0 + assert order.orderType == "LMT" + assert order.lmtPrice == 400.0 + assert order.tif == "DAY" + assert order.account == "U123456" + assert order.permId == 1001 + assert order.clientId == 10 + assert order.goodAfterTime == "20250101 10:00:00" + assert order.modelCode == "ModelA" + assert order.rule80A == "Individual" + assert order.displaySize == 10 + assert order.algoStrategy == "Vwap" + assert order.clearingAccount == "ClearAcc" + assert order.clearingIntent == "IB" + assert order.minTradeQty == 0 + + def test_createOrder_with_soft_dollar_tier(self): + order_proto = OrderProto( + orderId=1, + action="BUY", + totalQuantity="100.0", + orderType="LMT", + lmtPrice=400.0, + ) + sd_tier_proto = SoftDollarTierProto( + name="Tier1", value="Value1", displayName="Display1" + ) + order_proto.softDollarTier.CopyFrom(sd_tier_proto) + contract_proto = ContractProto(conId=123, symbol="SPY") + + order = createOrder(order_proto.orderId, contract_proto, order_proto) + assert isinstance(order, Order) + assert order.softDollarTier.name == "Tier1" + assert order.softDollarTier.val == "Value1" + assert order.softDollarTier.displayName == "Display1" + + def test_createOrderComboLegs(self): + contract_proto = ContractProto() + leg1_proto = contract_proto.comboLegs.add() + leg1_proto.conId = 1 + leg1_proto.perLegPrice = 10.0 + leg2_proto = contract_proto.comboLegs.add() + leg2_proto.conId = 2 + leg2_proto.perLegPrice = 20.0 + + order_combo_legs = createOrderComboLegs(contract_proto) + assert len(order_combo_legs) == 2 + assert order_combo_legs[0].price == 10.0 + assert order_combo_legs[1].price == 20.0 + + def test_createOrderConditions(self): + order_proto = OrderProto() + price_cond_proto = order_proto.conditions.add() + price_cond_proto.type = PriceCondition.condType + price_cond_proto.isConjunctionConnection = True + price_cond_proto.isMore = True + price_cond_proto.conId = 123 + price_cond_proto.exchange = "SMART" + price_cond_proto.price = 101.0 + price_cond_proto.triggerMethod = 1 + + conditions = createOrderConditions(order_proto) + assert len(conditions) == 1 + price_condition = conditions[0] + assert isinstance(price_condition, PriceCondition) + assert price_condition.conjunction == "a" + assert price_condition.isMore is True + assert price_condition.conId == 123 + assert price_condition.exch == "SMART" + assert price_condition.price == 101.0 + assert price_condition.triggerMethod == 1 + + def test_setConditionFields(self): + cond_proto = OrderConditionProto() + cond_proto.isConjunctionConnection = True + order_cond = PriceCondition() + setConditionFields(cond_proto, order_cond) + assert order_cond.conjunction == "a" + + def test_setOperatorConditionFields(self): + cond_proto = OrderConditionProto() + cond_proto.isConjunctionConnection = False + cond_proto.isMore = True + op_cond = PriceCondition() + setOperatorConditionFields(cond_proto, op_cond) + assert op_cond.conjunction == "o" + assert op_cond.isMore is True + + def test_setContractConditionFields(self): + cond_proto = OrderConditionProto() + cond_proto.conId = 456 + cond_proto.exchange = "GLOBEX" + contract_cond = PriceCondition() + setContractConditionFields(cond_proto, contract_cond) + assert contract_cond.conId == 456 + assert contract_cond.exch == "GLOBEX" + + def test_createPriceCondition(self): + cond_proto = OrderConditionProto(price=200.0, triggerMethod=2) + price_cond = createPriceCondition(cond_proto) + assert isinstance(price_cond, PriceCondition) + assert price_cond.price == 200.0 + assert price_cond.triggerMethod == 2 + + def test_createTimeCondition(self): + cond_proto = OrderConditionProto(time="20250101 15:00:00") + time_cond = createTimeCondition(cond_proto) + assert isinstance(time_cond, TimeCondition) + assert time_cond.time == "20250101 15:00:00" + + def test_createMarginCondition(self): + cond_proto = OrderConditionProto(percent=180) + margin_cond = createMarginCondition(cond_proto) + assert isinstance(margin_cond, MarginCondition) + assert margin_cond.percent == 180 + + def test_createExecutionCondition(self): + cond_proto = OrderConditionProto(secType="FUT", exchange="GLOBEX", symbol="ES") + exec_cond = createExecutionCondition(cond_proto) + assert isinstance(exec_cond, ExecutionCondition) + assert exec_cond.secType == "FUT" + assert exec_cond.exch == "GLOBEX" + assert exec_cond.symbol == "ES" + + def test_createVolumeCondition(self): + cond_proto = OrderConditionProto(volume=500) + volume_cond = createVolumeCondition(cond_proto) + assert isinstance(volume_cond, VolumeCondition) + assert volume_cond.volume == 500 + + def test_createPercentChangeCondition(self): + cond_proto = OrderConditionProto(changePercent=10.0) + percent_change_cond = createPercentChangeCondition(cond_proto) + assert isinstance(percent_change_cond, PercentChangeCondition) + assert percent_change_cond.changePercent == 10.0 + + def test_createSoftDollarTier(self): + sd_tier_proto = SoftDollarTierProto( + name="Tier2", value="Value2", displayName="Display2" + ) + sd_tier = createSoftDollarTier(sd_tier_proto) + assert isinstance(sd_tier, SoftDollarTier) + assert sd_tier.name == "Tier2" + assert sd_tier.val == "Value2" + assert sd_tier.displayName == "Display2" + + def test_createSoftDollarTier_empty(self): + sd_tier_proto = SoftDollarTierProto() + sd_tier = createSoftDollarTier(sd_tier_proto) + assert isinstance(sd_tier, SoftDollarTier) + assert sd_tier.name == "" + assert sd_tier.val == "" + assert sd_tier.displayName == "" + + def test_createSoftDollarTierFromOrder(self): + order_proto = OrderProto() + sd_tier_proto = SoftDollarTierProto( + name="Tier3", value="Value3", displayName="Display3" + ) + order_proto.softDollarTier.CopyFrom(sd_tier_proto) + + sd_tier = createSoftDollarTierFromOrder(order_proto) + assert isinstance(sd_tier, SoftDollarTier) + assert sd_tier.name == "Tier3" + assert sd_tier.val == "Value3" + assert sd_tier.displayName == "Display3" + + def test_createSoftDollarTierFromOrder_no_tier(self): + order_proto = OrderProto() + sd_tier = createSoftDollarTierFromOrder(order_proto) + assert sd_tier is None + + def test_createTagValueList(self): + proto_map = {"Tag1": "Value1", "Tag2": "Value2"} + tag_value_list = createTagValueList(proto_map) + assert len(tag_value_list) == 2 + assert tag_value_list[0].tag == "Tag1" + assert tag_value_list[0].value == "Value1" + assert tag_value_list[1].tag == "Tag2" + assert tag_value_list[1].value == "Value2" + + def test_createTagValueList_empty(self): + proto_map = {} # type: ignore + tag_value_list = createTagValueList(proto_map) + assert len(tag_value_list) == 0 + + def test_createOrderState(self): + order_state_proto = OrderStateProto( + status="Filled", + initMarginBefore=1000.0, + maintMarginBefore=500.0, + equityWithLoanBefore=1500.0, + initMarginChange=100.0, + maintMarginChange=50.0, + equityWithLoanChange=150.0, + initMarginAfter=1100.0, + maintMarginAfter=550.0, + equityWithLoanAfter=1650.0, + commissionAndFees=10.0, + minCommissionAndFees=5.0, + maxCommissionAndFees=15.0, + commissionAndFeesCurrency="USD", + warningText="", + marginCurrency="USD", + initMarginBeforeOutsideRTH=1000.0, + maintMarginBeforeOutsideRTH=500.0, + equityWithLoanBeforeOutsideRTH=1500.0, + initMarginChangeOutsideRTH=100.0, + maintMarginChangeOutsideRTH=50.0, + equityWithLoanChangeOutsideRTH=150.0, + initMarginAfterOutsideRTH=1100.0, + maintMarginAfterOutsideRTH=550.0, + equityWithLoanAfterOutsideRTH=1650.0, + suggestedSize="100.0", + rejectReason="", + completedTime="20250101 10:00:00", + completedStatus="Completed", + ) + + order_state = createOrderState(order_state_proto) + assert isinstance(order_state, OrderState) + assert order_state.status == "Filled" + assert order_state.initMarginBefore == "1000.0" + assert order_state.commission == 10.0 + assert order_state.completedTime == "20250101 10:00:00" + + def test_createOrderAllocations(self): + order_state_proto = OrderStateProto() + alloc1_proto = order_state_proto.orderAllocations.add() + alloc1_proto.account = "U1" + alloc1_proto.position = "10.0" + alloc1_proto.desiredAllocQty = "5" + + allocations = createOrderAllocations(order_state_proto) + assert len(allocations) == 1 + assert allocations[0].account == "U1" + assert allocations[0].position == Decimal("10.0") + + def test_createContractFromExecutionDetails(self): + exec_details_proto = ExecutionDetailsProto() + exec_details_proto.contract.symbol = "AAPL" + exec_details_proto.contract.secType = "STK" + + contract = createContractFromExecutionDetails(exec_details_proto) + assert isinstance(contract, Contract) + assert contract.symbol == "AAPL" + + def test_createOrderStatus(self): + order_status_proto = OrderStatusProto( + orderId=1, + status="Submitted", + filled="50.0", + remaining="50.0", + avgFillPrice=150.0, + permId=1001, + parentId=0, + lastFillPrice=150.5, + clientId=10, + whyHeld="", + mktCapPrice=0.0, + ) + order_status = createOrderStatus(order_status_proto) + assert isinstance(order_status, OrderStatus) + assert order_status.orderId == 1 + assert order_status.status == "Submitted" + assert order_status.filled == Decimal("50.0") + + def test_createExecution(self): + exec_proto = ExecutionProto( + execId="0001", + time="20250101 10:00:00", + acctNumber="U123456", + exchange="SMART", + side="BOT", + shares="100.0", + price=150.0, + permId=1001, + clientId=10, + orderId=1, + isLiquidation=False, + cumQty="100.0", + avgPrice=150.0, + orderRef="", + evRule="", + evMultiplier=0.0, + modelCode="", + lastLiquidity=1, + isPriceRevisionPending=False, + submitter="", + optExerciseOrLapseType=OptionExerciseType.NoneItem.value[0], + ) + execution = createExecution(exec_proto) + assert isinstance(execution, Execution) + assert execution.execId == "0001" + assert execution.time == datetime(2025, 1, 1, 10, 0, 0) + assert execution.shares == 100.0 + assert execution.optExerciseOrLapseType == OptionExerciseType.NoneItem + + def test_createFill(self): + exec_details_proto = ExecutionDetailsProto() + exec_details_proto.contract.symbol = "AAPL" + exec_details_proto.execution.execId = "0002" + exec_details_proto.execution.time = "20250101 11:00:00" + + fill = createFill(exec_details_proto) + assert isinstance(fill, Fill) + assert fill.contract.symbol == "AAPL" + assert fill.execution.execId == "0002" + assert isinstance(fill.commissionReport, CommissionReport) + + def test_createTradeFromOpenOrder(self): + open_order_proto = OpenOrderProto() + open_order_proto.contract.symbol = "GOOG" + open_order_proto.order.orderId = 2 + open_order_proto.order.action = "SELL" + open_order_proto.orderState.status = "Filled" + + trade, order_state = createTradeFromOpenOrder(open_order_proto) + assert isinstance(trade, Trade) + assert trade.contract.symbol == "GOOG" + assert trade.order.orderId == 2 + assert isinstance(order_state, OrderState) + assert order_state.status == "Filled" + assert trade.orderStatus.status == "Filled" + + def test_createTradeFromOpenOrder_missing_contract(self): + open_order_proto = OpenOrderProto() + # open_order_proto.contract is missing + result = createTradeFromOpenOrder(open_order_proto) + assert result is None + + def test_createCommissionReport(self): + commission_report_proto = CommissionReportProto( + execId="0003", + commissionAndFees=1.5, + currency="USD", + realizedPNL=10.0, + bondYield=0.05, + yieldRedemptionDate="20250101", + ) + commission_report = createCommissionReport(commission_report_proto) + assert isinstance(commission_report, CommissionReport) + assert commission_report.execId == "0003" + assert commission_report.commission == 1.5 + assert commission_report.currency == "USD" + + def test_createExecutionRequestProto(self): + exec_filter = ExecutionFilter(clientId=1, acctCode="U123", symbol="MSFT") + proto = createExecutionRequestProto(1, exec_filter) + assert isinstance(proto, ExecutionRequestProto) + assert proto.reqId == 1 + assert proto.executionFilter.clientId == 1 + assert proto.executionFilter.symbol == "MSFT" + + def test_createOrderCancelProto(self): + order_cancel = OrderCancel( + manualOrderCancelTime="20250101-12:00:00", extOperator="OP1" + ) + proto = createOrderCancelProto(order_cancel) + assert isinstance(proto, OrderCancelProto) + assert proto.manualOrderCancelTime == "20250101-12:00:00" + assert proto.extOperator == "OP1" + + def test_createOrderCancelProto_none(self): + proto = createOrderCancelProto(None) # type: ignore + assert proto is None + + def test_createGlobalCancelRequestProto(self): + order_cancel = OrderCancel(manualOrderCancelTime="20250101-12:00:00") + proto = createGlobalCancelRequestProto(order_cancel) + assert isinstance(proto, GlobalCancelRequestProto) + assert proto.orderCancel.manualOrderCancelTime == "20250101-12:00:00" + + def test_createCancelOrderRequestProto(self): + order_cancel = OrderCancel(manualOrderCancelTime="20250101-12:00:00") + proto = createCancelOrderRequestProto(5, order_cancel) + assert isinstance(proto, CancelOrderRequestProto) + assert proto.orderId == 5 + assert proto.orderCancel.manualOrderCancelTime == "20250101-12:00:00" + + def test_createExerciseOptionsRequestProto(self): + contract = Contract(conId=456, symbol="SPY", secType="OPT") + proto = createExerciseOptionsRequestProto( + 6, contract, 1, 100, "U123", True, "20250101", "CUST1", False + ) + assert isinstance(proto, ExerciseOptionsRequestProto) + assert proto.orderId == 6 + assert proto.contract.conId == 456 + assert proto.exerciseAction == 1 + assert proto.exerciseQuantity == 100 + assert proto.account == "U123" + assert proto.override is True From 64e22f00c59f5f4c5504676dbbb707d6eaedf3e5 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:16:58 +0100 Subject: [PATCH 06/48] ruff format and checks --- ib_async/client.py | 32 ++++-------- ib_async/contract.py | 2 + ib_async/ib.py | 26 ++++++---- ib_async/message.py | 2 +- ib_async/objects.py | 12 ++--- ib_async/order.py | 22 ++++---- .../contract_converters.py | 4 +- .../historical_data_converters.py | 4 +- .../subscription_converters.py | 2 - .../protobuf_converters/trade_converter.py | 6 +-- ib_async/util.py | 3 +- ib_async/wrapper.py | 26 +++++----- tests/conftest.py | 1 + tests/test_bidict.py | 22 +++++--- tests/test_contract_converters.py | 44 ++++++++-------- tests/test_contract_requests.py | 51 +++++++++++-------- tests/test_historical_data_converters.py | 3 -- tests/test_market_data_converters.py | 40 ++++++++++----- tests/test_trade_converter.py | 8 ++- 19 files changed, 164 insertions(+), 146 deletions(-) diff --git a/ib_async/client.py b/ib_async/client.py index d8aa0f13..0ee45ecf 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -2,7 +2,6 @@ import asyncio import logging -import math import struct import time from collections import deque @@ -112,8 +111,7 @@ createGlobalCancelRequestProto, createPlaceOrderRequestProto, ) -from .util import UNSET_DOUBLE, dataclassAsTuple, getLoop, run - +from .util import getLoop, run class Client: @@ -510,10 +508,8 @@ def cancelMktData(self, reqId): cancelMarketDataProto(reqId), ) - def placeOrder(self,orderId:int, contract: Contract, order: Order): - orderRequestProto = createPlaceOrderRequestProto( - orderId, contract, order - ) + def placeOrder(self, orderId: int, contract: Contract, order: Order): + orderRequestProto = createPlaceOrderRequestProto(orderId, contract, order) self.sendProto(MessageId.OUT.PLACE_ORDER, orderRequestProto) def cancelOrder(self, orderId: int, orderCancel: OrderCancel): @@ -667,11 +663,10 @@ def exerciseOptions( self.sendProto( MessageId.OUT.EXERCISE_OPTIONS, createExerciseOptionsRequestProto( - reqId,contract, exerciseAction, exerciseQuantity, account, override - ) + reqId, contract, exerciseAction, exerciseQuantity, account, override + ), ) - def reqScannerSubscription( self, reqId, @@ -974,28 +969,20 @@ def reqMarketRule(self, marketRuleId: int): def reqPnL(self, reqId, account, modelCode): self.sendProto( - MessageId.OUT.REQ_PNL, - createPnLRequestProto(reqId, account, modelCode) + MessageId.OUT.REQ_PNL, createPnLRequestProto(reqId, account, modelCode) ) - def cancelPnL(self, reqId): - self.sendProto( - MessageId.OUT.CANCEL_PNL, - createCancelPnLProto(reqId) - ) + self.sendProto(MessageId.OUT.CANCEL_PNL, createCancelPnLProto(reqId)) def reqPnLSingle(self, reqId, account, modelCode, conid): self.sendProto( MessageId.OUT.REQ_PNL_SINGLE, - createPnLSingleRequestProto(reqId, account, modelCode, conid) + createPnLSingleRequestProto(reqId, account, modelCode, conid), ) def cancelPnLSingle(self, reqId): - self.sendProto( - MessageId.OUT.CANCEL_PNL_SINGLE, - createCancelPnLProto(reqId) - ) + self.sendProto(MessageId.OUT.CANCEL_PNL_SINGLE, createCancelPnLProto(reqId)) def reqHistoricalTicks( self, @@ -1075,4 +1062,3 @@ def reqUserInfo(self, reqId): MessageId.OUT.REQ_USER_INFO, createUserInfoRequestProto(reqId), ) - diff --git a/ib_async/contract.py b/ib_async/contract.py index 3e359d55..3167ff35 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -25,11 +25,13 @@ class FundDistributionPolicyIndicator(Enum): AccumulationFund = ("N", "Accumulation Fund") IncomeFund = ("Y", "Income Fund") + @dataclass class IneligibilityReason: id_: str = field(default_factory=str) description: str = field(default_factory=str) + @dataclass(slots=True) class Contract: """ diff --git a/ib_async/ib.py b/ib_async/ib.py index 548a6c47..d0831d60 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -5,19 +5,15 @@ import datetime import logging import time -from asyncio import TimeoutError from enum import Flag, auto from typing import ( Any, - AsyncIterator, Awaitable, - Callable, Iterator, List, Optional, TypeVar, Union, - cast, ) from eventkit import Event @@ -61,13 +57,12 @@ Order, OrderCancel, OrderState, - OrderStateNumeric, OrderStatus, StopOrder, Trade, ) from ib_async.ticker import Ticker -from ib_async.wrapper import RequestError, Wrapper +from ib_async.wrapper import Wrapper _T = TypeVar("_T") @@ -1544,7 +1539,7 @@ def reqSmartComponents(self, bboExchange: str) -> list[SmartComponent]: Note: The exchanges must be open when using this request, otherwise an empty list is returned. - + ie `ib.reqSmartComponents(spy_ticker.bboExchange)` """ return self._run(self.reqSmartComponentsAsync(bboExchange)) @@ -2258,7 +2253,7 @@ async def reqTickersAsync( def whatIfOrderAsync( self, contract: Contract, order: Order ) -> Awaitable[OrderState]: - reqId = self.client.getReqId() + reqId = self.client.getReqId() whatIfOrder = copy.copy(order) whatIfOrder.whatIf = True whatIfOrder.orderId = reqId @@ -2276,6 +2271,7 @@ def reqCurrentTimeAsync(self) -> Awaitable[datetime.datetime]: self.wrapper.response_bus.filter(lambda key, _: key == "currentTime") .pluck(1) .take(1) + .map(self._raise_if_error) ) def reqCurrentTimeMiliAsync(self) -> Awaitable[datetime.datetime]: @@ -2284,6 +2280,7 @@ def reqCurrentTimeMiliAsync(self) -> Awaitable[datetime.datetime]: self.wrapper.response_bus.filter(lambda key, _: key == "currentTimeMili") .pluck(1) .take(1) + .map(self._raise_if_error) ) def reqAccountUpdatesAsync(self, account: str = "") -> Awaitable[None]: @@ -2299,9 +2296,11 @@ def reqAccountUpdatesAsync(self, account: str = "") -> Awaitable[None]: """ acctCode = account or self.wrapper.accounts[0] self.client.reqAccountUpdates(True, acctCode) - return self.wrapper.response_bus.filter( - lambda key, _: key == "accountValues" - ).take(1) + return ( + self.wrapper.response_bus.filter(lambda key, _: key == "accountValues") + .take(1) + .map(self._raise_if_error) + ) def reqAccountUpdatesMultiAsync( self, account: str, modelCode: str = "" @@ -2362,6 +2361,7 @@ def reqOpenOrdersAsync(self) -> Awaitable[list[Trade]]: self.wrapper.response_bus.filter(lambda key, _: key == "openOrders") .takewhile(lambda key, data: data is not None) .pluck(1) + .map(self._raise_if_error) .list() ) @@ -2371,6 +2371,7 @@ def reqAllOpenOrdersAsync(self) -> Awaitable[list[Trade]]: self.wrapper.response_bus.filter(lambda key, _: key == "openOrders") .takewhile(lambda key, data: data is not None) .pluck(1) + .map(self._raise_if_error) .list() ) @@ -2380,6 +2381,7 @@ def reqCompletedOrdersAsync(self, apiOnly: bool) -> Awaitable[list[Trade]]: self.wrapper.response_bus.filter(lambda key, _: key == "completedOrders") .takewhile(lambda key, data: data is not None) .pluck(1) + .map(self._raise_if_error) .list() ) @@ -2405,6 +2407,7 @@ def reqPositionsAsync(self) -> Awaitable[list[Position]]: self.wrapper.response_bus.filter(lambda name, _: name == "position") .takewhile(lambda name, data: data is not None) .pluck(1) + .map(self._raise_if_error) ) def reqContractDetailsAsync( @@ -2757,6 +2760,7 @@ def reqSecDefOptParamsAsync( self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) .takewhile(lambda rId, data: data is not None) .pluck(1) + .map(self._raise_if_error) .list() ) diff --git a/ib_async/message.py b/ib_async/message.py index d3eb156f..a8f7f83a 100644 --- a/ib_async/message.py +++ b/ib_async/message.py @@ -179,4 +179,4 @@ class OUT(IntEnum): REQ_USER_INFO = 304 REQ_CURRENT_TIME_IN_MILLIS = 305 CANCEL_CONTRACT_DATA = 306 - CANCEL_HISTORICAL_TICKS = 307 \ No newline at end of file + CANCEL_HISTORICAL_TICKS = 307 diff --git a/ib_async/objects.py b/ib_async/objects.py index 1c1cecaa..e508ee4f 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -345,7 +345,7 @@ class TickAttribLast: unreported: bool = False -@dataclass(slots=True,frozen=True) +@dataclass(slots=True, frozen=True) class TickPriceData: """Data from a TickPriceProto message.""" @@ -468,7 +468,7 @@ class PnL: dailyPnL: float = nan unrealizedPnL: float = nan realizedPnL: float = nan - + def getKey(self): """return PnL key ie: ib.cancelPnL(pnl.getKey()) @@ -494,7 +494,7 @@ class PnLSingle: realizedPnL: float = nan position: int = 0 value: float = nan - + def getKey(self): """return PnLSingle key ie: ib.cancelPnLSingle(pnl_single.getKey()) @@ -626,6 +626,7 @@ class Position(NamedTuple): position: float avgCost: float + @dataclass(slots=True) class Fill: contract: Contract @@ -732,7 +733,6 @@ def __init__(self, *args): self.updateEvent = Event("updateEvent") self.subscription_bus = Event("Subscription bus") - def __eq__(self, other) -> bool: return self is other @@ -806,11 +806,11 @@ def __init__(self, *args): def __eq__(self, other): return self is other - + def _on_data(self, ib: "IB", data: ScanData): """Called on scanner data.""" rank = data[0].rank if 0 <= len(data) else None - if rank == 0: + if rank == 0: self.clear() self.extend(data) ib.scannerDataEvent.emit(self) diff --git a/ib_async/order.py b/ib_async/order.py index 997502ed..466edcc2 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -334,19 +334,19 @@ class OrderState: minCommission: float = UNSET_DOUBLE maxCommission: float = UNSET_DOUBLE commissionCurrency: str = "" - marginCurrency:str = "" - initMarginBeforeOutsideRTH:float = UNSET_DOUBLE # type: float - maintMarginBeforeOutsideRTH:float = UNSET_DOUBLE # type: float - equityWithLoanBeforeOutsideRTH:float = UNSET_DOUBLE # type: float - initMarginChangeOutsideRTH:float = UNSET_DOUBLE # type: float - maintMarginChangeOutsideRTH:float = UNSET_DOUBLE # type: float - equityWithLoanChangeOutsideRTH:float = UNSET_DOUBLE # type: float - initMarginAfterOutsideRTH:float = UNSET_DOUBLE # type: float - maintMarginAfterOutsideRTH:float = UNSET_DOUBLE # type: float - equityWithLoanAfterOutsideRTH:float = UNSET_DOUBLE # type: float + marginCurrency: str = "" + initMarginBeforeOutsideRTH: float = UNSET_DOUBLE # type: float + maintMarginBeforeOutsideRTH: float = UNSET_DOUBLE # type: float + equityWithLoanBeforeOutsideRTH: float = UNSET_DOUBLE # type: float + initMarginChangeOutsideRTH: float = UNSET_DOUBLE # type: float + maintMarginChangeOutsideRTH: float = UNSET_DOUBLE # type: float + equityWithLoanChangeOutsideRTH: float = UNSET_DOUBLE # type: float + initMarginAfterOutsideRTH: float = UNSET_DOUBLE # type: float + maintMarginAfterOutsideRTH: float = UNSET_DOUBLE # type: float + equityWithLoanAfterOutsideRTH: float = UNSET_DOUBLE # type: float suggestedSize = UNSET_DECIMAL rejectReason = "" - orderAllocations = None + orderAllocations = None warningText: str = "" completedTime: str = "" completedStatus: str = "" diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py index 8b622864..78466ae5 100644 --- a/ib_async/protobuf_converters/contract_converters.py +++ b/ib_async/protobuf_converters/contract_converters.py @@ -562,7 +562,5 @@ def createSmartComponents( if smartComponentProto.HasField("exchangeLetter") else " " ) - smartComponents.append( - SmartComponent(bitNumber, exchange, exchangeLetter) - ) + smartComponents.append(SmartComponent(bitNumber, exchange, exchangeLetter)) return smartComponents diff --git a/ib_async/protobuf_converters/historical_data_converters.py b/ib_async/protobuf_converters/historical_data_converters.py index 3a4e9de9..75403981 100644 --- a/ib_async/protobuf_converters/historical_data_converters.py +++ b/ib_async/protobuf_converters/historical_data_converters.py @@ -123,9 +123,11 @@ def createHistoricalDataRequestProto( fillTagValueList(chartOptionsList, historicalDataRequestProto.chartOptions) return historicalDataRequestProto + def createCancelHistoricalDataProto(reqId: int) -> CancelHistoricalDataProto: cancelHistoricalDataProto = CancelHistoricalDataProto() - if isValidIntValue(reqId): cancelHistoricalDataProto.reqId = reqId + if isValidIntValue(reqId): + cancelHistoricalDataProto.reqId = reqId return cancelHistoricalDataProto diff --git a/ib_async/protobuf_converters/subscription_converters.py b/ib_async/protobuf_converters/subscription_converters.py index 9de5292d..1b8c36c3 100644 --- a/ib_async/protobuf_converters/subscription_converters.py +++ b/ib_async/protobuf_converters/subscription_converters.py @@ -1,4 +1,3 @@ -import logging from ib_async.objects import ScannerSubscription, TagValue from ib_async.util import UNSET_DOUBLE, isValidIntValue from ib_async.contract import ScanData, ContractDetails @@ -190,4 +189,3 @@ def createScannerDataList(scannerDataProto: ScannerDataProto) -> list[ScanData]: ) dataList.append(scanData) return dataList - diff --git a/ib_async/protobuf_converters/trade_converter.py b/ib_async/protobuf_converters/trade_converter.py index 85847070..3f7f1a6c 100644 --- a/ib_async/protobuf_converters/trade_converter.py +++ b/ib_async/protobuf_converters/trade_converter.py @@ -2,10 +2,9 @@ Converters for trade-related Protobuf messages. """ -from datetime import datetime from decimal import Decimal -from ib_async.contract import ComboLeg, Contract, DeltaNeutralContract +from ib_async.contract import Contract from ib_async.objects import ( CommissionReport, Execution, @@ -62,9 +61,6 @@ ) from ..protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto from ..protobuf.Order_pb2 import Order as OrderProto -from ..protobuf.OrderAllocation_pb2 import ( - OrderAllocation as OrderAllocationProto, -) from ..protobuf.OrderCancel_pb2 import OrderCancel as OrderCancelProto from ..protobuf.OrderCondition_pb2 import OrderCondition as OrderConditionProto from ..protobuf.OrderState_pb2 import OrderState as OrderStateProto diff --git a/ib_async/util.py b/ib_async/util.py index 0c8f32fb..15687dcb 100644 --- a/ib_async/util.py +++ b/ib_async/util.py @@ -613,7 +613,8 @@ def parseIBDatetime(s: str) -> Union[dt.date, dt.datetime]: return t -def parseIBTimeStamp(t:int, tz:dt.tzinfo=dt.timezone.utc) -> dt.datetime: + +def parseIBTimeStamp(t: int, tz: dt.tzinfo = dt.timezone.utc) -> dt.datetime: return dt.datetime.fromtimestamp(t, tz) diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index b145446d..b6792c7a 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -129,7 +129,8 @@ def __init__(self, *, track_objects_weakly: bool = False) -> None: """Bidirectional mapping Args: - track_objects_weakly (bool, optional): enable get_request_id_by_object, using weakreferences. Defaults to False. + track_objects_weakly (bool, optional): enable get_request_id_by_object, + using weakreferences. Defaults to False. """ # Primary storage: request_id → (object_id, object) self._by_request: dict[int, tuple[K, V]] = {} @@ -231,7 +232,7 @@ def remove_by_object_id(self, object_id: K) -> None: def update_request_id(self, object_id: K, new_request_id: int): """ - Updates the request_id associated with an existing entry, identified by its + Updates the request_id associated with an existing entry, identified by its object_id. This method is used when the primary request_id (e.g., permId for a Trade) @@ -240,9 +241,9 @@ def update_request_id(self, object_id: K, new_request_id: int): preserving the object's identity and its association with the object_id. Args: - object_id (K): The unique identifier for the object (e.g., (clientId, + object_id (K): The unique identifier for the object (e.g., (clientId, orderId) for a Trade). - new_request_id (int): The new, permanent request ID to associate with the + new_request_id (int): The new, permanent request ID to associate with the object (e.g., permId). """ old_request_id = self._request_by_object_id.get(object_id) @@ -589,7 +590,6 @@ def position(self, position: Position): self.ib.positionEvent.emit(position) self.response_bus.emit("position", position) - def positionEnd(self): self._endReq("position") @@ -612,7 +612,7 @@ def pnl( ): pnl = self.Pnl.get_by_request_id(reqId) if not pnl: - self._logger.error("pnl: No pnl found for reqId %s",reqId) + self._logger.error("pnl: No pnl found for reqId %s", reqId) return pnl.dailyPnL = dailyPnL @@ -631,7 +631,7 @@ def pnlSingle( ): pnlSingle = self.pnlSingles.get_by_request_id(reqId) if not pnlSingle: - self._logger.error("pnlSingle: No pnlSingle found for reqId %s",reqId) + self._logger.error("pnlSingle: No pnlSingle found for reqId %s", reqId) return pnlSingle.position = pos @@ -650,7 +650,7 @@ def orderKey(self, clientId: int, orderId: int, permId: int) -> OrderKeyType: key = (clientId, orderId) return key - def openOrder(self, trade: Trade,orderState: OrderState): + def openOrder(self, trade: Trade, orderState: OrderState): """ This wrapper is called to: @@ -661,7 +661,7 @@ def openOrder(self, trade: Trade,orderState: OrderState): * handle openOrders and allOpenOrders responses. """ if trade.order.whatIf: - self.response_bus.emit(trade.order.orderId,orderState) + self.response_bus.emit(trade.order.orderId, orderState) return key = self.orderKey( @@ -855,7 +855,7 @@ def historicalData(self, reqId: int, bars: list[BarData]): def historicalDataEnd(self, reqId, _start: str, _end: str): self._endReq(reqId) - + def historicalDataUpdate(self, reqId: int, bar: BarData): subscription = self.subscriptions.get_by_request_id(reqId) if subscription: @@ -1001,8 +1001,10 @@ def updateMktDepthL2( # if you're curious when these operations run and what they do, enable this too: # fmt: off - # print("BID" if side else "ASK", "OPERATION", operation, "at position", position, "for price", price, "at qty", size) - # assert list(dom.keys()) == list(range(0, len(dom))), f"Keys aren't sequential? {dom} :: {ticker}" + # print("BID" if side else "ASK", "OPERATION", operation, "at position", + # position, "for price", price, "at qty", size) + # assert list(dom.keys()) == list(range(0, len(dom))), f"Keys aren't + # sequential? {dom} :: {ticker}" # fmt: on if operation in {0, 1}: diff --git a/tests/conftest.py b/tests/conftest.py index 618b3e90..676ff872 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,6 +21,7 @@ async def ib(): yield ib ib.disconnect() + @pytest.fixture def mock_ib(): """Fixture for a mocked IB instance.""" diff --git a/tests/test_bidict.py b/tests/test_bidict.py index b3151d49..f47a8b90 100644 --- a/tests/test_bidict.py +++ b/tests/test_bidict.py @@ -127,8 +127,8 @@ def test_update_request_id(self): bidict = BiDict[str, object]() obj_value = "My Test Object" object_id = "temp_key" - initial_request_id = 100 # Represents an orderId - final_request_id = 5000 # Represents a permId + initial_request_id = 100 # Represents an orderId + final_request_id = 5000 # Represents a permId # Add the object with initial (dummy) request_id bidict.add(initial_request_id, object_id, obj_value) @@ -143,11 +143,19 @@ def test_update_request_id(self): bidict.update_request_id(object_id, final_request_id) # Verify updated state - assert bidict.get_by_request_id(initial_request_id) is None # Old request_id should no longer work - assert bidict.get_by_request_id(final_request_id) is obj_value # New request_id should work - assert bidict.get_by_object_id(object_id) is obj_value # object_id should still work - assert bidict.get_request_id(object_id) == final_request_id # object_id maps to new request_id - assert len(bidict) == 1 # Size should remain the same + assert ( + bidict.get_by_request_id(initial_request_id) is None + ) # Old request_id should no longer work + assert ( + bidict.get_by_request_id(final_request_id) is obj_value + ) # New request_id should work + assert ( + bidict.get_by_object_id(object_id) is obj_value + ) # object_id should still work + assert ( + bidict.get_request_id(object_id) == final_request_id + ) # object_id maps to new request_id + assert len(bidict) == 1 # Size should remain the same # Verify object identity (it's the same object, just re-indexed) retrieved_obj_by_new_req_id = bidict.get_by_request_id(final_request_id) diff --git a/tests/test_contract_converters.py b/tests/test_contract_converters.py index 911a668f..b64680d1 100644 --- a/tests/test_contract_converters.py +++ b/tests/test_contract_converters.py @@ -6,8 +6,6 @@ ContractDescription, ContractDetails, DeltaNeutralContract, - FundAssetType, - FundDistributionPolicyIndicator, IneligibilityReason, ) from ib_async.objects import OptionChain, SmartComponent @@ -18,11 +16,15 @@ from ib_async.protobuf.ContractDescription_pb2 import ( ContractDescription as ContractDescriptionProto, ) -from ib_async.protobuf.ContractDetails_pb2 import ContractDetails as ContractDetailsProto +from ib_async.protobuf.ContractDetails_pb2 import ( + ContractDetails as ContractDetailsProto, +) from ib_async.protobuf.DeltaNeutralContract_pb2 import ( DeltaNeutralContract as DeltaNeutralContractProto, ) -from ib_async.protobuf.MarketRuleRequest_pb2 import MarketRuleRequest as MarketRuleRequestProto +from ib_async.protobuf.MarketRuleRequest_pb2 import ( + MarketRuleRequest as MarketRuleRequestProto, +) from ib_async.protobuf.MatchingSymbolsRequest_pb2 import ( MatchingSymbolsRequest as MatchingSymbolsRequestProto, ) @@ -35,7 +37,9 @@ from ib_async.protobuf.SmartComponentsRequest_pb2 import ( SmartComponentsRequest as SmartComponentsRequestProto, ) -from ib_async.protobuf.SmartComponents_pb2 import SmartComponents as SmartComponentsProto +from ib_async.protobuf.SmartComponents_pb2 import ( + SmartComponents as SmartComponentsProto, +) from ib_async.protobuf_converters.contract_converters import ( createSecDefOptParamsRequestProto, createOptionChain, @@ -56,8 +60,8 @@ createSmartComponents, ) -class TestContractConverters: +class TestContractConverters: def test_createSecDefOptParamsRequestProto(self): proto = createSecDefOptParamsRequestProto(1, "SPX", "SMART", "IND", 123) assert isinstance(proto, SecDefOptParamsRequestProto) @@ -74,7 +78,7 @@ def test_createOptionChain(self): tradingClass="SPXW", multiplier="100", expirations=["202512", "202603"], - strikes=[4000.0, 4100.0] + strikes=[4000.0, 4100.0], ) option_chain = createOptionChain(proto) assert isinstance(option_chain, OptionChain) @@ -87,11 +91,7 @@ def test_createOptionChain(self): def test_createContract(self): proto = ContractProto( - conId=1, - symbol="AAPL", - secType="STK", - exchange="SMART", - currency="USD" + conId=1, symbol="AAPL", secType="STK", exchange="SMART", currency="USD" ) contract = createContract(proto) assert isinstance(contract, Contract) @@ -108,7 +108,7 @@ def test_createComboLegs(self): leg1.ratio = 1 leg1.action = "BUY" leg1.exchange = "SMART" - + combo_legs = createComboLegs(proto) assert len(combo_legs) == 1 leg = combo_legs[0] @@ -124,7 +124,7 @@ def test_createDeltaNeutralContract(self): dn_proto.conId = 123 dn_proto.delta = 0.5 dn_proto.price = 10.0 - + dn_contract = createDeltaNeutralContract(proto) assert isinstance(dn_contract, DeltaNeutralContract) assert dn_contract.conId == 123 @@ -136,7 +136,7 @@ def test_createIneligibilityReasonList(self): reason1 = proto.ineligibilityReasonList.add() reason1.id = "1" reason1.description = "Reason 1" - + reasons = createIneligibilityReasonList(proto) assert len(reasons) == 1 reason = reasons[0] @@ -157,7 +157,7 @@ def test_createContractDetails(self): contract_proto = ContractProto(symbol="TSLA", secType="STK") details_proto = ContractDetailsProto(marketName="Tesla", longName="Tesla Inc.") msg = ContractDataProto(contract=contract_proto, contractDetails=details_proto) - + cd = createContractDetails(msg) assert isinstance(cd, ContractDetails) assert cd.contract.symbol == "TSLA" @@ -168,7 +168,7 @@ def test_createContractDescription(self): contract_proto = ContractProto(symbol="GOOG", secType="STK") desc_proto = ContractDescriptionProto(contract=contract_proto) desc_proto.derivativeSecTypes.extend(["OPT", "FUT"]) - + desc = createContractDescription(desc_proto) assert isinstance(desc, ContractDescription) assert desc.contract.symbol == "GOOG" @@ -186,7 +186,9 @@ def test_createMarketRuleRequestProto(self): assert proto.marketRuleId == 123 def test_createContractProto(self): - contract = Contract(symbol="MSFT", secType="STK", exchange="SMART", currency="USD") + contract = Contract( + symbol="MSFT", secType="STK", exchange="SMART", currency="USD" + ) proto = createContractProto(contract, None) assert isinstance(proto, ContractProto) assert proto.symbol == "MSFT" @@ -208,7 +210,7 @@ def test_createComboLegProtoList(self): leg2 = ComboLeg(conId=2, ratio=2, action="SELL") contract = Contract(comboLegs=[leg1, leg2]) order = Order(orderComboLegs=[Mock(price=10.0), Mock(price=20.0)]) - + proto_list = createComboLegProtoList(contract, order) assert len(proto_list) == 2 assert proto_list[0].conId == 1 @@ -249,7 +251,7 @@ def test_createSmartComponents(self): smart_components = createSmartComponents(smart_components_proto) assert isinstance(smart_components, list) assert len(smart_components) == 2 - + assert isinstance(smart_components[0], SmartComponent) assert smart_components[0].bitNumber == 1 assert smart_components[0].exchange == "SMART" @@ -264,4 +266,4 @@ def test_createSmartComponents_empty(self): smart_components_proto = SmartComponentsProto() smart_components = createSmartComponents(smart_components_proto) assert isinstance(smart_components, list) - assert len(smart_components) == 0 \ No newline at end of file + assert len(smart_components) == 0 diff --git a/tests/test_contract_requests.py b/tests/test_contract_requests.py index 664ad372..883a0215 100644 --- a/tests/test_contract_requests.py +++ b/tests/test_contract_requests.py @@ -1,4 +1,3 @@ - import asyncio import pytest from unittest.mock import Mock, patch @@ -6,10 +5,17 @@ from ib_async import IB, Contract, ContractDetails, ContractDescription, OptionChain from ib_async.objects import ConnectionStats from ib_async.protobuf.Contract_pb2 import Contract as ContractProto -from ib_async.protobuf.ContractDescription_pb2 import ContractDescription as ContractDescriptionProto +from ib_async.protobuf.ContractDescription_pb2 import ( + ContractDescription as ContractDescriptionProto, +) from ib_async.protobuf.SymbolSamples_pb2 import SymbolSamples as SymbolSamplesProto -from ib_async.protobuf_converters.contract_converters import createContractDescription, createOptionChain -from ib_async.protobuf.SecDefOptParameter_pb2 import SecDefOptParameter as SecDefOptParameterProto +from ib_async.protobuf_converters.contract_converters import ( + createContractDescription, + createOptionChain, +) +from ib_async.protobuf.SecDefOptParameter_pb2 import ( + SecDefOptParameter as SecDefOptParameterProto, +) @pytest.mark.asyncio @@ -39,10 +45,7 @@ async def emitter(): ib.wrapper.response_bus.emit(1, cd2) ib.wrapper.response_bus.emit(1, None) - results, _ = await asyncio.gather( - event, - emitter() - ) + results, _ = await asyncio.gather(event, emitter()) assert len(results) == 2 assert results[0].contract.conId == 1 assert results[1].contract.conId == 2 @@ -50,6 +53,7 @@ async def emitter(): # Check that the underlying client method was called ib.client.reqContractDetails.assert_called_once_with(1, contract) + @pytest.mark.asyncio async def test_reqContractDetails_protobuf_not_supported(): """ @@ -72,6 +76,7 @@ async def test_reqContractDetails_protobuf_not_supported(): assert "Protobuf not supported by server." in str(exc_info.value) ib.client.reqContractDetails.assert_not_called() + @pytest.mark.asyncio async def test_reqMatchingSymbolsAsync(): """ @@ -92,21 +97,22 @@ async def test_reqMatchingSymbolsAsync(): async def emitter(): await asyncio.sleep(0.01) # give consumer time to subscribe # Simulate the response from TWS - cd_proto1 = ContractDescriptionProto(contract=ContractProto(symbol="AAPL", conId=10)) - cd_proto2 = ContractDescriptionProto(contract=ContractProto(symbol="AAPL", conId=20)) - + cd_proto1 = ContractDescriptionProto( + contract=ContractProto(symbol="AAPL", conId=10) + ) + cd_proto2 = ContractDescriptionProto( + contract=ContractProto(symbol="AAPL", conId=20) + ) + # The Decoder.symbolSamples method emits a list of ContractDescription objects contract_descriptions = [ createContractDescription(cd_proto1), - createContractDescription(cd_proto2) + createContractDescription(cd_proto2), ] ib.wrapper.response_bus.emit(1, contract_descriptions) - results, _ = await asyncio.gather( - event, - emitter() - ) + results, _ = await asyncio.gather(event, emitter()) assert len(results) == 2 assert results[0].contract.conId == 10 @@ -134,7 +140,9 @@ async def test_reqSecDefOptParamsAsync(): underlyingConId = 123 # Call the async method - event = ib.reqSecDefOptParamsAsync(underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId) + event = ib.reqSecDefOptParamsAsync( + underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId + ) async def emitter(): await asyncio.sleep(0.1) # give consumer time to subscribe @@ -165,10 +173,7 @@ async def emitter(): ib.wrapper.response_bus.emit(1, option_chain2) ib.wrapper.response_bus.emit(1, None) - results, _ = await asyncio.gather( - event, - emitter() - ) + results, _ = await asyncio.gather(event, emitter()) assert len(results) == 2 assert results[0].exchange == "SMART" @@ -176,4 +181,6 @@ async def emitter(): assert results[1].expirations == ["202503", "202504"] # Check that the underlying client method was called - ib.client.reqSecDefOptParams.assert_called_once_with(1, underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId) + ib.client.reqSecDefOptParams.assert_called_once_with( + 1, underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId + ) diff --git a/tests/test_historical_data_converters.py b/tests/test_historical_data_converters.py index 67eb4944..bc0bf264 100644 --- a/tests/test_historical_data_converters.py +++ b/tests/test_historical_data_converters.py @@ -7,13 +7,10 @@ BarData, HistogramData, HistoricalSchedule, - HistoricalSession, HistoricalTick, HistoricalTickBidAsk, HistoricalTickLast, RealTimeBar, - TickAttribBidAsk, - TickAttribLast, ) from ib_async.protobuf.FundamentalsDataRequest_pb2 import ( FundamentalsDataRequest as FundamentalsDataRequestProto, diff --git a/tests/test_market_data_converters.py b/tests/test_market_data_converters.py index f3302f58..722a400f 100644 --- a/tests/test_market_data_converters.py +++ b/tests/test_market_data_converters.py @@ -1,8 +1,7 @@ import pytest -from ib_async.contract import Contract, TagValue +from ib_async.contract import Contract from ib_async.objects import ( OptionComputation, - TickAttrib, TickComputationData, TickGenericData, TickParams, @@ -17,8 +16,12 @@ from ib_async.protobuf.MarketDataRequest_pb2 import ( MarketDataRequest as MarketDataRequestProto, ) -from ib_async.protobuf.CancelMarketData_pb2 import CancelMarketData as CancelMarketDataProto -from ib_async.protobuf.TickByTickRequest_pb2 import TickByTickRequest as TickByTickRequestProto +from ib_async.protobuf.CancelMarketData_pb2 import ( + CancelMarketData as CancelMarketDataProto, +) +from ib_async.protobuf.TickByTickRequest_pb2 import ( + TickByTickRequest as TickByTickRequestProto, +) from ib_async.protobuf.TickReqParams_pb2 import TickReqParams as TickReqParamsProto from ib_async.protobuf.TickPrice_pb2 import TickPrice as TickPriceProto from ib_async.protobuf.TickSize_pb2 import TickSize as TickSizeProto @@ -56,8 +59,8 @@ createCancelCalculateOptionPriceProto, ) -class TestMarketDataConverters: +class TestMarketDataConverters: def test_createMarketDataTypeRequestProto(self): proto = createMarketDataTypeRequestProto(1) assert isinstance(proto, MarketDataTypeRequestProto) @@ -89,7 +92,9 @@ def test_createTickByTickRequestProto(self): assert proto.ignoreSize is False def test_createTickParams(self): - proto = TickReqParamsProto(reqId=1, minTick="0.01", bboExchange="ISLAND", snapshotPermissions=3) + proto = TickReqParamsProto( + reqId=1, minTick="0.01", bboExchange="ISLAND", snapshotPermissions=3 + ) params = createTickParams(proto) assert isinstance(params, TickParams) assert params.reqId == 1 @@ -98,9 +103,11 @@ def test_createTickParams(self): assert params.snapshotPermissions == 3 def test_createTickPriceData(self): - proto = TickPriceProto(reqId=1, tickType=TickType.BID.value, price=1.2, size="100", attrMask=1) + proto = TickPriceProto( + reqId=1, tickType=TickType.BID.value, price=1.2, size="100", attrMask=1 + ) price_data, size_data = createTickPriceData(proto) - + assert isinstance(price_data, TickPriceData) assert price_data.reqId == 1 assert price_data.tickType == TickType.BID @@ -121,7 +128,9 @@ def test_createTickSizeData(self): assert size_data.size == 200 def test_createTickStringData(self): - proto = TickStringProto(reqId=1, tickType=TickType.LAST_TIMESTAMP.value, value="1672531200") + proto = TickStringProto( + reqId=1, tickType=TickType.LAST_TIMESTAMP.value, value="1672531200" + ) string_data = createTickStringData(proto) assert isinstance(string_data, TickStringData) assert string_data.reqId == 1 @@ -129,7 +138,9 @@ def test_createTickStringData(self): assert string_data.value == "1672531200" def test_createTickGenericData(self): - proto = TickGenericProto(reqId=1, tickType=TickType.OPTION_IMPLIED_VOL.value, value=0.5) + proto = TickGenericProto( + reqId=1, tickType=TickType.OPTION_IMPLIED_VOL.value, value=0.5 + ) generic_data = createTickGenericData(proto) assert isinstance(generic_data, TickGenericData) assert generic_data.reqId == 1 @@ -138,7 +149,10 @@ def test_createTickGenericData(self): def test_createTickOptionComputation(self): proto = TickOptionComputationProto( - reqId=1, tickType=TickType.BID_OPTION_COMPUTATION.value, impliedVol=0.25, delta=0.6 + reqId=1, + tickType=TickType.BID_OPTION_COMPUTATION.value, + impliedVol=0.25, + delta=0.6, ) comp_data = createTickOptionComputation(proto) assert isinstance(comp_data, TickComputationData) @@ -150,7 +164,9 @@ def test_createTickOptionComputation(self): def test_createCalculateImpliedVolatilityRequestProto(self): contract = Contract(symbol="AAPL", secType="OPT", right="C", strike=150) - proto = createCalculateImpliedVolatilityRequestProto(1, contract, 2.5, 145.0, []) + proto = createCalculateImpliedVolatilityRequestProto( + 1, contract, 2.5, 145.0, [] + ) assert isinstance(proto, CalculateImpliedVolatilityRequestProto) assert proto.reqId == 1 assert proto.contract.symbol == "AAPL" diff --git a/tests/test_trade_converter.py b/tests/test_trade_converter.py index b654e460..3b4f4481 100644 --- a/tests/test_trade_converter.py +++ b/tests/test_trade_converter.py @@ -3,9 +3,8 @@ from decimal import Decimal from unittest.mock import Mock -from ib_async.contract import Contract, ComboLeg, DeltaNeutralContract +from ib_async.contract import Contract, ComboLeg from ib_async.objects import ( - TagValue, OptionExerciseType, SoftDollarTier, Execution, @@ -26,7 +25,6 @@ VolumeCondition, PercentChangeCondition, OrderCancel, - OrderAllocation, ) from ib_async.protobuf.Order_pb2 import Order as OrderProto from ib_async.protobuf.PlaceOrderRequest_pb2 import ( @@ -699,7 +697,7 @@ def test_createTagValueList(self): assert tag_value_list[1].value == "Value2" def test_createTagValueList_empty(self): - proto_map = {} # type: ignore + proto_map = {} # type: ignore tag_value_list = createTagValueList(proto_map) assert len(tag_value_list) == 0 @@ -881,7 +879,7 @@ def test_createOrderCancelProto(self): assert proto.extOperator == "OP1" def test_createOrderCancelProto_none(self): - proto = createOrderCancelProto(None) # type: ignore + proto = createOrderCancelProto(None) # type: ignore assert proto is None def test_createGlobalCancelRequestProto(self): From dcffb055f9aa09416a6b80b3aadd1a654396bfd2 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:53:02 +0100 Subject: [PATCH 07/48] feat(ticker): Enhance Ticker class with additional ETF NAV fields and improve data handling - Added new tick types for ETF NAV including close, prior close, bid, ask, last, frozen last, high, and low. - Updated Ticker dataclass to include new fields for ETF NAV data. - Modified the __post_init__ method to initialize new ETF NAV fields. - Improved the handling of ticker data updates to accommodate new tick types. - Refactored ticker data processing methods for clarity and efficiency. feat(util): Introduce quantize_decimals decorator for Decimal fields - Added a decorator to quantize Decimal fields in dataclass objects to specified decimal places. - Enhanced the parseIBDatetime function to correctly handle datetime strings. refactor(wrapper): Streamline ticker and subscription handling - Consolidated tick data handling methods to reduce redundancy. - Updated method signatures to remove unnecessary parameters. - Improved error handling for unknown request IDs in ticker delivery methods. chore(tests): Update tests for market data and trade converters - Refactored test cases to align with changes in the codebase. - Ensured tests validate new fields and behaviors introduced in the Ticker class. - Updated import paths for trade converter functions to reflect new module structure. --- .gitignore | 1 + ib_async/__init__.py | 10 +- ib_async/client.py | 23 +- ib_async/contract.py | 62 ++--- ib_async/decoder.py | 175 ++++--------- ib_async/ib.py | 60 +++-- ib_async/objects.py | 146 ++++++----- ib_async/order.py | 245 ++++++++---------- .../protobuf_converters/base_converters.py | 18 ++ .../contract_converters.py | 3 +- .../historical_data_converters.py | 82 +++++- .../market_data_converters.py | 45 ++-- ...trade_converter.py => trade_converters.py} | 186 +++++++------ ib_async/ticker.py | 67 +++-- ib_async/util.py | 30 ++- ib_async/wrapper.py | 167 ++++-------- pyproject.toml | 16 +- tests/test_market_data_converters.py | 21 +- tests/test_trade_converter.py | 12 +- 19 files changed, 697 insertions(+), 672 deletions(-) create mode 100644 ib_async/protobuf_converters/base_converters.py rename ib_async/protobuf_converters/{trade_converter.py => trade_converters.py} (91%) diff --git a/.gitignore b/.gitignore index 6209c949..5fa79f7b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ poetry.lock proto/* **/__pycache__/ #pythonclient/* +.coverage diff --git a/ib_async/__init__.py b/ib_async/__init__.py index 8e216278..d4a7b4ec 100644 --- a/ib_async/__init__.py +++ b/ib_async/__init__.py @@ -8,9 +8,9 @@ from . import util from .client import Client from .contract import ( + CFD, Bag, Bond, - CFD, ComboLeg, Commodity, ContFuture, @@ -76,9 +76,6 @@ TickAttrib, TickAttribBidAsk, TickAttribLast, - TickByTickAllLast, - TickByTickBidAsk, - TickByTickMidPoint, TickData, TradeLogEntry, WshEventData, @@ -93,7 +90,6 @@ OrderComboLeg, OrderCondition, OrderState, - OrderStateNumeric, OrderStatus, PercentChangeCondition, PriceCondition, @@ -136,7 +132,6 @@ "FlexReport", "IB", "IBDefaults", - "OrderStateNumeric", "IBC", "Watchdog", "AccountValue", @@ -180,10 +175,7 @@ "TickAttrib", "TickAttribBidAsk", "TickAttribLast", - "TickByTickAllLast", "WshEventData", - "TickByTickBidAsk", - "TickByTickMidPoint", "TickData", "TradeLogEntry", "BracketOrder", diff --git a/ib_async/client.py b/ib_async/client.py index 0ee45ecf..50b7ad27 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -104,7 +104,7 @@ createScannerSubscriptionRequestProto, createScannerParametersRequestProto, ) -from .protobuf_converters.trade_converter import ( +from .protobuf_converters.trade_converters import ( createCancelOrderRequestProto, createExecutionRequestProto, createExerciseOptionsRequestProto, @@ -658,12 +658,29 @@ def reqHistoricalData( ) def exerciseOptions( - self, reqId, contract, exerciseAction, exerciseQuantity, account, override + self, + reqId, + contract, + exerciseAction, + exerciseQuantity, + account, + override, + manualOrderTime, + customerAccount, + professionalCustomer, ): self.sendProto( MessageId.OUT.EXERCISE_OPTIONS, createExerciseOptionsRequestProto( - reqId, contract, exerciseAction, exerciseQuantity, account, override + reqId, + contract, + exerciseAction, + exerciseQuantity, + account, + override, + manualOrderTime, + customerAccount, + professionalCustomer, ), ) diff --git a/ib_async/contract.py b/ib_async/contract.py index 3167ff35..b017ccc8 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -3,7 +3,7 @@ import datetime as dt from dataclasses import dataclass, field from enum import Enum -from typing import List, NamedTuple, Optional +from typing import List, Optional import ib_async.util as util @@ -111,7 +111,7 @@ class Contract: symbol: str = "" secType: str = "" lastTradeDateOrContractMonth: str = "" - lastTradeDate = "" + lastTradeDate: str = "" strike: float = 0.0 right: str = "" multiplier: str = "" @@ -564,13 +564,13 @@ def __init__( **kwargs, ) - -class TagValue(NamedTuple): +@dataclass(slots=True, frozen=True) +class TagValue: tag: str value: str -@dataclass +@dataclass(slots=True) class ComboLeg: conId: int = 0 ratio: int = 0 @@ -582,19 +582,19 @@ class ComboLeg: exemptCode: int = -1 -@dataclass +@dataclass(slots=True) class DeltaNeutralContract: conId: int = 0 delta: float = 0.0 price: float = 0.0 - -class TradingSession(NamedTuple): +@dataclass(slots=True, frozen=True) +class TradingSession: start: dt.datetime end: dt.datetime -@dataclass +@dataclass(slots=True) class ContractDetails: contract: Optional[Contract] = None marketName: str = "" @@ -642,27 +642,27 @@ class ContractDetails: nextOptionPartial: bool = False notes: str = "" # FUND values - fundName = "" - fundFamily = "" - fundType = "" - fundFrontLoad = "" - fundBackLoad = "" - fundBackLoadTimeInterval = "" - fundManagementFee = "" - fundClosed = False - fundClosedForNewInvestors = False - fundClosedForNewMoney = False - fundNotifyAmount = "" - fundMinimumInitialPurchase = "" - fundSubsequentMinimumPurchase = "" - fundBlueSkyStates = "" - fundBlueSkyTerritories = "" - fundDistributionPolicyIndicator = FundDistributionPolicyIndicator.NoneItem - fundAssetType = FundAssetType.NoneItem + fundName: str = "" + fundFamily: str = "" + fundType: str = "" + fundFrontLoad: str = "" + fundBackLoad: str = "" + fundBackLoadTimeInterval: str = "" + fundManagementFee: str = "" + fundClosed: bool = False + fundClosedForNewInvestors: bool = False + fundClosedForNewMoney: bool = False + fundNotifyAmount: str = "" + fundMinimumInitialPurchase: str = "" + fundSubsequentMinimumPurchase: str = "" + fundBlueSkyStates: str = "" + fundBlueSkyTerritories: str = "" + fundDistributionPolicyIndicator: FundDistributionPolicyIndicator = FundDistributionPolicyIndicator.NoneItem + fundAssetType: FundAssetType = FundAssetType.NoneItem ineligibilityReasonList: list[IneligibilityReason] = field(default_factory=list) - eventContract1 = "" - eventContractDescription1 = "" - eventContractDescription2 = "" + eventContract1: str = "" + eventContractDescription1: str = "" + eventContractDescription2: str = "" def tradingSessions(self) -> List[TradingSession]: return self._parseSessions(self.tradingHours) @@ -703,13 +703,13 @@ def _parseSessions(self, s: str) -> List[TradingSession]: return sessions -@dataclass +@dataclass(slots=True) class ContractDescription: contract: Optional[Contract] = None derivativeSecTypes: List[str] = field(default_factory=list) -@dataclass +@dataclass(slots=True, frozen=True) class ScanData: rank: int contractDetails: ContractDetails diff --git a/ib_async/decoder.py b/ib_async/decoder.py index 16d3fecb..df909ff8 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -2,8 +2,6 @@ import logging -from ib_async.protobuf_converters.subscription_converters import createScannerDataList - from .contract import ( Contract, ContractDescription, @@ -15,14 +13,11 @@ DepthMktDataDescription, FamilyCode, HistogramData, - HistoricalTickBidAsk, - HistoricalTickLast, + HistoricalTickType, NewsProvider, PriceIncrement, - SmartComponent, SoftDollarTier, TagValue, - TickType, ) from .order import OrderStatus from .protobuf.AccountDataEnd_pb2 import AccountDataEnd as AccountDataEndProto @@ -80,16 +75,16 @@ from .protobuf.NextValidId_pb2 import NextValidId as NextValidIdProto from .protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto from .protobuf.OpenOrdersEnd_pb2 import OpenOrdersEnd as OpenOrderEndProto -from .protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto from .protobuf.OrderBound_pb2 import OrderBound as OrderBoundProto +from .protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto +from .protobuf.PnL_pb2 import PnL as PnLProto +from .protobuf.PnLSingle_pb2 import PnLSingle as PnLSingleProto from .protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto from .protobuf.Position_pb2 import Position as PositionProto from .protobuf.PositionEnd_pb2 import PositionEnd as PositionEndProto -from .protobuf.PnL_pb2 import PnL as PnLProto -from .protobuf.PnLSingle_pb2 import PnLSingle as PnLSingleProto from .protobuf.RealTimeBarTick_pb2 import RealTimeBarTick as RealTimeBarTickProto -from .protobuf.ScannerParameters_pb2 import ScannerParameters as ScannerParametersProto from .protobuf.ScannerData_pb2 import ScannerData as ScannerDataProto +from .protobuf.ScannerParameters_pb2 import ScannerParameters as ScannerParametersProto from .protobuf.SecDefOptParameter_pb2 import ( SecDefOptParameter as SecDefOptParameterProto, ) @@ -98,6 +93,7 @@ ) from .protobuf.SmartComponents_pb2 import SmartComponents as SmartComponentsProto from .protobuf.SymbolSamples_pb2 import SymbolSamples as SymbolSamplesProto +from .protobuf.TickByTickData_pb2 import TickByTickData as TickByTickDataProto from .protobuf.TickGeneric_pb2 import TickGeneric as TickGenericProto from .protobuf.TickOptionComputation_pb2 import ( TickOptionComputation as TickOptionComputationProto, @@ -107,7 +103,6 @@ from .protobuf.TickSize_pb2 import TickSize as TickSizeProto from .protobuf.TickSnapshotEnd_pb2 import TickSnapshotEnd as TickSnapshotEndProto from .protobuf.TickString_pb2 import TickString as TickStringProto -from .protobuf.TickByTickData_pb2 import TickByTickData as TickByTickDataProto from .protobuf.UserInfo_pb2 import UserInfo as UserInfoProto from .protobuf_converters.account_converters import ( createAccountSummary, @@ -123,24 +118,23 @@ createSmartComponents, ) from .protobuf_converters.historical_data_converters import ( + HistoricalTicksProtoType, createBarData, createBarDataList, createHistogramDataEntry, createHistoricalSchedule, - createHistoricalTick, - createHistoricalTickBidAsk, - createHistoricalTickLast, + createHistoricalTickShim, createRealTimeBarTick, + createTickByTick, ) from .protobuf_converters.market_data_converters import ( - createTickGenericData, + TickDeliveryProto, + createTickData, createTickOptionComputation, createTickParams, - createTickPriceData, - createTickSizeData, - createTickStringData, ) -from .protobuf_converters.trade_converter import ( +from .protobuf_converters.subscription_converters import createScannerDataList +from .protobuf_converters.trade_converters import ( createCommissionReport, createContract, createFill, @@ -148,8 +142,7 @@ createOrderStatus, createTradeFromOpenOrder, ) - -from .util import NO_VALID_ID, UNSET_DOUBLE, UNSET_INTEGER +from .util import NO_VALID_ID, UNSET_INTEGER from .wrapper import Wrapper @@ -258,11 +251,11 @@ class Decoder: MessageId.IN.HISTORICAL_TICKS: (HistoricalTicksProto, "historicalTicksProto"), MessageId.IN.HISTORICAL_TICKS_BID_ASK: ( HistoricalTicksBidAskProto, - "historicalTicksBidAskProto", + "historicalTicksProto", ), MessageId.IN.HISTORICAL_TICKS_LAST: ( HistoricalTicksLastProto, - "historicalTicksLastProto", + "historicalTicksProto", ), MessageId.IN.HISTOGRAM_DATA: (HistogramDataProto, "histogramDataProto"), MessageId.IN.HISTORICAL_SCHEDULE: ( @@ -271,10 +264,10 @@ class Decoder: ), MessageId.IN.REAL_TIME_BARS: (RealTimeBarTickProto, "realTimeBarTickProto"), MessageId.IN.TICK_REQ_PARAMS: (TickReqParamsProto, "tickReqParamsProto"), - MessageId.IN.TICK_PRICE: (TickPriceProto, "tickPriceProto"), - MessageId.IN.TICK_SIZE: (TickSizeProto, "tickSizeProto"), - MessageId.IN.TICK_GENERIC: (TickGenericProto, "tickGenericProto"), - MessageId.IN.TICK_STRING: (TickStringProto, "tickStringProto"), + MessageId.IN.TICK_PRICE: (TickPriceProto, "tickDeliveryProto"), + MessageId.IN.TICK_SIZE: (TickSizeProto, "tickDeliveryProto"), + MessageId.IN.TICK_GENERIC: (TickGenericProto, "tickDeliveryProto"), + MessageId.IN.TICK_STRING: (TickStringProto, "tickDeliveryProto"), MessageId.IN.TICK_OPTION_COMPUTATION: ( TickOptionComputationProto, "tickOptionComputationProto", @@ -416,7 +409,7 @@ def nextValidIdProto(self, msg: NextValidIdProto): def openOrderProto(self, msg: OpenOrderProto): trade, orderState = createTradeFromOpenOrder(msg) - if trade: + if trade and orderState: self.wrapper.openOrder(trade, orderState) return self.logger.error("Error processing order, %r", msg) @@ -474,42 +467,14 @@ def historicalDataProto(self, msg: HistoricalDataProto): def historicalDataProtoEnd(self, msg: HistoricalDataEndProto): self.wrapper.historicalDataEnd(msg.reqId, msg.startDateStr, msg.endDateStr) - def historicalTicksProto(self, msg: HistoricalTicksProto): + def historicalTicksProto(self, msg: HistoricalTicksProtoType): reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID isDone = msg.isDone if msg.HasField("isDone") else False - historicalTicks = [] - if msg.historicalTicks: - for historicalTickProto in msg.historicalTicks: - historicalTick = createHistoricalTick( - historicalTickProto, self.wrapper.defaults.timezone - ) - historicalTicks.append(historicalTick) + historicalTicks: list[HistoricalTickType] = createHistoricalTickShim( + msg, self.wrapper.defaults.timezone + ) self.wrapper.historicalTicks(reqId, historicalTicks, isDone) - def historicalTicksBidAskProto(self, msg: HistoricalTicksBidAskProto): - reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID - isDone = msg.isDone if msg.HasField("isDone") else False - historicalTicksBidAsk: list[HistoricalTickBidAsk] = [] - if msg.historicalTicksBidAsk: - for historicalTickProto in msg.historicalTicksBidAsk: - historicalTickBidAsk = createHistoricalTickBidAsk( - historicalTickProto, self.wrapper.defaults.timezone - ) - historicalTicksBidAsk.append(historicalTickBidAsk) - self.wrapper.historicalTicksBidAsk(reqId, historicalTicksBidAsk, isDone) - - def historicalTicksLastProto(self, msg: HistoricalTicksLastProto): - reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID - isDone = msg.isDone if msg.HasField("isDone") else False - historicalTicksLast: list[HistoricalTickLast] = [] - if msg.historicalTicksLast: - for historicalTickProto in msg.historicalTicksLast: - historicalTickLast = createHistoricalTickLast( - historicalTickProto, self.wrapper.defaults.timezone - ) - historicalTicksLast.append(historicalTickLast) - self.wrapper.historicalTicksLast(reqId, historicalTicksLast, isDone) - def histogramDataProto(self, msg: HistogramDataProto): histogram: list[HistogramData] = [] if msg.histogramDataEntries: @@ -529,7 +494,6 @@ def historicalScheduleProto(self, msg: HistoricalScheduleProto): def realTimeBarTickProto(self, msg: RealTimeBarTickProto): reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID - realTimeBarTick = createRealTimeBarTick(msg, self.wrapper.defaults.timezone) self.wrapper.realtimeBar(reqId, realTimeBarTick) @@ -540,23 +504,9 @@ def tickReqParamsProto(self, msg: TickReqParamsProto): tickParams = createTickParams(msg) self.wrapper.tickReqParams(tickParams.reqId, tickParams) - def tickPriceProto(self, msg: TickPriceProto): - tickPrice, tickSize = createTickPriceData(msg) - self.wrapper.priceSizeTick(tickPrice.reqId, tickPrice) - if tickSize.tickType != TickType.NOT_SET: - self.wrapper.tickSize(tickSize.reqId, tickSize) - - def tickSizeProto(self, msg: TickSizeProto): - tickSize = createTickSizeData(msg) - self.wrapper.tickSize(tickSize.reqId, tickSize) - - def tickGenericProto(self, msg: TickGenericProto): - tickGeneric = createTickGenericData(msg) - self.wrapper.tickGeneric(tickGeneric.reqId, tickGeneric) - - def tickStringProto(self, msg: TickStringProto): - tickString = createTickStringData(msg) - self.wrapper.tickString(tickString.reqId, tickString) + def tickDeliveryProto(self, msg: TickDeliveryProto): + tickData = createTickData(msg) + self.wrapper.tickerDelivery(msg.reqId, tickData) def tickOptionComputationProto(self, msg: TickOptionComputationProto): tick_computation = createTickOptionComputation(msg) @@ -568,31 +518,9 @@ def tickSnapshotEndProto(self, msg: TickSnapshotEndProto): def tickByTickDataProto(self, msg: TickByTickDataProto): reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID - tickType = msg.tickType if msg.HasField("tickType") else 0 - - if tickType == 0: - pass - elif tickType == 1 or tickType == 2: - # Last or AllLast - if msg.HasField("historicalTickLast"): - tick_last = createHistoricalTickLast( - msg.historicalTickLast, self.wrapper.defaults.timezone - ) - self.wrapper.tickByTickAllLast(reqId, tick_last) - elif tickType == 3: - # BidAsk - if msg.HasField("historicalTickBidAsk"): - tick_bid_ask = createHistoricalTickBidAsk( - msg.historicalTickBidAsk, self.wrapper.defaults.timezone - ) - self.wrapper.tickByTickBidAsk(reqId, tick_bid_ask) - elif tickType == 4: - # MidPoint - if msg.HasField("historicalTickMidPoint"): - tick_mid = createHistoricalTick( - msg.historicalTickMidPoint, self.wrapper.defaults.timezone - ) - self.wrapper.tickByTickMidPoint(reqId, tick_mid) + tickByTickData = createTickByTick(msg, self.wrapper.defaults.timezone) + if tickByTickData: + self.wrapper.tickByTick(reqId, tickByTickData) def fundamentatalDataProto(self, msg: FundamentalsDataProto): self.wrapper.fundamentalData(msg.reqId, msg.fundamentalData) @@ -608,23 +536,39 @@ def scannerDataProto(self, msg: ScannerDataProto): def pnlProto(self, msg: PnLProto): reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID - dailyPnL = msg.dailyPnL if msg.HasField("dailyPnL") else UNSET_DOUBLE + dailyPnL = ( + msg.dailyPnL if msg.HasField("dailyPnL") else self.wrapper.defaults.unset + ) unrealizedPnL = ( - msg.unrealizedPnL if msg.HasField("unrealizedPnL") else UNSET_DOUBLE + msg.unrealizedPnL + if msg.HasField("unrealizedPnL") + else self.wrapper.defaults.unset + ) + realizedPnL = ( + msg.realizedPnL + if msg.HasField("realizedPnL") + else self.wrapper.defaults.unset ) - realizedPnL = msg.realizedPnL if msg.HasField("realizedPnL") else UNSET_DOUBLE self.wrapper.pnl(reqId, dailyPnL, unrealizedPnL, realizedPnL) def pnlSingleProto(self, msg: PnLSingleProto): reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID position = int(msg.position) if msg.HasField("position") else UNSET_INTEGER - dailyPnL = msg.dailyPnL if msg.HasField("dailyPnL") else UNSET_DOUBLE + dailyPnL = ( + msg.dailyPnL if msg.HasField("dailyPnL") else self.wrapper.defaults.unset + ) unrealizedPnL = ( - msg.unrealizedPnL if msg.HasField("unrealizedPnL") else UNSET_DOUBLE + msg.unrealizedPnL + if msg.HasField("unrealizedPnL") + else self.wrapper.defaults.unset + ) + realizedPnL = ( + msg.realizedPnL + if msg.HasField("realizedPnL") + else self.wrapper.defaults.unset ) - realizedPnL = msg.realizedPnL if msg.HasField("realizedPnL") else UNSET_DOUBLE - value = msg.value if msg.HasField("value") else UNSET_DOUBLE + value = msg.value if msg.HasField("value") else self.wrapper.defaults.unset self.wrapper.pnlSingle( reqId, position, dailyPnL, unrealizedPnL, realizedPnL, value @@ -751,17 +695,6 @@ def familyCodes(self, fields): self.wrapper.familyCodes(familyCodes) - def smartComponents(self, fields): - _, reqId, n, *fields = fields - get = iter(fields).__next__ - - components = [ - SmartComponent(bitNumber=int(get()), exchange=get(), exchangeLetter=get()) - for _ in range(int(n)) - ] - - self.wrapper.smartComponents(int(reqId), components) - def mktDepthExchanges(self, fields): _, n, *fields = fields get = iter(fields).__next__ diff --git a/ib_async/ib.py b/ib_async/ib.py index d0831d60..ff6bc4d9 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -671,7 +671,11 @@ def realtimeBars(self) -> list[Union[BarDataList, RealTimeBarList]]: Get a list of all live updated bars. These can be 5 second realtime bars or live updated historical bars. """ - return list(self.wrapper.reqId2Subscriber.values()) + return [ + v + for v in self.wrapper.subscriptions.values() + if isinstance(v, (BarDataList, RealTimeBarList)) + ] def newsTicks(self) -> list[NewsTick]: """ @@ -1041,6 +1045,7 @@ def reqPnL(self, account: str, modelCode: str = "") -> PnL: pnl = PnL(account, modelCode) self.wrapper.Pnl.add(reqId, key, pnl) self.client.reqPnL(reqId, account, modelCode) + pnl.pnl_bus.connect(pnl._on_update) return pnl @@ -1055,8 +1060,11 @@ def cancelPnL(self, account, modelCode: str = ""): key = (account, modelCode) reqId = self.wrapper.Pnl.get_request_id(key) if reqId: - self.client.cancelPnL(reqId) + pnl = self.wrapper.Pnl.get_by_request_id(reqId) + if pnl: + pnl.pnl_bus.clear() self.wrapper.Pnl.remove_by_request_id(reqId) + self.client.cancelPnL(reqId) else: self._logger.error( "cancelPnL: No subscription for " @@ -1083,6 +1091,7 @@ def reqPnLSingle(self, account: str, modelCode: str, conId: int) -> PnLSingle: reqId = self.client.getReqId() pnlSingle = PnLSingle(account, modelCode, conId) self.wrapper.pnlSingles.add(reqId, key, pnlSingle) + pnlSingle.pnl_single_bus.connect(pnlSingle._on_update) self.client.reqPnLSingle(reqId, account, modelCode, conId) return pnlSingle @@ -1100,6 +1109,9 @@ def cancelPnLSingle(self, account: str, modelCode: str, conId: int): key = (account, modelCode, conId) reqId = self.wrapper.pnlSingles.get_request_id(key) if reqId: + pnlSingle = self.wrapper.pnlSingles.get_by_request_id(reqId) + if pnlSingle: + pnlSingle.pnl_single_bus.clear() self.client.cancelPnLSingle(reqId) self.wrapper.pnlSingles.remove_by_request_id(reqId) else: @@ -1451,7 +1463,7 @@ def reqMktData( mktDataOptions: Unknown """ reqId = self.client.getReqId() - ticker = self.wrapper.startTicker(reqId, contract, "mktData") + ticker = self.wrapper.startTicker(reqId, contract) self.client.reqMktData( reqId, contract, @@ -1474,7 +1486,7 @@ def cancelMktData(self, contract: Contract) -> bool: Returns False if 'contract' was not found. """ ticker = self.ticker(contract) - reqId = self.wrapper.endTicker(ticker, "mktData") if ticker else 0 + reqId = self.wrapper.endTicker(ticker) if ticker else None if reqId: self.client.cancelMktData(reqId) @@ -1504,7 +1516,7 @@ def reqTickByTickData( ignoreSize: Ignore bid/ask ticks that only update the size. """ reqId = self.client.getReqId() - ticker = self.wrapper.startTicker(reqId, contract, tickType) + ticker = self.wrapper.startTicker(reqId, contract) self.client.reqTickByTickData( reqId, contract, tickType, numberOfTicks, ignoreSize @@ -1512,7 +1524,7 @@ def reqTickByTickData( return ticker - def cancelTickByTickData(self, contract: Contract, tickType: str) -> bool: + def cancelTickByTickData(self, contract: Contract) -> bool: """ Unsubscribe from tick-by-tick data @@ -1524,7 +1536,7 @@ def cancelTickByTickData(self, contract: Contract, tickType: str) -> bool: Returns False if 'contract' was not found. """ ticker = self.ticker(contract) - reqId = self.wrapper.endTicker(ticker, tickType) if ticker else 0 + reqId = self.wrapper.endTicker(ticker) if ticker else None if reqId: self.client.cancelTickByTickData(reqId) @@ -1576,7 +1588,7 @@ def reqMktDepth( ``ticker.domTicks``. """ reqId = self.client.getReqId() - ticker = self.wrapper.startTicker(reqId, contract, "mktDepth") + ticker = self.wrapper.startTicker(reqId, contract) ticker.domBids.clear() ticker.domAsks.clear() ticker.domBidsDict.clear() @@ -1593,7 +1605,7 @@ def cancelMktDepth(self, contract: Contract, isSmartDepth=False): subscribe with. """ ticker = self.ticker(contract) - reqId = self.wrapper.endTicker(ticker, "mktDepth") if ticker else 0 + reqId = self.wrapper.endTicker(ticker) if ticker else None if ticker and reqId: self.client.cancelMktDepth(reqId, isSmartDepth) @@ -1826,6 +1838,9 @@ def exerciseOptions( exerciseQuantity: int, account: str, override: int, + manualOrderTime: str, + customerAccount: str, + professionalCustomer: bool, ): """ Exercise an options contract. @@ -1842,10 +1857,21 @@ def exerciseOptions( override: * 0 = no override * 1 = override the system's natural action + manualOrderTime:str - manual order time + customerAccount:str - customer account + professionalCustomer:bool - professional customer """ reqId = self.client.getReqId() self.client.exerciseOptions( - reqId, contract, exerciseAction, exerciseQuantity, account, override + reqId, + contract, + exerciseAction, + exerciseQuantity, + account, + override, + manualOrderTime, + customerAccount, + professionalCustomer, ) def reqNewsProviders(self) -> list[NewsProvider]: @@ -2189,8 +2215,8 @@ async def qualifyContractsAsync( elif len(detailsList) > 1: # BUG FIX: # - IBKR is returning EC _and_ FOP contracts for only FOP requests, - # which is clearly incorrect, so now if an input request has `secType` - # defined, we only return matching `secType` contracts. + # which is clearly incorrect, so now if an input request has + # `secType` defined, we only return matching `secType` contracts. if contract.secType: possibles = [ details.contract @@ -2198,12 +2224,13 @@ async def qualifyContractsAsync( if contract.secType == details.contract.secType # type: ignore ] - # if our match instrument type filter resolved to only _one_ matching - # contract, then we found a single usable result to add. + # if our match instrument type filter resolved to only _one_ + # matching contract, then we found a single usable result to add. if len(possibles) == 1: c = possibles[0] if contract.exchange == "SMART": - # Allow contracts to become more generic if SMART requested as input + # Allow contracts to become more generic if SMART requested + # as input c.exchange = contract.exchange # type: ignore util.dataclassUpdate(contract, c) @@ -2241,7 +2268,7 @@ async def reqTickersAsync( for contract in contracts: reqId = self.client.getReqId() reqIds.append(reqId) - ticker = self.wrapper.startTicker(reqId, contract, "snapshot") + ticker = self.wrapper.startTicker(reqId, contract) tickers.append(ticker) self.client.reqMktData(reqId, contract, "", True, regulatorySnapshot, []) @@ -2299,6 +2326,7 @@ def reqAccountUpdatesAsync(self, account: str = "") -> Awaitable[None]: return ( self.wrapper.response_bus.filter(lambda key, _: key == "accountValues") .take(1) + .pluck(1) .map(self._raise_if_error) ) diff --git a/ib_async/objects.py b/ib_async/objects.py index e508ee4f..924af079 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -326,20 +326,20 @@ class TickData: size: float -@dataclass(slots=True) +@dataclass(slots=True, frozen=True) class TickAttrib: canAutoExecute: bool = False pastLimit: bool = False preOpen: bool = False -@dataclass(slots=True) +@dataclass(slots=True, frozen=True) class TickAttribBidAsk: bidPastLow: bool = False askPastHigh: bool = False -@dataclass(slots=True) +@dataclass(slots=True, frozen=True) class TickAttribLast: pastLimit: bool = False unreported: bool = False @@ -383,6 +383,11 @@ class TickGenericData: value: float +TickDeliveryType: TypeAlias = ( + TickPriceData | TickSizeData | TickStringData | TickGenericData +) + + @dataclass(slots=True, frozen=True) class TickComputationData: """Data from a TickComputationProto message.""" @@ -428,19 +433,41 @@ class TickByTickMidPointData: midPoint: float -TickDataType: TypeAlias = ( - TickPriceData - | TickSizeData - | TickStringData - | TickGenericData - | TickByTickAllLastData - | TickByTickBidAskData - | TickByTickMidPointData - | TickComputationData +@dataclass(slots=True, frozen=True) +class HistoricalTick: + time: datetime + price: float + size: float + + +@dataclass(slots=True, frozen=True) +class HistoricalTickBidAsk: + time: datetime + tickAttribBidAsk: TickAttribBidAsk + priceBid: float + priceAsk: float + sizeBid: float + sizeAsk: float + + +@dataclass(slots=True, frozen=True) +class HistoricalTickLast: + time: datetime + tickAttribLast: TickAttribLast + price: float + size: float + exchange: str + specialConditions: str + + +HistoricalTickType: TypeAlias = ( + HistoricalTick | HistoricalTickBidAsk | HistoricalTickLast ) +TickDataType: TypeAlias = TickDeliveryType | HistoricalTickType | TickComputationData -@dataclass(slots=True) + +@dataclass(slots=True, frozen=True) class HistogramData: price: float = 0.0 count: int = 0 @@ -468,6 +495,10 @@ class PnL: dailyPnL: float = nan unrealizedPnL: float = nan realizedPnL: float = nan + pnl_bus: Event = field(init=False, repr=False) + + def __post_init__(self): + self.pnl_bus = Event("pnl_bus") def getKey(self): """return PnL key @@ -475,6 +506,11 @@ def getKey(self): """ return (self.account, self.modelCode) + def _on_update(self, dailyPnL: float, unrealizedPnL: float, realizedPnL: float): + self.dailyPnL = dailyPnL + self.unrealizedPnL = unrealizedPnL + self.realizedPnL = realizedPnL + @dataclass(slots=True) class TradeLogEntry: @@ -494,6 +530,10 @@ class PnLSingle: realizedPnL: float = nan position: int = 0 value: float = nan + pnl_single_bus: Event = field(init=False, repr=False) + + def __post_init__(self): + self.pnl_single_bus = Event("pnl_bus") def getKey(self): """return PnLSingle key @@ -501,6 +541,20 @@ def getKey(self): """ return (self.account, self.modelCode, self.conId) + def _on_update( + self, + pos: int, + dailyPnL: float, + unrealizedPnL: float, + realizedPnL: float, + value: float, + ): + self.position = pos + self.dailyPnL = dailyPnL + self.unrealizedPnL = unrealizedPnL + self.realizedPnL = realizedPnL + self.value = value + @dataclass(slots=True, frozen=True) class HistoricalSession: @@ -529,7 +583,8 @@ class WshEventData: totalLimit: int = UNSET_INTEGER -class AccountValue(NamedTuple): +@dataclass(slots=True, frozen=True) +class AccountValue: account: str tag: str value: str @@ -537,57 +592,6 @@ class AccountValue(NamedTuple): modelCode: str -@dataclass(slots=True) -class HistoricalTick: - time: datetime - price: float - size: float - - -@dataclass(slots=True) -class HistoricalTickBidAsk: - time: datetime - tickAttribBidAsk: TickAttribBidAsk - priceBid: float - priceAsk: float - sizeBid: float - sizeAsk: float - - -@dataclass(slots=True, frozen=True) -class HistoricalTickLast: - time: datetime - tickAttribLast: TickAttribLast - price: float - size: float - exchange: str - specialConditions: str - - -class TickByTickAllLast(NamedTuple): - tickType: int - time: datetime - price: float - size: float - tickAttribLast: TickAttribLast - exchange: str - specialConditions: str - - -class TickByTickBidAsk(NamedTuple): - time: datetime - bidPrice: float - askPrice: float - bidSize: float - askSize: float - tickAttribBidAsk: TickAttribBidAsk - - -class TickByTickMidPoint(NamedTuple): - time: datetime - midPoint: float - - class MktDepthData(NamedTuple): time: datetime position: int @@ -604,12 +608,14 @@ class DOMLevel(NamedTuple): marketMaker: str -class PriceIncrement(NamedTuple): +@dataclass(slots=True, frozen=True) +class PriceIncrement: lowEdge: float increment: float -class PortfolioItem(NamedTuple): +@dataclass(slots=True, frozen=True) +class PortfolioItem: contract: Contract position: float marketPrice: float @@ -620,7 +626,8 @@ class PortfolioItem(NamedTuple): account: str -class Position(NamedTuple): +@dataclass(slots=True, frozen=True) +class Position: account: str contract: Contract position: float @@ -807,7 +814,7 @@ def __init__(self, *args): def __eq__(self, other): return self is other - def _on_data(self, ib: "IB", data: ScanData): + def _on_data(self, ib: "IB", data: list[ScanData]): """Called on scanner data.""" rank = data[0].rank if 0 <= len(data) else None if rank == 0: @@ -836,7 +843,6 @@ class FundamentalRatios(DynamicObject): pass -@dataclass class IBDefaults: """A simple way to provide default values when populating API data.""" diff --git a/ib_async/order.py b/ib_async/order.py index 466edcc2..2ccb64cc 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -2,11 +2,11 @@ from __future__ import annotations -import dataclasses - from dataclasses import dataclass, field from decimal import Decimal -from typing import ClassVar, NamedTuple, TypeAlias +from enum import StrEnum +from typing import ClassVar, TypeAlias +from math import nan from eventkit import Event @@ -15,7 +15,25 @@ from .util import UNSET_DECIMAL, dataclassNonDefaults, UNSET_DOUBLE, UNSET_INTEGER -@dataclass +class OrderTIF(StrEnum): + """order time in force + https://www.ibkrguides.com/traderworkstation/order-types.htm + https://www.interactivebrokers.com/en/trading/ordertypes.php + """ + + DAY = "DAY" + GTC = "GTC" # good til cancelled + OPG = "OPG" # + GTD = "GTD" # good til date + IOC = "IOC" # Immediate or Cancel + FOK = "FOK" # fill or kill + GAT = "GAT" # good after time + OVERNIGHT = "OVERNIGHT" # overnight + OVERNIGHT_DAY = "OVERNIGHT + DAY" # overnight & day + AUC = "AUC" # at auction + + +@dataclass(slots=True) class Order: """ Order for trading contracts. @@ -29,7 +47,7 @@ class Order: clientId: int = 0 permId: int = 0 action: str = "" - totalQuantity: float = 0.0 + totalQuantity: float | Decimal = 0.0 orderType: str = "" lmtPrice: float | Decimal | None = UNSET_DOUBLE auxPrice: float | Decimal | None = UNSET_DOUBLE @@ -243,7 +261,7 @@ def __init__( ) -@dataclass +@dataclass(slots=True) class OrderStatus: """ Reference: @@ -252,18 +270,18 @@ class OrderStatus: orderId: int = 0 status: str = "" - filled: float = 0.0 - remaining: float = 0.0 - avgFillPrice: float = 0.0 + filled: float | Decimal = 0.0 + remaining: float | Decimal = 0.0 + avgFillPrice: float | Decimal = 0.0 permId: int = 0 parentId: int = 0 - lastFillPrice: float = 0.0 + lastFillPrice: float | Decimal = 0.0 clientId: int = 0 whyHeld: str = "" - mktCapPrice: float = 0.0 + mktCapPrice: float | Decimal = 0.0 @property - def total(self) -> float: + def total(self) -> float|Decimal: """Helper property to return the total size of this requested order.""" return self.filled + self.remaining @@ -296,7 +314,8 @@ def total(self) -> float: ] ) - # order hasn't triggered "live" yet (but it could become live and execute before we receive a notice) + # order hasn't triggered "live" yet (but it could become live and execute before we + # receive a notice) WaitingStates: ClassVar[frozenset[str]] = frozenset( [ "PendingSubmit", @@ -310,122 +329,57 @@ def total(self) -> float: [ "Submitted", # ValidationError can happen on submit or modify. - # If ValidationError happens on submit, the states go PreSubmitted -> ValidationError -> Submitted (if it can be ignored automatically), so order is still live. - # If ValidationError happens on modify, the update is just ValidationError with no new Submitted, so the previous order state remains active. + # If ValidationError happens on submit, the states go PreSubmitted -> + # ValidationError -> Submitted (if it can be ignored automatically), so + # order is still live. + # If ValidationError happens on modify, the update is just ValidationError + # with no new Submitted, so the previous order state remains active. "ValidationError", "ApiUpdate", ] ) -@dataclass +@dataclass(slots=True) class OrderState: status: str = "" - initMarginBefore: str = "" - maintMarginBefore: str = "" - equityWithLoanBefore: str = "" - initMarginChange: str = "" - maintMarginChange: str = "" - equityWithLoanChange: str = "" - initMarginAfter: str = "" - maintMarginAfter: str = "" - equityWithLoanAfter: str = "" - commission: float = UNSET_DOUBLE - minCommission: float = UNSET_DOUBLE - maxCommission: float = UNSET_DOUBLE + initMarginBefore: float | Decimal = nan + maintMarginBefore: float | Decimal = nan + equityWithLoanBefore: float | Decimal = nan + initMarginChange: float | Decimal = nan + maintMarginChange: float | Decimal = nan + equityWithLoanChange: float | Decimal = nan + initMarginAfter: float | Decimal = nan + maintMarginAfter: float | Decimal = nan + equityWithLoanAfter: float | Decimal = nan + commission: float | Decimal = nan + minCommission: float | Decimal = nan + maxCommission: float | Decimal = nan commissionCurrency: str = "" marginCurrency: str = "" - initMarginBeforeOutsideRTH: float = UNSET_DOUBLE # type: float - maintMarginBeforeOutsideRTH: float = UNSET_DOUBLE # type: float - equityWithLoanBeforeOutsideRTH: float = UNSET_DOUBLE # type: float - initMarginChangeOutsideRTH: float = UNSET_DOUBLE # type: float - maintMarginChangeOutsideRTH: float = UNSET_DOUBLE # type: float - equityWithLoanChangeOutsideRTH: float = UNSET_DOUBLE # type: float - initMarginAfterOutsideRTH: float = UNSET_DOUBLE # type: float - maintMarginAfterOutsideRTH: float = UNSET_DOUBLE # type: float - equityWithLoanAfterOutsideRTH: float = UNSET_DOUBLE # type: float - suggestedSize = UNSET_DECIMAL - rejectReason = "" - orderAllocations = None + initMarginBeforeOutsideRTH: float | Decimal = nan + maintMarginBeforeOutsideRTH: float | Decimal = nan + equityWithLoanBeforeOutsideRTH: float | Decimal = nan + initMarginChangeOutsideRTH: float | Decimal = nan + maintMarginChangeOutsideRTH: float | Decimal = nan + equityWithLoanChangeOutsideRTH: float | Decimal = nan + initMarginAfterOutsideRTH: float | Decimal = nan + maintMarginAfterOutsideRTH: float | Decimal = nan + equityWithLoanAfterOutsideRTH: float | Decimal = nan + suggestedSize: float | Decimal = nan + rejectReason: str = "" + orderAllocations: list["OrderAllocation"] | None = None warningText: str = "" completedTime: str = "" completedStatus: str = "" - def transform(self, transformer): - """Convert the numeric values of this OrderState into a new OrderState transformed by 'using'""" - return dataclasses.replace( - self, - initMarginBefore=transformer(self.initMarginBefore), - maintMarginBefore=transformer(self.maintMarginBefore), - equityWithLoanBefore=transformer(self.equityWithLoanBefore), - initMarginChange=transformer(self.initMarginChange), - maintMarginChange=transformer(self.maintMarginChange), - equityWithLoanChange=transformer(self.equityWithLoanChange), - initMarginAfter=transformer(self.initMarginAfter), - maintMarginAfter=transformer(self.maintMarginAfter), - equityWithLoanAfter=transformer(self.equityWithLoanAfter), - commission=transformer(self.commission), - minCommission=transformer(self.minCommission), - maxCommission=transformer(self.maxCommission), - ) - - def numeric(self, digits: int = 2) -> OrderStateNumeric: - """Return a new OrderState with the current values values to floats instead of strings as returned from IBKR directly.""" - - def floatOrNone(what, precision) -> float | None: - """Attempt to convert input to a float, but if we fail (value is just empty string) return None""" - try: - # convert - floated = float(what) - - # if the conversion is IBKR speak for "this value is not set" then give us None - if floated == UNSET_DOUBLE: - return None - - # else, round to the requested precision - return round(floated, precision) - except Exception as _: - # initial conversion failed so just return None in its place - return None - - return self.transform(lambda x: floatOrNone(x, digits)) - - def formatted(self, digits: int = 2): - """Return a new OrderState with the current values as formatted strings.""" - return self.numeric(8).transform( - # 300000.21 -> 300,000.21 - # 0.0 -> 0.00 - # 431.342000000001 -> 431.34 - # Note: we need 'is not None' here because 'x=0' is a valid numeric input too - lambda x: f"{x:,.{digits}f}" if x is not None else None - ) - - -@dataclass -class OrderStateNumeric(OrderState): - """Just a type helper for mypy to check against if you convert OrderState to .numeric(). - - Usage: - - state_numeric: OrderStateNumeric = state.numeric(digits=2)""" - initMarginBefore: float = float("nan") # type: ignore - maintMarginBefore: float = float("nan") # type: ignore - equityWithLoanBefore: float = float("nan") # type: ignore - initMarginChange: float = float("nan") # type: ignore - maintMarginChange: float = float("nan") # type: ignore - equityWithLoanChange: float = float("nan") # type: ignore - initMarginAfter: float = float("nan") # type: ignore - maintMarginAfter: float = float("nan") # type: ignore - equityWithLoanAfter: float = float("nan") # type: ignore - - -@dataclass +@dataclass(slots=True) class OrderComboLeg: price: float | Decimal = UNSET_DOUBLE -@dataclass +@dataclass(slots=True) class Trade: """ Trade keeps track of an order, its status and all its fills. @@ -448,6 +402,15 @@ class Trade: log: list[TradeLogEntry] = field(default_factory=list) advancedError: str = "" + # instance Events must be declared as fields so they exist in __slots__ + statusEvent: Event = field(init=False, repr=False, compare=False) + modifyEvent: Event = field(init=False, repr=False, compare=False) + fillEvent: Event = field(init=False, repr=False, compare=False) + commissionReportEvent: Event = field(init=False, repr=False, compare=False) + filledEvent: Event = field(init=False, repr=False, compare=False) + cancelEvent: Event = field(init=False, repr=False, compare=False) + cancelledEvent: Event = field(init=False, repr=False, compare=False) + # TODO: replace these with an enum? events: ClassVar = ( "statusEvent", @@ -498,14 +461,18 @@ def remaining(self) -> float: return float(self.order.totalQuantity) - self.filled() -class BracketOrder(NamedTuple): +@dataclass(slots=True) +class BracketOrder: parent: Order takeProfit: Order stopLoss: Order -@dataclass +@dataclass(slots=True) class OrderCondition: + condType: ClassVar[int] + conjunction: str = "a" + @staticmethod def createClass(condType): d = { @@ -527,10 +494,9 @@ def Or(self): return self -@dataclass +@dataclass(slots=True) class PriceCondition(OrderCondition): - condType: int = 1 - conjunction: str = "a" + condType: ClassVar[int] = 1 isMore: bool = True price: float = 0.0 conId: int = 0 @@ -538,45 +504,40 @@ class PriceCondition(OrderCondition): triggerMethod: int = 0 -@dataclass +@dataclass(slots=True) class TimeCondition(OrderCondition): - condType: int = 3 - conjunction: str = "a" + condType: ClassVar[int] = 3 isMore: bool = True time: str = "" -@dataclass +@dataclass(slots=True) class MarginCondition(OrderCondition): - condType: int = 4 - conjunction: str = "a" + condType: ClassVar[int] = 4 isMore: bool = True percent: int = 0 -@dataclass +@dataclass(slots=True) class ExecutionCondition(OrderCondition): - condType: int = 5 - conjunction: str = "a" + condType: ClassVar[int] = 5 secType: str = "" exch: str = "" symbol: str = "" -@dataclass +@dataclass(slots=True) class VolumeCondition(OrderCondition): - condType: int = 6 - conjunction: str = "a" + condType: ClassVar[int] = 6 isMore: bool = True volume: int = 0 conId: int = 0 exch: str = "" -@dataclass +@dataclass(slots=True) class PercentChangeCondition(OrderCondition): - condType: int = 7 - conjunction: str = "a" + condType: ClassVar[int] = 7 isMore: bool = True changePercent: float = 0.0 conId: int = 0 @@ -593,22 +554,22 @@ class PercentChangeCondition(OrderCondition): ) -@dataclass +@dataclass(slots=True) class OrderAllocation: """ Reference: https://ibkrcampus.com/campus/ibkr-api-page/twsapi-ref/#order-static-pub-func """ - account = "" - position: Decimal = UNSET_DECIMAL - positionDesired: Decimal = UNSET_DECIMAL - positionAfter: Decimal = UNSET_DECIMAL - desiredAllocQty: Decimal = UNSET_DECIMAL - allowedAllocQty: Decimal = UNSET_DECIMAL - isMonetary = False + account: str = field(default="") + position: Decimal = field(default=UNSET_DECIMAL) + positionDesired: Decimal = field(default=UNSET_DECIMAL) + positionAfter: Decimal = field(default=UNSET_DECIMAL) + desiredAllocQty: Decimal = field(default=UNSET_DECIMAL) + allowedAllocQty: Decimal = field(default=UNSET_DECIMAL) + isMonetary: bool = field(default=False) -@dataclass +@dataclass(slots=True, frozen=True) class OrderCancel: """ Reference https://ibkrcampus.com/campus/ibkr-api-page/twsapi-ref/#orderallocation-ref @@ -624,6 +585,6 @@ class OrderCancel: signify if an order is manual (1) or automated (0). """ - manualOrderCancelTime: str = "" - extOperator: str = "" - manualOrderIndicator: int = UNSET_INTEGER + manualOrderCancelTime: str = field(default="") + extOperator: str = field(default="") + manualOrderIndicator: int = field(default=UNSET_INTEGER) diff --git a/ib_async/protobuf_converters/base_converters.py b/ib_async/protobuf_converters/base_converters.py new file mode 100644 index 00000000..e27823c0 --- /dev/null +++ b/ib_async/protobuf_converters/base_converters.py @@ -0,0 +1,18 @@ +"""Base converters for protobuf messages.""" + +from typing import List +from ..contract import TagValue + + +class ClientException(Exception): + def __init__(self, code, message, text): + super().__init__(f"Client request error: {code}: {message}, {text}") + self.code = code + self.message = message + self.text = text + + +def fillTagValueList(tagValueList: List[TagValue], orderProtoMap: dict): + if tagValueList is not None and tagValueList: + for tagValue in tagValueList: + orderProtoMap[tagValue.tag] = tagValue.value diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py index 78466ae5..73f31eac 100644 --- a/ib_async/protobuf_converters/contract_converters.py +++ b/ib_async/protobuf_converters/contract_converters.py @@ -498,9 +498,8 @@ def createComboLegProtoList( comboLegProtoList = [] if comboLegs: for i, comboLeg in enumerate(comboLegs): - perLegPrice = UNSET_DOUBLE if orderComboLegs and i < len(orderComboLegs): - perLegPrice: float = orderComboLegs[i].price + perLegPrice = float(orderComboLegs[i].price) comboLegProto = createComboLegProto(comboLeg, perLegPrice) if comboLegProto is not None: comboLegProtoList.append(comboLegProto) diff --git a/ib_async/protobuf_converters/historical_data_converters.py b/ib_async/protobuf_converters/historical_data_converters.py index 75403981..f7b3e2a3 100644 --- a/ib_async/protobuf_converters/historical_data_converters.py +++ b/ib_async/protobuf_converters/historical_data_converters.py @@ -1,6 +1,7 @@ """Historical data protobuf converters""" from datetime import tzinfo +from typing import TypeAlias from ..contract import Contract, TagValue from ..objects import ( @@ -11,6 +12,7 @@ HistoricalTick, HistoricalTickBidAsk, HistoricalTickLast, + HistoricalTickType, IBDefaults, RealTimeBar, TickAttribBidAsk, @@ -47,6 +49,13 @@ from ..protobuf.HistoricalTickLast_pb2 import ( HistoricalTickLast as HistoricalTickLastProto, ) +from ..protobuf.HistoricalTicks_pb2 import HistoricalTicks as HistoricalTicksProto +from ..protobuf.HistoricalTicksBidAsk_pb2 import ( + HistoricalTicksBidAsk as HistoricalTicksBidAskProto, +) +from ..protobuf.HistoricalTicksLast_pb2 import ( + HistoricalTicksLast as HistoricalTicksLastProto, +) from ..protobuf.HistoricalTicksRequest_pb2 import ( HistoricalTicksRequest as HistoricalTicksRequestProto, ) @@ -54,6 +63,7 @@ RealTimeBarsRequest as RealTimeBarsRequestProto, ) from ..protobuf.RealTimeBarTick_pb2 import RealTimeBarTick as RealTimeBarTickProto +from ..protobuf.TickByTickData_pb2 import TickByTickData as TickByTickDataProto from ..util import ( EPOCH, UNSET_DOUBLE, @@ -62,6 +72,7 @@ parseIBTimeStamp, ) from .contract_converters import createContractProto +from .base_converters import ClientException, fillTagValueList def createHeadTimestampRequestProto( @@ -82,12 +93,6 @@ def createHeadTimestampRequestProto( return headTimestampRequestProto -def fillTagValueList(tagValueList: list[TagValue], orderProtoMap: dict): - if tagValueList is not None and tagValueList: - for tagValue in tagValueList: - orderProtoMap[tagValue.tag] = tagValue.value - - def createHistoricalDataRequestProto( reqId: int, contract: Contract, @@ -278,6 +283,54 @@ def createHistoricalTickLast( return historicalTickLast +HistoricalTicksProtoType: TypeAlias = ( + HistoricalTicksProto | HistoricalTicksBidAskProto | HistoricalTicksLastProto +) + + +def createHistoricalTickShim( + historicalTicksProto: HistoricalTicksProtoType, tz: tzinfo +) -> list[HistoricalTickType]: + historicalTicks: list[HistoricalTickType] = [] + if isinstance(historicalTicksProto, HistoricalTicksProto): + for tick_proto in historicalTicksProto.historicalTicks: + historicalTicks.append(createHistoricalTick(tick_proto, tz)) + elif isinstance(historicalTicksProto, HistoricalTicksBidAskProto): + for tick_proto in historicalTicksProto.historicalTicksBidAsk: + historicalTicks.append(createHistoricalTickBidAsk(tick_proto, tz)) + elif isinstance(historicalTicksProto, HistoricalTicksLastProto): + for tick_proto in historicalTicksProto.historicalTicksLast: + historicalTicks.append(createHistoricalTickLast(tick_proto, tz)) + else: + raise ClientException(575, "Unknown historical ticks type", "") + return historicalTicks + + +def createTickByTick( + tickByTickData: TickByTickDataProto, tz: tzinfo +) -> HistoricalTickType|None: + tickType = tickByTickData.tickType if tickByTickData.HasField("tickType") else 0 + if tickType == 0: + raise ValueError("%s: Invalid tick type: %r",__name__,tickByTickData) + elif tickType == 1 or tickType == 2: + # Last or AllLast + if tickByTickData.HasField("historicalTickLast"): + tick_last = createHistoricalTickLast(tickByTickData.historicalTickLast, tz) + return tick_last + elif tickType == 3: + # BidAsk + if tickByTickData.HasField("historicalTickBidAsk"): + tick_bid_ask = createHistoricalTickBidAsk( + tickByTickData.historicalTickBidAsk, tz + ) + return tick_bid_ask + elif tickType == 4: + # MidPoint + if tickByTickData.HasField("historicalTickMidPoint"): + tick_mid = createHistoricalTick(tickByTickData.historicalTickMidPoint, tz) + return tick_mid + return None + def createHistogramDataRequestProto( reqId: int, contract: Contract, useRTH: bool, timePeriod: str ) -> HistogramDataRequestProto: @@ -297,12 +350,17 @@ def createHistogramDataRequestProto( def createHistogramDataEntry( histogramDataEntryProto: HistogramDataEntryProto, ) -> HistogramData: - histogramData = HistogramData() - if histogramDataEntryProto.HasField("price"): - histogramData.price = histogramDataEntryProto.price - if histogramDataEntryProto.HasField("size"): - histogramData.count = int(histogramDataEntryProto.size) - return histogramData + price = ( + histogramDataEntryProto.price + if histogramDataEntryProto.HasField("price") + else 0 + ) + count = ( + int(histogramDataEntryProto.size) + if histogramDataEntryProto.HasField("size") + else 0 + ) + return HistogramData(price, count) def createHistoricalSchedule( diff --git a/ib_async/protobuf_converters/market_data_converters.py b/ib_async/protobuf_converters/market_data_converters.py index fa3e79db..bc2b07f4 100644 --- a/ib_async/protobuf_converters/market_data_converters.py +++ b/ib_async/protobuf_converters/market_data_converters.py @@ -2,12 +2,14 @@ Market data protobuf converters """ +from typing import Any, Callable, TypeAlias from ..objects import ( Contract, OptionComputation, TagValue, TickAttrib, TickComputationData, + TickDeliveryType, TickGenericData, TickParams, TickPriceData, @@ -123,7 +125,7 @@ def createTickParams(msg: TickReqParamsProto) -> TickParams: return tickParams -def createTickPriceData(msg: TickPriceProto) -> tuple[TickPriceData, TickSizeData]: +def createTickPriceData(msg: TickPriceProto) -> TickPriceData: """Create a TickPriceData object from a TickPriceProto message.""" if msg.HasField("reqId"): reqId = msg.reqId @@ -147,22 +149,7 @@ def createTickPriceData(msg: TickPriceProto) -> tuple[TickPriceData, TickSizeDat tickPrice = TickPriceData(reqId, tickType, price, size, attribs) - sizeTickType = TickType.NOT_SET - if TickType.BID == tickType: - sizeTickType = TickType.BID_SIZE - elif TickType.ASK == tickType: - sizeTickType = TickType.ASK_SIZE - elif TickType.LAST == tickType: - sizeTickType = TickType.LAST_SIZE - elif TickType.DELAYED_BID == tickType: - sizeTickType = TickType.DELAYED_BID_SIZE - elif TickType.DELAYED_ASK == tickType: - sizeTickType = TickType.DELAYED_ASK_SIZE - elif TickType.DELAYED_LAST == tickType: - sizeTickType = TickType.DELAYED_LAST_SIZE - - tickSize = TickSizeData(reqId, sizeTickType, size) - return tickPrice, tickSize + return tickPrice def createTickSizeData(msg: TickSizeProto) -> TickSizeData: @@ -200,6 +187,30 @@ def createTickGenericData(msg: TickGenericProto) -> TickGenericData: tickGeneric = TickGenericData(reqId, tickType, value) return tickGeneric +TickDeliveryProto:TypeAlias = ( + TickPriceProto + | TickSizeProto + | TickStringProto + | TickGenericProto +) + + +def createTickData(msg: TickDeliveryProto) -> TickDeliveryType: + delivery_map: dict[type[TickDeliveryProto], Callable[[Any], TickDeliveryType]] = { + TickPriceProto: createTickPriceData, + TickSizeProto: createTickSizeData, + TickStringProto: createTickStringData, + TickGenericProto: createTickGenericData, + } + + create_method = delivery_map.get(type(msg)) + + if create_method is None: + # runtime error + raise ValueError(f"createTickData - no converter found for tick delivery type: {type(msg)}") + + return create_method(msg) + def createTickOptionComputation(msg: TickOptionComputationProto) -> TickComputationData: """Create a OptionComputation object from a TickOptionCompuationProto message.""" diff --git a/ib_async/protobuf_converters/trade_converter.py b/ib_async/protobuf_converters/trade_converters.py similarity index 91% rename from ib_async/protobuf_converters/trade_converter.py rename to ib_async/protobuf_converters/trade_converters.py index 3f7f1a6c..ca5b19a5 100644 --- a/ib_async/protobuf_converters/trade_converter.py +++ b/ib_async/protobuf_converters/trade_converters.py @@ -2,6 +2,7 @@ Converters for trade-related Protobuf messages. """ +import datetime as dt from decimal import Decimal from ib_async.contract import Contract @@ -32,12 +33,12 @@ VolumeCondition, ) from ib_async.util import ( - UNSET_INTEGER, UNSET_DOUBLE, - decimalMaxString, + UNSET_INTEGER, getEnumTypeFromString, isValidIntValue, parseIBDatetime, + quantize_decimals ) from ..protobuf.CancelOrderRequest_pb2 import ( @@ -65,21 +66,13 @@ from ..protobuf.OrderCondition_pb2 import OrderCondition as OrderConditionProto from ..protobuf.OrderState_pb2 import OrderState as OrderStateProto from ..protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto -from ..protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto from ..protobuf.PlaceOrderRequest_pb2 import PlaceOrderRequest as PlaceOrderRequestProto -from ..protobuf_converters.contract_converters import ( +from ..protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto +from .base_converters import ClientException, fillTagValueList +from .contract_converters import ( createContract, createContractProto, ) -from ..protobuf_converters.historical_data_converters import fillTagValueList - - -class ClientException(Exception): - def __init__(self, code, message, text): - super().__init__(f"Client request error: {code}: {message}, {text}") - self.code = code - self.message = message - self.text = text def createPlaceOrderRequestProto( @@ -108,14 +101,14 @@ def createOrderProto(order: Order) -> OrderProto: if order.action: orderProto.action = order.action if order.totalQuantity != UNSET_DOUBLE: - orderProto.totalQuantity = decimalMaxString(order.totalQuantity) + orderProto.totalQuantity = str(order.totalQuantity) if isValidIntValue(order.displaySize): orderProto.displaySize = order.displaySize if order.orderType: orderProto.orderType = order.orderType - if order.lmtPrice != UNSET_DOUBLE: + if order.lmtPrice != UNSET_DOUBLE and order.lmtPrice is not None: orderProto.lmtPrice = float(order.lmtPrice) - if order.auxPrice != UNSET_DOUBLE: + if order.auxPrice != UNSET_DOUBLE and order.auxPrice is not None: orderProto.auxPrice = float(order.auxPrice) if order.tif: orderProto.tif = order.tif @@ -287,18 +280,24 @@ def createOrderProto(order: Order) -> OrderProto: orderProto.referenceExchangeId = order.referenceExchangeId if order.adjustedOrderType: orderProto.adjustedOrderType = order.adjustedOrderType - if order.triggerPrice != UNSET_DOUBLE: - orderProto.triggerPrice = order.triggerPrice - if order.adjustedStopPrice != UNSET_DOUBLE: - orderProto.adjustedStopPrice = order.adjustedStopPrice - if order.adjustedStopLimitPrice != UNSET_DOUBLE: - orderProto.adjustedStopLimitPrice = order.adjustedStopLimitPrice - if order.adjustedTrailingAmount != UNSET_DOUBLE: - orderProto.adjustedTrailingAmount = order.adjustedTrailingAmount + if order.triggerPrice != UNSET_DOUBLE and order.triggerPrice is not None: + orderProto.triggerPrice = float(order.triggerPrice) + if order.adjustedStopPrice != UNSET_DOUBLE and order.adjustedStopPrice is not None: + orderProto.adjustedStopPrice = float(order.adjustedStopPrice) + if ( + order.adjustedStopLimitPrice != UNSET_DOUBLE + and order.adjustedStopLimitPrice is not None + ): + orderProto.adjustedStopLimitPrice = float(order.adjustedStopLimitPrice) # type: ignore[assignment] + if ( + order.adjustedTrailingAmount != UNSET_DOUBLE + and order.adjustedTrailingAmount is not None + ): + orderProto.adjustedTrailingAmount = float(order.adjustedTrailingAmount) # type: ignore[assignment] if isValidIntValue(order.adjustableTrailingUnit): orderProto.adjustableTrailingUnit = order.adjustableTrailingUnit - if order.lmtPriceOffset != UNSET_DOUBLE: - orderProto.lmtPriceOffset = order.lmtPriceOffset + if order.lmtPriceOffset != UNSET_DOUBLE and order.lmtPriceOffset is not None: + orderProto.lmtPriceOffset = float(order.lmtPriceOffset) # type: ignore[assignment] orderConditionList = createConditionsProto(order) if orderConditionList is not None and orderConditionList: @@ -318,7 +317,7 @@ def createOrderProto(order: Order) -> OrderProto: orderProto.softDollarTier.CopyFrom(softDollarTier) if order.cashQty != UNSET_DOUBLE: - orderProto.cashQty = order.cashQty + orderProto.cashQty = float(order.cashQty) if order.mifid2DecisionMaker: orderProto.mifid2DecisionMaker = order.mifid2DecisionMaker if order.mifid2DecisionAlgo: @@ -348,11 +347,11 @@ def createOrderProto(order: Order) -> OrderProto: if isValidIntValue(order.minCompeteSize): orderProto.minCompeteSize = order.minCompeteSize if order.competeAgainstBestOffset != UNSET_DOUBLE: - orderProto.competeAgainstBestOffset = order.competeAgainstBestOffset + orderProto.competeAgainstBestOffset = float(order.competeAgainstBestOffset) if order.midOffsetAtWhole != UNSET_DOUBLE: - orderProto.midOffsetAtWhole = order.midOffsetAtWhole + orderProto.midOffsetAtWhole = float(order.midOffsetAtWhole) if order.midOffsetAtHalf != UNSET_DOUBLE: - orderProto.midOffsetAtHalf = order.midOffsetAtHalf + orderProto.midOffsetAtHalf = float(order.midOffsetAtHalf) if order.customerAccount: orderProto.customerAccount = order.customerAccount if order.professionalCustomer: @@ -378,19 +377,18 @@ def createConditionsProto(order: Order) -> list[OrderConditionProto]: try: if order.conditions is not None and order.conditions: for orderCondition in order.conditions: - conditionType = getattr(orderCondition, "condType", None) - - if PriceCondition.condType == conditionType: + orderConditionProto = None + if isinstance(orderCondition, PriceCondition): orderConditionProto = createPriceConditionProto(orderCondition) - elif TimeCondition.condType == conditionType: + elif isinstance(orderCondition, TimeCondition): orderConditionProto = createTimeConditionProto(orderCondition) - elif MarginCondition.condType == conditionType: + elif isinstance(orderCondition, MarginCondition): orderConditionProto = createMarginConditionProto(orderCondition) - elif ExecutionCondition.condType == conditionType: + elif isinstance(orderCondition, ExecutionCondition): orderConditionProto = createExecutionConditionProto(orderCondition) - elif VolumeCondition.condType == conditionType: + elif isinstance(orderCondition, VolumeCondition): orderConditionProto = createVolumeConditionProto(orderCondition) - elif PercentChangeCondition.condType == conditionType: + elif isinstance(orderCondition, PercentChangeCondition): orderConditionProto = createPercentChangeConditionProto( orderCondition ) @@ -535,7 +533,7 @@ def createSoftDollarTierProto(order: Order) -> SoftDollarTierProto: softDollarTierProto.displayName = tier.displayName return softDollarTierProto - +@quantize_decimals() def createOrder( orderId: int, contractProto: ContractProto, orderProto: OrderProto ) -> Order: @@ -763,7 +761,7 @@ def createOrder( if orderProto.HasField("discretionaryUpToLimitPrice"): order.discretionaryUpToLimitPrice = orderProto.discretionaryUpToLimitPrice if orderProto.HasField("usePriceMgmtAlgo"): - order.usePriceMgmtAlgo = orderProto.usePriceMgmtAlgo + order.usePriceMgmtAlgo = bool(orderProto.usePriceMgmtAlgo) if orderProto.HasField("duration"): order.duration = orderProto.duration if orderProto.HasField("postToAts"): @@ -824,7 +822,7 @@ def createOrderConditions(orderProto: OrderProto) -> list[OrderConditionType]: orderConditionProto.type if orderConditionProto.HasField("type") else 0 ) - condition: OrderCondition | None = None + condition: OrderConditionType | None = None if PriceCondition.condType == conditionType: condition = createPriceCondition(orderConditionProto) elif TimeCondition.condType == conditionType: @@ -858,7 +856,17 @@ def setOperatorConditionFields( ): setConditionFields(orderConditionProto, operatorCondition) if orderConditionProto.HasField("isMore"): - operatorCondition.isMore = orderConditionProto.isMore + if isinstance( + operatorCondition, + ( + PriceCondition, + TimeCondition, + MarginCondition, + VolumeCondition, + PercentChangeCondition, + ), + ): + operatorCondition.isMore = orderConditionProto.isMore def setContractConditionFields( @@ -866,9 +874,22 @@ def setContractConditionFields( ): setOperatorConditionFields(orderConditionProto, contractCondition) if orderConditionProto.HasField("conId"): - contractCondition.conId = orderConditionProto.conId + if isinstance( + contractCondition, + (PriceCondition, VolumeCondition, PercentChangeCondition), + ): + contractCondition.conId = orderConditionProto.conId if orderConditionProto.HasField("exchange"): - contractCondition.exch = orderConditionProto.exchange + if isinstance( + contractCondition, + ( + PriceCondition, + ExecutionCondition, + VolumeCondition, + PercentChangeCondition, + ), + ): + contractCondition.exch = orderConditionProto.exchange def createPriceCondition(orderConditionProto: OrderConditionProto) -> PriceCondition: @@ -968,45 +989,41 @@ def createTagValueList(protoMap: dict[str, str]) -> list[TagValue]: tagValueList.append(tagValue) return tagValueList - +@quantize_decimals() def createOrderState(orderStateProto: OrderStateProto) -> OrderState: orderState = OrderState() if orderStateProto.HasField("status"): orderState.status = orderStateProto.status if orderStateProto.HasField("initMarginBefore"): - orderState.initMarginBefore = str(orderStateProto.initMarginBefore) + orderState.initMarginBefore = Decimal(orderStateProto.initMarginBefore) if orderStateProto.HasField("maintMarginBefore"): - orderState.maintMarginBefore = decimalMaxString( - orderStateProto.maintMarginBefore - ) + orderState.maintMarginBefore = Decimal(orderStateProto.maintMarginBefore) if orderStateProto.HasField("equityWithLoanBefore"): - orderState.equityWithLoanBefore = decimalMaxString( + orderState.equityWithLoanBefore = Decimal( orderStateProto.equityWithLoanBefore ) if orderStateProto.HasField("initMarginChange"): - orderState.initMarginChange = decimalMaxString(orderStateProto.initMarginChange) + orderState.initMarginChange = Decimal(orderStateProto.initMarginChange) if orderStateProto.HasField("maintMarginChange"): - orderState.maintMarginChange = decimalMaxString( - orderStateProto.maintMarginChange - ) + orderState.maintMarginChange = Decimal(orderStateProto.maintMarginChange) if orderStateProto.HasField("equityWithLoanChange"): - orderState.equityWithLoanChange = decimalMaxString( + orderState.equityWithLoanChange = Decimal( orderStateProto.equityWithLoanChange ) if orderStateProto.HasField("initMarginAfter"): - orderState.initMarginAfter = decimalMaxString(orderStateProto.initMarginAfter) + orderState.initMarginAfter = Decimal(orderStateProto.initMarginAfter) if orderStateProto.HasField("maintMarginAfter"): - orderState.maintMarginAfter = decimalMaxString(orderStateProto.maintMarginAfter) + orderState.maintMarginAfter = Decimal(orderStateProto.maintMarginAfter) if orderStateProto.HasField("equityWithLoanAfter"): - orderState.equityWithLoanAfter = decimalMaxString( + orderState.equityWithLoanAfter = Decimal( orderStateProto.equityWithLoanAfter ) if orderStateProto.HasField("commissionAndFees"): - orderState.commission = orderStateProto.commissionAndFees + orderState.commission = Decimal(orderStateProto.commissionAndFees) if orderStateProto.HasField("minCommissionAndFees"): - orderState.minCommission = orderStateProto.minCommissionAndFees + orderState.minCommission = Decimal(orderStateProto.minCommissionAndFees) if orderStateProto.HasField("maxCommissionAndFees"): - orderState.maxCommission = orderStateProto.maxCommissionAndFees + orderState.maxCommission = Decimal(orderStateProto.maxCommissionAndFees) if orderStateProto.HasField("commissionAndFeesCurrency"): orderState.commissionCurrency = orderStateProto.commissionAndFeesCurrency if orderStateProto.HasField("warningText"): @@ -1014,39 +1031,35 @@ def createOrderState(orderStateProto: OrderStateProto) -> OrderState: if orderStateProto.HasField("marginCurrency"): orderState.marginCurrency = orderStateProto.marginCurrency if orderStateProto.HasField("initMarginBeforeOutsideRTH"): - orderState.initMarginBeforeOutsideRTH = ( + orderState.initMarginBeforeOutsideRTH = Decimal( orderStateProto.initMarginBeforeOutsideRTH ) if orderStateProto.HasField("maintMarginBeforeOutsideRTH"): - orderState.maintMarginBeforeOutsideRTH = ( + orderState.maintMarginBeforeOutsideRTH = Decimal( orderStateProto.maintMarginBeforeOutsideRTH ) if orderStateProto.HasField("equityWithLoanBeforeOutsideRTH"): - orderState.equityWithLoanBeforeOutsideRTH = ( + orderState.equityWithLoanBeforeOutsideRTH = Decimal( orderStateProto.equityWithLoanBeforeOutsideRTH ) if orderStateProto.HasField("initMarginChangeOutsideRTH"): - orderState.initMarginChangeOutsideRTH = ( + orderState.initMarginChangeOutsideRTH = Decimal( orderStateProto.initMarginChangeOutsideRTH ) if orderStateProto.HasField("maintMarginChangeOutsideRTH"): - orderState.maintMarginChangeOutsideRTH = ( + orderState.maintMarginChangeOutsideRTH = Decimal( orderStateProto.maintMarginChangeOutsideRTH ) if orderStateProto.HasField("equityWithLoanChangeOutsideRTH"): - orderState.equityWithLoanChangeOutsideRTH = ( + orderState.equityWithLoanChangeOutsideRTH = Decimal( orderStateProto.equityWithLoanChangeOutsideRTH ) if orderStateProto.HasField("initMarginAfterOutsideRTH"): - orderState.initMarginAfterOutsideRTH = orderStateProto.initMarginAfterOutsideRTH + orderState.initMarginAfterOutsideRTH = Decimal(orderStateProto.initMarginAfterOutsideRTH) if orderStateProto.HasField("maintMarginAfterOutsideRTH"): - orderState.maintMarginAfterOutsideRTH = ( - orderStateProto.maintMarginAfterOutsideRTH - ) + orderState.maintMarginAfterOutsideRTH = Decimal(orderStateProto.maintMarginAfterOutsideRTH) if orderStateProto.HasField("equityWithLoanAfterOutsideRTH"): - orderState.equityWithLoanAfterOutsideRTH = ( - orderStateProto.equityWithLoanAfterOutsideRTH - ) + orderState.equityWithLoanAfterOutsideRTH = Decimal(orderStateProto.equityWithLoanAfterOutsideRTH) if orderStateProto.HasField("suggestedSize"): orderState.suggestedSize = Decimal(orderStateProto.suggestedSize) if orderStateProto.HasField("rejectReason"): @@ -1063,7 +1076,6 @@ def createOrderState(orderStateProto: OrderStateProto) -> OrderState: return orderState - def createOrderAllocations(orderStateProto: OrderStateProto) -> list[OrderAllocation]: orderAllocations = [] orderAllocationProtoList = [] @@ -1106,7 +1118,7 @@ def createContractFromExecutionDetails( """ return createContract(exec_details_proto.contract) - +@quantize_decimals() def createOrderStatus(orderStatusProto: OrderStatusProto) -> OrderStatus: orderStatus = OrderStatus() if orderStatusProto.HasField("orderId"): @@ -1118,19 +1130,19 @@ def createOrderStatus(orderStatusProto: OrderStatusProto) -> OrderStatus: if orderStatusProto.HasField("remaining"): orderStatus.remaining = Decimal(orderStatusProto.remaining) if orderStatusProto.HasField("avgFillPrice"): - orderStatus.avgFillPrice = orderStatusProto.avgFillPrice + orderStatus.avgFillPrice = Decimal(str(orderStatusProto.avgFillPrice)) if orderStatusProto.HasField("permId"): orderStatus.permId = orderStatusProto.permId if orderStatusProto.HasField("parentId"): orderStatus.parentId = orderStatusProto.parentId if orderStatusProto.HasField("lastFillPrice"): - orderStatus.lastFillPrice = orderStatusProto.lastFillPrice + orderStatus.lastFillPrice = Decimal(str(orderStatusProto.lastFillPrice)) if orderStatusProto.HasField("clientId"): orderStatus.clientId = orderStatusProto.clientId if orderStatusProto.HasField("whyHeld"): orderStatus.whyHeld = orderStatusProto.whyHeld if orderStatusProto.HasField("mktCapPrice"): - orderStatus.mktCapPrice = orderStatusProto.mktCapPrice + orderStatus.mktCapPrice = Decimal(str(orderStatusProto.mktCapPrice)) return orderStatus @@ -1139,7 +1151,13 @@ def createExecution(executionProto: ExecutionProto) -> Execution: if executionProto.HasField("execId"): execution.execId = executionProto.execId if executionProto.HasField("time"): - execution.time = parseIBDatetime(executionProto.time) + parsed_time = parseIBDatetime(executionProto.time) + if not isinstance(parsed_time, dt.datetime): + execution.time = dt.datetime( + parsed_time.year, parsed_time.month, parsed_time.day + ) + else: + execution.time = parsed_time if executionProto.HasField("acctNumber"): execution.acctNumber = executionProto.acctNumber if executionProto.HasField("exchange"): @@ -1193,17 +1211,17 @@ def createFill(execDetailsProto: ExecutionDetailsProto) -> Fill: def createTradeFromOpenOrder( openOrderProto: OpenOrderProto, -) -> tuple[Trade, OrderState] | None: +) -> tuple[Trade | None, OrderState | None]: if not openOrderProto.HasField("contract"): - return None + return (None, None) contract = createContract(openOrderProto.contract) if not openOrderProto.HasField("order"): - return None + return (None, None) order = createOrder( openOrderProto.order.orderId, openOrderProto.contract, openOrderProto.order ) if not openOrderProto.HasField("orderState"): - return None + return (None, None) orderState = createOrderState(openOrderProto.orderState) orderStatus = OrderStatus(orderId=order.orderId, status=orderState.status) return Trade(contract, order, orderStatus, [], []), orderState diff --git a/ib_async/ticker.py b/ib_async/ticker.py index 1af30f6b..50e22b35 100644 --- a/ib_async/ticker.py +++ b/ib_async/ticker.py @@ -1,5 +1,6 @@ """Access to realtime market information.""" +from decimal import Decimal import logging from contextlib import suppress from dataclasses import dataclass, field @@ -56,6 +57,14 @@ TickType.ASK_YIELD: "askYield", TickType.DELAYED_YIELD_ASK: "askYield", TickType.LAST_YIELD: "lastYield", + TickType.ETF_NAV_CLOSE: "etf_nav_close", + TickType.ETF_NAV_PRIOR_CLOSE: "etf_nav_prior_close", + TickType.ETF_NAV_BID: "etf_nav_bid", + TickType.ETF_NAV_ASK: "etf_nav_ask", + TickType.ETF_NAV_LAST: "etf_nav_last", + TickType.ETF_FROZEN_NAV_LAST: "etf_frozen_nav_last", + TickType.ETF_NAV_HIGH: "etf_nav_high", + TickType.ETF_NAV_LOW: "etf_nav_low", } @@ -84,6 +93,7 @@ TickType.INDEX_FUTURE_PREMIUM: "indexFuturePremium", TickType.SHORTABLE: "shortable", TickType.HALTED: "halted", + TickType.DELAYED_HALTED: "halted", TickType.TRADE_COUNT: "tradeCount", TickType.TRADE_RATE: "tradeRate", TickType.VOLUME_RATE: "volumeRate", @@ -104,7 +114,7 @@ _logger = logging.getLogger("ib_async.ticker") -@dataclass +@dataclass(slots=True) class Ticker: """ Current market data such as bid, ask, last price, etc. for a contract. @@ -128,7 +138,6 @@ class Ticker: """ events: ClassVar = ("updateEvent",) - contract: Contract | None = None time: datetime | None = None timestamp: float | None = None @@ -210,9 +219,19 @@ class Ticker: regulatoryImbalance: float = nan bboExchange: str = "" snapshotPermissions: int = 0 + etf_nav_close: float | Decimal = nan + etf_nav_prior_close: float | Decimal = nan + etf_nav_bid: float | Decimal = nan + etf_nav_ask: float | Decimal = nan + etf_nav_last: float | Decimal = nan + etf_frozen_nav_last: float | Decimal = nan + etf_nav_high: float | Decimal = nan + etf_nav_low: float | Decimal = nan defaults: IBDefaults = field(default_factory=IBDefaults, repr=False) created: bool = field(default=False, repr=False) + updateEvent: Event = field(repr=False, init=False) + ticker_bus: Event = field(repr=False, init=False) def __post_init__(self): # when copying a dataclass, the __post_init__ runs again, so we @@ -221,6 +240,8 @@ def __post_init__(self): if not self.created: self.updateEvent = TickerUpdateEvent("updateEvent") self.ticker_bus = Event("Ticker bus") + """ticker bus event [TickDataType,datetime|None] + """ self.minTick = self.defaults.unset self.bid = self.defaults.unset self.bidSize = self.defaults.unset @@ -276,6 +297,14 @@ def __post_init__(self): self.auctionPrice = self.defaults.unset self.auctionImbalance = self.defaults.unset self.regulatoryImbalance = self.defaults.unset + self.etf_nav_close = self.defaults.unset + self.etf_nav_prior_close = self.defaults.unset + self.etf_nav_bid = self.defaults.unset + self.etf_nav_ask = self.defaults.unset + self.etf_nav_last = self.defaults.unset + self.etf_frozen_nav_last = self.defaults.unset + self.etf_nav_high = self.defaults.unset + self.etf_nav_low = self.defaults.unset self.created = True def __eq__(self, other): @@ -288,12 +317,7 @@ def __hash__(self): __str__ = dataclassRepr def _on_ticker_data(self, tick_data: TickDataType, last_time: datetime): - # _logger.debug( - # "Ticker %s. Received tick data: %s, %s", - # self.contract.symbol, - # tick_data, - # last_time, - # ) + """get ticker data updates and dispatch to the right handler.""" if isinstance(tick_data, TickPriceData): self._on_price_size_tick(tick_data, last_time) elif isinstance(tick_data, TickSizeData): @@ -319,11 +343,11 @@ def _on_price_size_tick(self, tick_price: TickPriceData, last_time: datetime): # https://interactivebrokers.github.io/tws-api/tick_types.html if tick_price.tickType in {TickType.BID, TickType.DELAYED_BID}: - # Note: Keep these size==0 overrides INSIDE each tickType where it is needed because - # other tickTypes like open/high/low/close are values with size=0 but those - # are still valid prices to receive. - # Bid/Ask updates always have a Price+Size delivered at the same time, while the - # other properties are mainly price-only delivery methods. + # Note: Keep these size==0 overrides INSIDE each tickType where it is + # needed because other tickTypes like open/high/low/close are values with + # size=0 but those are still valid prices to receive. + # Bid/Ask updates always have a Price+Size delivered at the same time, + # while the other properties are mainly price-only delivery methods. if tick_price.size == 0: price = self.defaults.emptyPrice size = self.defaults.emptySize @@ -363,16 +387,11 @@ def _on_price_size_tick(self, tick_price: TickPriceData, last_time: datetime): # More research: IBKR also shows the bad value in their own app, so there # is a data bug in their own server logic somewhere. - # self._logger.error(f"[{tickType=}] updating last price size: {price=} {size=} :: BEFORE {ticker=}") - # self._logger.error(f"[{tickType=}] SETTING {ticker.prevLast=} = {ticker.last=}; {ticker.prevLastSize=} = {ticker.lastSize=}") - self.prevLast = self.last self.prevLastSize = self.lastSize self.last = price self.lastSize = size - # self._logger.error(f"[{tickType=}] SET {ticker.prevLast=} = {ticker.last=}; {ticker.prevLastSize=} = {ticker.lastSize=}") - # self._logger.error(f"[{tickType=}] updating last price size: {price=} {size=} :: AFTER {ticker=}") else: assert tick_price.tickType in PRICE_TICK_MAP, ( f"Received tick {tick_price.tickType=} {tick_price.price=} but we don't have an attribute mapping for it? Triggered from {self.contract=}" @@ -389,10 +408,6 @@ def _on_price_size_tick(self, tick_price: TickPriceData, last_time: datetime): def _on_size_tick(self, tick_size: TickSizeData, last_time: datetime): price = self.defaults.emptyPrice - # self._logger.error( - # f"tickSize with tickType {tickType}: " f"processing value: {size!r}" - # ) - # https://interactivebrokers.github.io/tws-api/tick_types.html if tick_size.tickType in { TickType.BID_SIZE, @@ -453,10 +468,14 @@ def _on_tick_string(self, tick_string: TickStringData, last_time: datetime): self.askExchange = tick_string.value elif tick_string.tickType == TickType.LAST_EXCH: self.lastExchange = tick_string.value - elif tick_string.tickType == TickType.LAST_TIMESTAMP: + elif tick_string.tickType in { + TickType.LAST_TIMESTAMP, + TickType.DELAYED_LAST_TIMESTAMP, + }: timestamp = int(tick_string.value) - # only populate if timestamp isn't '0' (we don't want to report "last trade: 20,000 days ago") + # only populate if timestamp isn't '0' (we don't want to report "last + # trade: 20,000 days ago") if timestamp: self.lastTimestamp = datetime.fromtimestamp( timestamp, self.defaults.timezone diff --git a/ib_async/util.py b/ib_async/util.py index 15687dcb..0ac39a88 100644 --- a/ib_async/util.py +++ b/ib_async/util.py @@ -22,6 +22,8 @@ TypeAlias, Union, ) +from functools import wraps +from decimal import ROUND_HALF_UP import eventkit as ev @@ -608,8 +610,8 @@ def parseIBDatetime(s: str) -> Union[dt.date, dt.datetime]: # YYYYmmdd HH:MM:SS # or # YYYY-mm-dd HH:MM:SS.0 - ss = s.replace(" ", "").replace("-", "")[:16] - t = dt.datetime.strptime(ss, "%Y%m%d%H:%M:%S") + ss = s.replace(" ", "").replace("-", "").replace(":", "")[:14] + t = dt.datetime.strptime(ss, "%Y%m%d%H%M%S") return t @@ -644,3 +646,27 @@ def listOfValues(cls): def isValidIntValue(val: int) -> bool: return val != UNSET_INTEGER + +def quantize_decimals(places=2, rounding=ROUND_HALF_UP): + """ + Decorator that finds all Decimal fields in a returned dataclass + object and quantizes them to a given number of decimal places. + """ + quantizer = Decimal('0.1') ** places + + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + # Get the dataclass object from the wrapped function + result = func(*args, **kwargs) + + # Iterate over its fields and quantize any Decimals + if result and is_dataclass(result): + for field in fields(result): + value = getattr(result, field.name) + if isinstance(value, Decimal): + quantized_value = value.quantize(quantizer, rounding=rounding) + setattr(result, field.name, quantized_value) + return result + return wrapper + return decorator diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index b6792c7a..222ce685 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -42,9 +42,7 @@ HistogramData, HistoricalNews, HistoricalSchedule, - HistoricalTick, - HistoricalTickBidAsk, - HistoricalTickLast, + HistoricalTickType, IBDefaults, MktDepthData, NewsArticle, @@ -62,16 +60,14 @@ ScanDataList, SoftDollarTier, TickComputationData, - TickGenericData, + TickDeliveryType, TickParams, - TickPriceData, - TickSizeData, - TickStringData, TradeLogEntry, ) from ib_async.order import Order, OrderState, OrderStatus, Trade from ib_async.ticker import Ticker from ib_async.util import ( + EPOCH, dataclassUpdate, getLoop, globalErrorEvent, @@ -404,15 +400,13 @@ def reset(self): self.portfolio = defaultdict(dict) self.positions = defaultdict(dict) self.trades = BiDict[OrderKeyType, Trade]() - self.tickers = BiDict[int, Ticker]() self.subscriptions = BiDict[int, SubscriptionType]() self._isReady = False self.fills = {} self.newsTicks = [] self.msgId2NewsBulletin = {} - self.tickers = BiDict[int, Ticker](track_objects_weakly=True) + self.tickers = BiDict[int, Ticker]() self.pendingTickers = set() - self.reqId2Subscriber = {} self.Pnl = BiDict[tuple[str, str], PnL]() self.pnlSingles = BiDict[tuple[str, str, int], PnLSingle]() self.lastTime = datetime.min @@ -452,7 +446,7 @@ def connectionClosed(self): def _endReq(self, reqId: int | str): self.response_bus.emit(reqId, None) - def startTicker(self, reqId: int, contract: Contract, tickType: Union[int, str]): + def startTicker(self, reqId: int, contract: Contract): """ Start a tick request that has the reqId associated with the contract. Return the ticker. @@ -461,17 +455,17 @@ def startTicker(self, reqId: int, contract: Contract, tickType: Union[int, str]) if not ticker: ticker = Ticker(contract=contract, defaults=self.defaults) self.tickers.add(reqId, hash(ticker.contract), ticker) - ticker.ticker_bus.takewhile(lambda r, data, t: data is not None).pluck( - 1, 2 - ).connect(ticker._on_ticker_data) + ticker.ticker_bus.takewhile(lambda data, t: data is not None).connect( + ticker._on_ticker_data + ) return ticker - def endTicker(self, ticker: Ticker, tickType: Union[int, str]): + def endTicker(self, ticker: Ticker): reqId = self.tickers.get_request_id(hash(ticker.contract)) if reqId: self.tickers.remove_by_request_id(reqId) - ticker.ticker_bus.emit(reqId, None, None) + ticker.ticker_bus.emit(None, None) return reqId def startSubscription( @@ -486,13 +480,13 @@ def startSubscription( # ib.reqScannerSubscription self.subscriptions.add(reqId, reqId, subscriber) # start subscription - subscriber.subscription_bus.takewhile(lambda r, data: data is not None).pluck( - 1 - ).map(self.ib._raise_if_error).partial(self.ib).connect(subscriber._on_data) + subscriber.subscription_bus.takewhile(lambda data: data is not None).map( + self.ib._raise_if_error + ).partial(self.ib).connect(subscriber._on_data) def endSubscription(self, subscriber): """Unregister a live subscription.""" - subscriber.subscription_bus.emit(subscriber.reqId, None) + subscriber.subscription_bus.emit(None) self.subscriptions.remove_by_request_id(subscriber.reqId) def setTimeout(self, timeout: float): @@ -611,14 +605,9 @@ def pnl( self, reqId: int, dailyPnL: float, unrealizedPnL: float, realizedPnL: float ): pnl = self.Pnl.get_by_request_id(reqId) - if not pnl: - self._logger.error("pnl: No pnl found for reqId %s", reqId) - return - - pnl.dailyPnL = dailyPnL - pnl.unrealizedPnL = unrealizedPnL - pnl.realizedPnL = realizedPnL - self.ib.pnlEvent.emit(pnl) + if pnl: + pnl.pnl_bus.emit(dailyPnL, unrealizedPnL, realizedPnL) + self.ib.pnlEvent.emit(pnl) def pnlSingle( self, @@ -630,16 +619,11 @@ def pnlSingle( value: float, ): pnlSingle = self.pnlSingles.get_by_request_id(reqId) - if not pnlSingle: - self._logger.error("pnlSingle: No pnlSingle found for reqId %s", reqId) - return - - pnlSingle.position = pos - pnlSingle.dailyPnL = dailyPnL - pnlSingle.unrealizedPnL = unrealizedPnL - pnlSingle.realizedPnL = realizedPnL - pnlSingle.value = value - self.ib.pnlSingleEvent.emit(pnlSingle) + if pnlSingle: + pnlSingle.pnl_single_bus.emit( + pos, dailyPnL, unrealizedPnL, realizedPnL, value + ) + self.ib.pnlSingleEvent.emit(pnlSingle) def orderKey(self, clientId: int, orderId: int, permId: int) -> OrderKeyType: key: OrderKeyType @@ -847,7 +831,7 @@ def realtimeBar(self, reqId: int, bar: RealTimeBar): if bar is not None: bars_subscription = self.subscriptions.get_by_request_id(reqId) if bars_subscription is not None: - bars_subscription.subscription_bus.emit(reqId, bar) + bars_subscription.subscription_bus.emit(bar) def historicalData(self, reqId: int, bars: list[BarData]): if bars is not None: @@ -859,7 +843,7 @@ def historicalDataEnd(self, reqId, _start: str, _end: str): def historicalDataUpdate(self, reqId: int, bar: BarData): subscription = self.subscriptions.get_by_request_id(reqId) if subscription: - subscription.subscription_bus.emit(reqId, bar) + subscription.subscription_bus.emit(bar) def headTimestamp(self, reqId: int, headTimestamp: str): try: @@ -868,51 +852,17 @@ def headTimestamp(self, reqId: int, headTimestamp: str): except ValueError as exc: self.response_bus.emit(reqId, exc) - def historicalTicks(self, reqId: int, ticks: list[HistoricalTick], done: bool): + def historicalTicks(self, reqId: int, ticks: list[HistoricalTickType], done: bool): self.response_bus.emit(reqId, ticks) if done: self._endReq(reqId) - def historicalTicksBidAsk( - self, reqId: int, ticks: list[HistoricalTickBidAsk], done: bool - ): - self.response_bus.emit(reqId, ticks) - if done: - self._endReq(reqId) - - def historicalTicksLast( - self, reqId: int, ticks: list[HistoricalTickLast], done: bool - ): - self.response_bus.emit(reqId, ticks) - if done: - self._endReq(reqId) - - def priceSizeTick(self, reqId: int, tick_price: TickPriceData): - ticker = self.tickers.get_by_request_id(reqId) - if not ticker: - self._logger.error(f"priceSizeTick: Unknown reqId: {reqId}") - return - ticker.ticker_bus.emit(reqId, tick_price, self.lastTime) - self.pendingTickers.add(ticker) - - def tickSize(self, reqId: int, tick_size: TickSizeData): + def tickerDelivery(self, reqId: int, tickData: TickDeliveryType): ticker = self.tickers.get_by_request_id(reqId) if not ticker: - self._logger.error(f"tickSize: Unknown reqId: {reqId}") - return - ticker.ticker_bus.emit(reqId, tick_size, self.lastTime) - self.pendingTickers.add(ticker) - - def tickString(self, reqId: int, tick_string: TickStringData): - if not (ticker := self.tickers.get_by_request_id(reqId)): - return - ticker.ticker_bus.emit(reqId, tick_string, self.lastTime) - self.pendingTickers.add(ticker) - - def tickGeneric(self, reqId: int, tick_generic: TickGenericData): - if not (ticker := self.tickers.get_by_request_id(reqId)): + self._logger.error("tickerDelivery: Unknown reqId: %s, %r", reqId, tickData) return - ticker.ticker_bus.emit(reqId, tick_generic, self.lastTime) + ticker.ticker_bus.emit(tickData, self.lastTime) self.pendingTickers.add(ticker) def tickReqParams(self, reqId: int, tickParams: TickParams): @@ -925,39 +875,32 @@ def tickReqParams(self, reqId: int, tickParams: TickParams): def tickSnapshotEnd(self, reqId: int): ticker = self.tickers.get_by_request_id(reqId) if ticker: - self.endTicker(ticker, "") + self.endTicker(ticker) return self._logger.error(f"tickSnapshotEnd: Unknown reqId: {reqId}") - def tickByTickAllLast(self, reqId: int, historicalTickLast: HistoricalTickLast): + def tickByTick(self, reqId: int, tickByTick: HistoricalTickType): ticker = self.tickers.get_by_request_id(reqId) if not ticker: - self._logger.error(f"tickByTickBidAsk: Unknown reqId: {reqId}") + self._logger.error(f"tickByTick: Unknown reqId: {reqId}") return - ticker.ticker_bus.emit(reqId, historicalTickLast, None) + ticker.ticker_bus.emit(tickByTick, EPOCH) self.pendingTickers.add(ticker) - def tickByTickBidAsk( - self, - reqId: int, - historicalTickBidAsk: HistoricalTickBidAsk, - ): - ticker = self.tickers.get_by_request_id(reqId) - if not ticker: - self._logger.error(f"tickByTickBidAsk: Unknown reqId: {reqId}") - return - ticker.ticker_bus.emit(reqId, historicalTickBidAsk, None) - self.pendingTickers.add(ticker) - - def tickByTickMidPoint(self, reqId: int, historicalTick: HistoricalTick): + def tickOptionComputation(self, reqId: int, tick_computation: TickComputationData): ticker = self.tickers.get_by_request_id(reqId) - if not ticker: - self._logger.error(f"tickByTickMidPoint: Unknown reqId: {reqId}") - return + if ticker: + # reply from reqMktData + # https://interactivebrokers.github.io/tws-api/tick_types.html - ticker.ticker_bus.emit(reqId, historicalTick, None) - self.pendingTickers.add(ticker) + ticker.ticker_bus.emit(tick_computation, self.lastTime) + self.pendingTickers.add(ticker) + elif reqId: + # reply from calculateImpliedVolatility or calculateOptionPrice + self.response_bus.emit(reqId, tick_computation.computation) + else: + self._logger.error(f"tickOptionComputation: Unknown reqId: {reqId}") def smartComponents(self, reqId, components): self.response_bus.emit(reqId, components) @@ -1001,9 +944,9 @@ def updateMktDepthL2( # if you're curious when these operations run and what they do, enable this too: # fmt: off - # print("BID" if side else "ASK", "OPERATION", operation, "at position", + # print("BID" if side else "ASK", "OPERATION", operation, "at position", # position, "for price", price, "at qty", size) - # assert list(dom.keys()) == list(range(0, len(dom))), f"Keys aren't + # assert list(dom.keys()) == list(range(0, len(dom))), f"Keys aren't # sequential? {dom} :: {ticker}" # fmt: on @@ -1043,20 +986,6 @@ def updateMktDepthL2( ticker.domTicks.append(tick) self.pendingTickers.add(ticker) - def tickOptionComputation(self, reqId: int, tick_computation: TickComputationData): - ticker = self.tickers.get_by_request_id(reqId) - if ticker: - # reply from reqMktData - # https://interactivebrokers.github.io/tws-api/tick_types.html - - self.ticker_bus.emit(reqId, tick_computation, self.lastTime) - self.pendingTickers.add(ticker) - elif reqId: - # reply from calculateImpliedVolatility or calculateOptionPrice - self.response_bus.emit(reqId, tick_computation.computation) - else: - self._logger.error(f"tickOptionComputation: Unknown reqId: {reqId}") - def deltaNeutralValidation(self, reqId: int, dnc: DeltaNeutralContract): pass @@ -1070,7 +999,7 @@ def scannerData(self, reqId: int, scanData: list[ScanData]): dataList = self.subscriptions.get_by_request_id(reqId) if dataList is not None: - dataList.subscription_bus.emit(reqId, scanData) + dataList.subscription_bus.emit(scanData) def scannerDataEnd(self, reqId: int): dataList = self.subscriptions.get_by_request_id(reqId) @@ -1301,7 +1230,7 @@ def error( if errorCode == 165: # for scan data subscription there are no longer matching results - dataList = self.reqId2Subscriber.get(reqId) + dataList = self.subscriptions.get_by_request_id(reqId) if dataList: dataList.clear() dataList.updateEvent.emit(dataList) @@ -1326,7 +1255,7 @@ def error( elif errorCode == 10225: # Bust event occurred, current subscription is deactivated. # Please resubscribe real-time bars immediately - bars = self.reqId2Subscriber.get(reqId) + bars = self.subscriptions.get_by_request_id(reqId) if isinstance(bars, RealTimeBarList): self.ib.client.cancelRealTimeBars(reqId) self.ib.client.reqRealTimeBars( diff --git a/pyproject.toml b/pyproject.toml index 9aa87987..e6464fd0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,6 @@ classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Office/Business :: Financial :: Investment", - "License :: OSI Approved :: BSD License", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -28,6 +27,7 @@ aeventkit = "^2.1.0" # aeventkit = { path = "../eventkit", develop = true } nest_asyncio = "*" protobuf = "*" +tzdata = "^2025.2" [tool.poetry.urls] "Bug Tracker" = "https://github.com/ib-api-reloaded/ib_async/issues" @@ -44,7 +44,7 @@ pandas = "^2.2.1" ruff = "^0.11.13" grpcio-tools = "^1.75.1" pytest-timeout = "*" - +pytest-cov = "^6.2.1" [tool.poetry.group.docs] optional = true @@ -71,6 +71,18 @@ asyncio_default_fixture_loop_scope = "function" asyncio_default_test_loop_scope = "function" timeout = 10 +[tool.ruff] +line-length = 88 + +[tool.ruff.lint] +extend-select = [ + # convert legacy python syntax to modern syntax + "UP", + # isort imports + "I", +] +ignore = ["E402"] # Ignore Module level import not at top of file + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/tests/test_market_data_converters.py b/tests/test_market_data_converters.py index 722a400f..1983e8f2 100644 --- a/tests/test_market_data_converters.py +++ b/tests/test_market_data_converters.py @@ -106,18 +106,15 @@ def test_createTickPriceData(self): proto = TickPriceProto( reqId=1, tickType=TickType.BID.value, price=1.2, size="100", attrMask=1 ) - price_data, size_data = createTickPriceData(proto) - - assert isinstance(price_data, TickPriceData) - assert price_data.reqId == 1 - assert price_data.tickType == TickType.BID - assert price_data.price == 1.2 - assert price_data.size == 100 - assert price_data.attribs.canAutoExecute is True - - assert isinstance(size_data, TickSizeData) - assert size_data.tickType == TickType.BID_SIZE - assert size_data.size == 100 + tick_price_data = createTickPriceData(proto) + assert isinstance(tick_price_data, TickPriceData) + assert tick_price_data.reqId == 1 + assert tick_price_data.tickType == TickType.BID + assert tick_price_data.price == 1.2 + assert tick_price_data.size == 100.0 + assert tick_price_data.attribs.canAutoExecute is True + assert tick_price_data.attribs.pastLimit is False + assert tick_price_data.attribs.preOpen is False def test_createTickSizeData(self): proto = TickSizeProto(reqId=1, tickType=TickType.ASK_SIZE.value, size="200") diff --git a/tests/test_trade_converter.py b/tests/test_trade_converter.py index 3b4f4481..280927ce 100644 --- a/tests/test_trade_converter.py +++ b/tests/test_trade_converter.py @@ -68,7 +68,7 @@ ) -from ib_async.protobuf_converters.trade_converter import ( +from ib_async.protobuf_converters.trade_converters import ( createPlaceOrderRequestProto, createOrderProto, createOrder, @@ -87,7 +87,7 @@ createGlobalCancelRequestProto, createCancelOrderRequestProto, createExerciseOptionsRequestProto, - createConditionsProto, # Also need to import these helper functions + createConditionsProto, createOrderComboLegs, createOrderConditionProto, createOperatorConditionProto, @@ -217,7 +217,7 @@ def test_createOrderProto_with_combo_legs(self): ) with pytest.MonkeyPatch().context() as mp: mp.setattr( - "ib_async.protobuf_converters.trade_converter.createContractProto", + "ib_async.protobuf_converters.trade_converters.createContractProto", lambda c, o: contract_proto_mock, ) place_order_proto = createPlaceOrderRequestProto(1, contract, order) @@ -737,8 +737,8 @@ def test_createOrderState(self): order_state = createOrderState(order_state_proto) assert isinstance(order_state, OrderState) assert order_state.status == "Filled" - assert order_state.initMarginBefore == "1000.0" - assert order_state.commission == 10.0 + assert order_state.initMarginBefore == Decimal("1000.00") + assert order_state.commission == 10.00 assert order_state.completedTime == "20250101 10:00:00" def test_createOrderAllocations(self): @@ -844,7 +844,7 @@ def test_createTradeFromOpenOrder_missing_contract(self): open_order_proto = OpenOrderProto() # open_order_proto.contract is missing result = createTradeFromOpenOrder(open_order_proto) - assert result is None + assert result == (None, None) def test_createCommissionReport(self): commission_report_proto = CommissionReportProto( From e40a49c5831d7f333b3009b1042f1b10d73c8958 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:48:54 +0100 Subject: [PATCH 08/48] fix ruff linting --- ib_async/client.py | 11 +- ib_async/contract.py | 23 +- ib_async/ib.py | 48 +- ib_async/objects.py | 31 +- ib_async/order.py | 8 +- .../protobuf_converters/base_converters.py | 3 +- .../contract_converters.py | 3 +- .../historical_data_converters.py | 7 +- .../market_data_converters.py | 15 +- .../subscription_converters.py | 6 +- .../protobuf_converters/trade_converters.py | 30 +- ib_async/ticker.py | 10 +- ib_async/util.py | 27 +- ib_async/wrapper.py | 8 +- tests/tests_util.py | 739 ++++++++++++++++++ 15 files changed, 853 insertions(+), 116 deletions(-) create mode 100644 tests/tests_util.py diff --git a/ib_async/client.py b/ib_async/client.py index 50b7ad27..4b968d18 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -5,7 +5,6 @@ import struct import time from collections import deque -from typing import Deque, List, Optional from eventkit import Event from google.protobuf.message import Message @@ -101,8 +100,8 @@ createCancelScannerSubscriptionProto, createPnLRequestProto, createPnLSingleRequestProto, - createScannerSubscriptionRequestProto, createScannerParametersRequestProto, + createScannerSubscriptionRequestProto, ) from .protobuf_converters.trade_converters import ( createCancelOrderRequestProto, @@ -224,8 +223,8 @@ def reset(self): self._numBytesRecv = 0 self._numMsgRecv = 0 self._isThrottling = False - self._msgQ: Deque[bytes] = deque() - self._timeQ: Deque[float] = deque() + self._msgQ: deque[bytes] = deque() + self._timeQ: deque[float] = deque() def serverVersion(self) -> int: return self._serverVersion @@ -315,7 +314,7 @@ def sendMsg(self, msg: bytes): self.throttleEnd.emit() self._logger.info("Stopped to throttle requests") - def getAccounts(self) -> List[str]: + def getAccounts(self) -> list[str]: """Get the list of account names that are under management.""" if not self.isReady(): raise ConnectionError("Not connected") @@ -332,7 +331,7 @@ def setConnectOptions(self, connectOptions: str): self.connectOptions = connectOptions.encode() def connect( - self, host: str, port: int, clientId: int, timeout: Optional[float] = 2.0 + self, host: str, port: int, clientId: int, timeout: float|None = 2.0 ): """ Connect to a running TWS or IB gateway application. diff --git a/ib_async/contract.py b/ib_async/contract.py index b017ccc8..11310c48 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -3,7 +3,6 @@ import datetime as dt from dataclasses import dataclass, field from enum import Enum -from typing import List, Optional import ib_async.util as util @@ -127,7 +126,7 @@ class Contract: issuerId: str = "" comboLegsDescrip: str = "" comboLegs: list["ComboLeg"] = field(default_factory=list) - deltaNeutralContract: Optional["DeltaNeutralContract"] = None + deltaNeutralContract: "DeltaNeutralContract" | None= None @staticmethod def create(**kwargs) -> "Contract": @@ -564,6 +563,7 @@ def __init__( **kwargs, ) + @dataclass(slots=True, frozen=True) class TagValue: tag: str @@ -588,6 +588,7 @@ class DeltaNeutralContract: delta: float = 0.0 price: float = 0.0 + @dataclass(slots=True, frozen=True) class TradingSession: start: dt.datetime @@ -596,7 +597,7 @@ class TradingSession: @dataclass(slots=True) class ContractDetails: - contract: Optional[Contract] = None + contract: Contract | None = None marketName: str = "" minTick: float = 0.0 orderTypes: str = "" @@ -618,7 +619,7 @@ class ContractDetails: underSymbol: str = "" underSecType: str = "" marketRuleIds: str = "" - secIdList: List[TagValue] = field(default_factory=list) + secIdList: list[TagValue] = field(default_factory=list) realExpirationDate: str = "" lastTradeTime: str = "" stockType: str = "" @@ -657,20 +658,22 @@ class ContractDetails: fundSubsequentMinimumPurchase: str = "" fundBlueSkyStates: str = "" fundBlueSkyTerritories: str = "" - fundDistributionPolicyIndicator: FundDistributionPolicyIndicator = FundDistributionPolicyIndicator.NoneItem + fundDistributionPolicyIndicator: FundDistributionPolicyIndicator = ( + FundDistributionPolicyIndicator.NoneItem + ) fundAssetType: FundAssetType = FundAssetType.NoneItem ineligibilityReasonList: list[IneligibilityReason] = field(default_factory=list) eventContract1: str = "" eventContractDescription1: str = "" eventContractDescription2: str = "" - def tradingSessions(self) -> List[TradingSession]: + def tradingSessions(self) -> list[TradingSession]: return self._parseSessions(self.tradingHours) - def liquidSessions(self) -> List[TradingSession]: + def liquidSessions(self) -> list[TradingSession]: return self._parseSessions(self.liquidHours) - def _parseSessions(self, s: str) -> List[TradingSession]: + def _parseSessions(self, s: str) -> list[TradingSession]: """Parse the IBKR session date range text format into native Python objects. Note: The IBKR date range format looks like: @@ -705,8 +708,8 @@ def _parseSessions(self, s: str) -> List[TradingSession]: @dataclass(slots=True) class ContractDescription: - contract: Optional[Contract] = None - derivativeSecTypes: List[str] = field(default_factory=list) + contract: Contract | None = None + derivativeSecTypes: list[str] = field(default_factory=list) @dataclass(slots=True, frozen=True) diff --git a/ib_async/ib.py b/ib_async/ib.py index ff6bc4d9..187e3463 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -5,15 +5,11 @@ import datetime import logging import time +from collections.abc import Awaitable, Iterator from enum import Flag, auto from typing import ( Any, - Awaitable, - Iterator, - List, - Optional, TypeVar, - Union, ) from eventkit import Event @@ -666,7 +662,7 @@ def pendingTickers(self) -> list[Ticker]: """Get a list of all tickers that have pending ticks or domTicks.""" return list(self.wrapper.pendingTickers) - def realtimeBars(self) -> list[Union[BarDataList, RealTimeBarList]]: + def realtimeBars(self) -> list[BarDataList| RealTimeBarList]: """ Get a list of all live updated bars. These can be 5 second realtime bars or live updated historical bars. @@ -844,7 +840,7 @@ def placeOrder(self, contract: Contract, order: Order) -> Trade: def cancelOrder( self, order: Order, orderCancel: OrderCancel | None = None - ) -> Optional[Trade]: + ) -> Trade|None: """ Cancel the order and return the Trade it belongs to. @@ -1001,7 +997,7 @@ def reqCompletedOrders(self, apiOnly: bool) -> list[Trade]: """ return self._run(self.reqCompletedOrdersAsync(apiOnly)) - def reqExecutions(self, execFilter: Optional[ExecutionFilter] = None) -> list[Fill]: + def reqExecutions(self, execFilter: ExecutionFilter| None) -> list[Fill]: """ It is recommended to use :meth:`.fills` or :meth:`.executions` instead. @@ -1217,7 +1213,7 @@ def cancelRealTimeBars(self, bars: RealTimeBarList): def reqHistoricalData( self, contract: Contract, - endDateTime: Union[datetime.datetime, datetime.date, str, None], + endDateTime: datetime.datetime| datetime.date| str| None, durationStr: str, barSizeSetting: str, whatToShow: str, @@ -1300,7 +1296,7 @@ def reqHistoricalSchedule( self, contract: Contract, numDays: int, - endDateTime: Union[datetime.datetime, datetime.date, str, None] = "", + endDateTime: datetime.datetime| datetime.date| str| None = "", useRTH: bool = True, ) -> HistoricalSchedule: """ @@ -1325,14 +1321,14 @@ def reqHistoricalSchedule( def reqHistoricalTicks( self, contract: Contract, - startDateTime: Union[str, datetime.date], - endDateTime: Union[str, datetime.date], + startDateTime: str| datetime.date, + endDateTime: str| datetime.date, numberOfTicks: int, whatToShow: str, useRth: bool, ignoreSize: bool = False, miscOptions: list[TagValue] = [], - ) -> List: + ) -> list: """ Request historical ticks. The time resolution of the ticks is one second. @@ -1905,8 +1901,8 @@ def reqHistoricalNews( self, conId: int, providerCodes: str, - startDateTime: Union[str, datetime.date], - endDateTime: Union[str, datetime.date], + startDateTime: str| datetime.date, + endDateTime: str| datetime.date, totalResults: int, historicalNewsOptions: list[TagValue] = [], ) -> HistoricalNews: @@ -2097,7 +2093,7 @@ async def connectAsync( host: str = "127.0.0.1", port: int = 7497, clientId: int = 1, - timeout: Optional[float] = 4, + timeout: float|None = 4, readonly: bool = False, account: str = "", raiseSyncErrors: bool = False, @@ -2513,7 +2509,7 @@ def reqMarketRuleAsync(self, marketRuleId: int) -> Awaitable[list[PriceIncrement async def reqHistoricalDataAsync( self, contract: Contract, - endDateTime: Union[datetime.datetime, datetime.date, str, None], + endDateTime: datetime.datetime| datetime.date| str| None, durationStr: str, barSizeSetting: str, whatToShow: str, @@ -2574,7 +2570,7 @@ def reqHistoricalScheduleAsync( self, contract: Contract, numDays: int, - endDateTime: Union[datetime.datetime, datetime.date, str, None] = "", + endDateTime: datetime.datetime| datetime.date| str| None = "", useRTH: bool = True, ) -> Awaitable[HistoricalSchedule]: reqId = self.client.getReqId() @@ -2602,14 +2598,14 @@ def reqHistoricalScheduleAsync( def reqHistoricalTicksAsync( self, contract: Contract, - startDateTime: Union[str, datetime.date], - endDateTime: Union[str, datetime.date], + startDateTime: str| datetime.date, + endDateTime: str| datetime.date, numberOfTicks: int, whatToShow: str, useRth: bool, ignoreSize: bool = False, miscOptions: list[TagValue] = [], - ) -> Awaitable[List]: + ) -> Awaitable[list]: reqId = self.client.getReqId() start = util.formatIBDatetime(startDateTime) end = util.formatIBDatetime(endDateTime) @@ -2727,7 +2723,7 @@ async def calculateImpliedVolatilityAsync( optionPrice: float, underPrice: float, implVolOptions: list[TagValue] = [], - ) -> Optional[OptionComputation]: + ) -> OptionComputation|None: reqId = self.client.getReqId() self.client.calculateImpliedVolatility( reqId, contract, optionPrice, underPrice, implVolOptions @@ -2753,7 +2749,7 @@ async def calculateOptionPriceAsync( volatility: float, underPrice: float, optPrcOptions: list[TagValue] = [], - ) -> Optional[OptionComputation]: + ) -> OptionComputation|None: reqId = self.client.getReqId() self.client.calculateOptionPrice( reqId, contract, volatility, underPrice, optPrcOptions @@ -2810,11 +2806,11 @@ async def reqHistoricalNewsAsync( self, conId: int, providerCodes: str, - startDateTime: Union[str, datetime.date], - endDateTime: Union[str, datetime.date], + startDateTime: str| datetime.date, + endDateTime: str| datetime.date, totalResults: int, historicalNewsOptions: list[TagValue] = [], - ) -> Optional[HistoricalNews]: + ) -> HistoricalNews|None: reqId = self.client.getReqId() future = self.wrapper.startReq(reqId) diff --git a/ib_async/objects.py b/ib_async/objects.py index 924af079..be561243 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -1,11 +1,12 @@ """Object hierarchy.""" from __future__ import annotations + from dataclasses import dataclass, field from datetime import date as date_ from datetime import datetime, timezone, tzinfo from enum import Enum -from typing import TYPE_CHECKING, Any, List, NamedTuple, Optional, TypeAlias, Union +from typing import TYPE_CHECKING, Any, NamedTuple, TypeAlias if TYPE_CHECKING: from ib_async import IB @@ -118,7 +119,7 @@ class ExecutionFilter: @dataclass(slots=True, frozen=True) class BarData: - date: Union[date_, datetime] = EPOCH + date: date_ | datetime = EPOCH open: float = 0.0 high: float = 0.0 low: float = 0.0 @@ -568,7 +569,7 @@ class HistoricalSchedule: startDateTime: str = "" endDateTime: str = "" timeZone: str = "" - sessions: List[HistoricalSession] = field(default_factory=list) + sessions: list[HistoricalSession] = field(default_factory=list) @dataclass @@ -648,16 +649,16 @@ class OptionChain: underlyingConId: int tradingClass: str multiplier: str - expirations: List[str] - strikes: List[float] + expirations: list[str] + strikes: list[float] @dataclass(slots=True, frozen=True) class Dividends: - past12Months: Optional[float] - next12Months: Optional[float] - nextDate: Optional[date_] - nextAmount: Optional[float] + past12Months: float | None + next12Months: float | None + nextDate: date_ | None + nextAmount: float | None @dataclass(slots=True, frozen=True) @@ -714,7 +715,7 @@ class ConnectionStats: numMsgSent: int -class BarDataList(List[BarData]): +class BarDataList(list[BarData]): """ List of :class:`.BarData` that also stores all request parameters. @@ -726,14 +727,14 @@ class BarDataList(List[BarData]): reqId: int contract: Contract - endDateTime: Union[datetime, date_, str, None] + endDateTime: datetime | date_ | str | None durationStr: str barSizeSetting: str whatToShow: str useRTH: bool formatDate: int keepUpToDate: bool - chartOptions: List[TagValue] + chartOptions: list[TagValue] def __init__(self, *args): super().__init__(*args) @@ -743,7 +744,7 @@ def __init__(self, *args): def __eq__(self, other) -> bool: return self is other - def _on_data(self, ib: "IB", bar: BarData): + def _on_data(self, ib: IB, bar: BarData): """Called on bar update when keepUpToDate=True.""" lastDate = self[-1].date @@ -786,7 +787,7 @@ def __init__(self, *args): def __eq__(self, other) -> bool: return self is other - def _on_data(self, ib: "IB", bar: RealTimeBar): + def _on_data(self, ib: IB, bar: RealTimeBar): """Called on real time bar update.""" self.append(bar) ib.barUpdateEvent.emit(self, True) @@ -814,7 +815,7 @@ def __init__(self, *args): def __eq__(self, other): return self is other - def _on_data(self, ib: "IB", data: list[ScanData]): + def _on_data(self, ib: IB, data: list[ScanData]): """Called on scanner data.""" rank = data[0].rank if 0 <= len(data) else None if rank == 0: diff --git a/ib_async/order.py b/ib_async/order.py index 2ccb64cc..69ea77b0 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -5,14 +5,14 @@ from dataclasses import dataclass, field from decimal import Decimal from enum import StrEnum -from typing import ClassVar, TypeAlias from math import nan +from typing import ClassVar, TypeAlias from eventkit import Event from .contract import Contract, TagValue from .objects import Fill, SoftDollarTier, TradeLogEntry -from .util import UNSET_DECIMAL, dataclassNonDefaults, UNSET_DOUBLE, UNSET_INTEGER +from .util import UNSET_DECIMAL, UNSET_DOUBLE, UNSET_INTEGER, dataclassNonDefaults class OrderTIF(StrEnum): @@ -281,7 +281,7 @@ class OrderStatus: mktCapPrice: float | Decimal = 0.0 @property - def total(self) -> float|Decimal: + def total(self) -> float | Decimal: """Helper property to return the total size of this requested order.""" return self.filled + self.remaining @@ -368,7 +368,7 @@ class OrderState: equityWithLoanAfterOutsideRTH: float | Decimal = nan suggestedSize: float | Decimal = nan rejectReason: str = "" - orderAllocations: list["OrderAllocation"] | None = None + orderAllocations: list[OrderAllocation] | None = None warningText: str = "" completedTime: str = "" completedStatus: str = "" diff --git a/ib_async/protobuf_converters/base_converters.py b/ib_async/protobuf_converters/base_converters.py index e27823c0..45a4980b 100644 --- a/ib_async/protobuf_converters/base_converters.py +++ b/ib_async/protobuf_converters/base_converters.py @@ -1,6 +1,5 @@ """Base converters for protobuf messages.""" -from typing import List from ..contract import TagValue @@ -12,7 +11,7 @@ def __init__(self, code, message, text): self.text = text -def fillTagValueList(tagValueList: List[TagValue], orderProtoMap: dict): +def fillTagValueList(tagValueList: list[TagValue], orderProtoMap: dict): if tagValueList is not None and tagValueList: for tagValue in tagValueList: orderProtoMap[tagValue.tag] = tagValue.value diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py index 73f31eac..754469b8 100644 --- a/ib_async/protobuf_converters/contract_converters.py +++ b/ib_async/protobuf_converters/contract_converters.py @@ -32,12 +32,11 @@ from ..protobuf.SecDefOptParamsRequest_pb2 import ( SecDefOptParamsRequest as SecDefOptParamsRequestProto, ) +from ..protobuf.SmartComponents_pb2 import SmartComponents as SmartComponentsProto from ..protobuf.SmartComponentsRequest_pb2 import ( SmartComponentsRequest as SmartComponentsRequestProto, ) -from ..protobuf.SmartComponents_pb2 import SmartComponents as SmartComponentsProto from ..util import ( - UNSET_DOUBLE, floatMaxString, getEnumTypeFromString, isValidIntValue, diff --git a/ib_async/protobuf_converters/historical_data_converters.py b/ib_async/protobuf_converters/historical_data_converters.py index f7b3e2a3..f6bef8b4 100644 --- a/ib_async/protobuf_converters/historical_data_converters.py +++ b/ib_async/protobuf_converters/historical_data_converters.py @@ -71,8 +71,8 @@ parseIBDatetime, parseIBTimeStamp, ) -from .contract_converters import createContractProto from .base_converters import ClientException, fillTagValueList +from .contract_converters import createContractProto def createHeadTimestampRequestProto( @@ -308,10 +308,10 @@ def createHistoricalTickShim( def createTickByTick( tickByTickData: TickByTickDataProto, tz: tzinfo -) -> HistoricalTickType|None: +) -> HistoricalTickType | None: tickType = tickByTickData.tickType if tickByTickData.HasField("tickType") else 0 if tickType == 0: - raise ValueError("%s: Invalid tick type: %r",__name__,tickByTickData) + raise ValueError("%s: Invalid tick type: %r", __name__, tickByTickData) elif tickType == 1 or tickType == 2: # Last or AllLast if tickByTickData.HasField("historicalTickLast"): @@ -331,6 +331,7 @@ def createTickByTick( return tick_mid return None + def createHistogramDataRequestProto( reqId: int, contract: Contract, useRTH: bool, timePeriod: str ) -> HistogramDataRequestProto: diff --git a/ib_async/protobuf_converters/market_data_converters.py b/ib_async/protobuf_converters/market_data_converters.py index bc2b07f4..25414cf7 100644 --- a/ib_async/protobuf_converters/market_data_converters.py +++ b/ib_async/protobuf_converters/market_data_converters.py @@ -3,6 +3,7 @@ """ from typing import Any, Callable, TypeAlias + from ..objects import ( Contract, OptionComputation, @@ -187,18 +188,16 @@ def createTickGenericData(msg: TickGenericProto) -> TickGenericData: tickGeneric = TickGenericData(reqId, tickType, value) return tickGeneric -TickDeliveryProto:TypeAlias = ( - TickPriceProto - | TickSizeProto - | TickStringProto - | TickGenericProto + +TickDeliveryProto: TypeAlias = ( + TickPriceProto | TickSizeProto | TickStringProto | TickGenericProto ) def createTickData(msg: TickDeliveryProto) -> TickDeliveryType: delivery_map: dict[type[TickDeliveryProto], Callable[[Any], TickDeliveryType]] = { TickPriceProto: createTickPriceData, - TickSizeProto: createTickSizeData, + TickSizeProto: createTickSizeData, TickStringProto: createTickStringData, TickGenericProto: createTickGenericData, } @@ -207,7 +206,9 @@ def createTickData(msg: TickDeliveryProto) -> TickDeliveryType: if create_method is None: # runtime error - raise ValueError(f"createTickData - no converter found for tick delivery type: {type(msg)}") + raise ValueError( + f"createTickData - no converter found for tick delivery type: {type(msg)}" + ) return create_method(msg) diff --git a/ib_async/protobuf_converters/subscription_converters.py b/ib_async/protobuf_converters/subscription_converters.py index 1b8c36c3..028d2a88 100644 --- a/ib_async/protobuf_converters/subscription_converters.py +++ b/ib_async/protobuf_converters/subscription_converters.py @@ -1,6 +1,6 @@ +from ib_async.contract import ContractDetails, ScanData from ib_async.objects import ScannerSubscription, TagValue from ib_async.util import UNSET_DOUBLE, isValidIntValue -from ib_async.contract import ScanData, ContractDetails from ..protobuf.CancelPnL_pb2 import CancelPnL as CancelPnLProto from ..protobuf.CancelPnLSingle_pb2 import CancelPnLSingle as CancelPnLSingleProto @@ -9,18 +9,18 @@ ) from ..protobuf.PnLRequest_pb2 import PnLRequest as PnLRequestProto from ..protobuf.PnLSingleRequest_pb2 import PnLSingleRequest as PnLSingleRequestProto +from ..protobuf.ScannerData_pb2 import ScannerData as ScannerDataProto from ..protobuf.ScannerParametersRequest_pb2 import ( ScannerParametersRequest as ScannerParametersRequestProto, ) from ..protobuf.ScannerSubscription_pb2 import ( ScannerSubscription as ScannerSubscriptionProto, ) -from ..protobuf.ScannerData_pb2 import ScannerData as ScannerDataProto from ..protobuf.ScannerSubscriptionRequest_pb2 import ( ScannerSubscriptionRequest as ScannerSubscriptionRequestProto, ) -from .historical_data_converters import fillTagValueList from .contract_converters import createContract +from .historical_data_converters import fillTagValueList def createScannerParametersRequestProto() -> ScannerParametersRequestProto: diff --git a/ib_async/protobuf_converters/trade_converters.py b/ib_async/protobuf_converters/trade_converters.py index ca5b19a5..29bb28d7 100644 --- a/ib_async/protobuf_converters/trade_converters.py +++ b/ib_async/protobuf_converters/trade_converters.py @@ -38,7 +38,7 @@ getEnumTypeFromString, isValidIntValue, parseIBDatetime, - quantize_decimals + quantize_decimals, ) from ..protobuf.CancelOrderRequest_pb2 import ( @@ -533,6 +533,7 @@ def createSoftDollarTierProto(order: Order) -> SoftDollarTierProto: softDollarTierProto.displayName = tier.displayName return softDollarTierProto + @quantize_decimals() def createOrder( orderId: int, contractProto: ContractProto, orderProto: OrderProto @@ -989,6 +990,7 @@ def createTagValueList(protoMap: dict[str, str]) -> list[TagValue]: tagValueList.append(tagValue) return tagValueList + @quantize_decimals() def createOrderState(orderStateProto: OrderStateProto) -> OrderState: orderState = OrderState() @@ -999,25 +1001,19 @@ def createOrderState(orderStateProto: OrderStateProto) -> OrderState: if orderStateProto.HasField("maintMarginBefore"): orderState.maintMarginBefore = Decimal(orderStateProto.maintMarginBefore) if orderStateProto.HasField("equityWithLoanBefore"): - orderState.equityWithLoanBefore = Decimal( - orderStateProto.equityWithLoanBefore - ) + orderState.equityWithLoanBefore = Decimal(orderStateProto.equityWithLoanBefore) if orderStateProto.HasField("initMarginChange"): orderState.initMarginChange = Decimal(orderStateProto.initMarginChange) if orderStateProto.HasField("maintMarginChange"): orderState.maintMarginChange = Decimal(orderStateProto.maintMarginChange) if orderStateProto.HasField("equityWithLoanChange"): - orderState.equityWithLoanChange = Decimal( - orderStateProto.equityWithLoanChange - ) + orderState.equityWithLoanChange = Decimal(orderStateProto.equityWithLoanChange) if orderStateProto.HasField("initMarginAfter"): orderState.initMarginAfter = Decimal(orderStateProto.initMarginAfter) if orderStateProto.HasField("maintMarginAfter"): orderState.maintMarginAfter = Decimal(orderStateProto.maintMarginAfter) if orderStateProto.HasField("equityWithLoanAfter"): - orderState.equityWithLoanAfter = Decimal( - orderStateProto.equityWithLoanAfter - ) + orderState.equityWithLoanAfter = Decimal(orderStateProto.equityWithLoanAfter) if orderStateProto.HasField("commissionAndFees"): orderState.commission = Decimal(orderStateProto.commissionAndFees) if orderStateProto.HasField("minCommissionAndFees"): @@ -1055,11 +1051,17 @@ def createOrderState(orderStateProto: OrderStateProto) -> OrderState: orderStateProto.equityWithLoanChangeOutsideRTH ) if orderStateProto.HasField("initMarginAfterOutsideRTH"): - orderState.initMarginAfterOutsideRTH = Decimal(orderStateProto.initMarginAfterOutsideRTH) + orderState.initMarginAfterOutsideRTH = Decimal( + orderStateProto.initMarginAfterOutsideRTH + ) if orderStateProto.HasField("maintMarginAfterOutsideRTH"): - orderState.maintMarginAfterOutsideRTH = Decimal(orderStateProto.maintMarginAfterOutsideRTH) + orderState.maintMarginAfterOutsideRTH = Decimal( + orderStateProto.maintMarginAfterOutsideRTH + ) if orderStateProto.HasField("equityWithLoanAfterOutsideRTH"): - orderState.equityWithLoanAfterOutsideRTH = Decimal(orderStateProto.equityWithLoanAfterOutsideRTH) + orderState.equityWithLoanAfterOutsideRTH = Decimal( + orderStateProto.equityWithLoanAfterOutsideRTH + ) if orderStateProto.HasField("suggestedSize"): orderState.suggestedSize = Decimal(orderStateProto.suggestedSize) if orderStateProto.HasField("rejectReason"): @@ -1076,6 +1078,7 @@ def createOrderState(orderStateProto: OrderStateProto) -> OrderState: return orderState + def createOrderAllocations(orderStateProto: OrderStateProto) -> list[OrderAllocation]: orderAllocations = [] orderAllocationProtoList = [] @@ -1118,6 +1121,7 @@ def createContractFromExecutionDetails( """ return createContract(exec_details_proto.contract) + @quantize_decimals() def createOrderStatus(orderStatusProto: OrderStatusProto) -> OrderStatus: orderStatus = OrderStatus() diff --git a/ib_async/ticker.py b/ib_async/ticker.py index 50e22b35..5c7a0bf3 100644 --- a/ib_async/ticker.py +++ b/ib_async/ticker.py @@ -1,10 +1,10 @@ """Access to realtime market information.""" -from decimal import Decimal import logging from contextlib import suppress from dataclasses import dataclass, field from datetime import datetime +from decimal import Decimal from typing import ClassVar, Final, TypeAlias from eventkit import Event, Op @@ -14,12 +14,12 @@ Dividends, DOMLevel, FundamentalRatios, + HistoricalTick, + HistoricalTickBidAsk, + HistoricalTickLast, IBDefaults, MktDepthData, OptionComputation, - HistoricalTickLast, - HistoricalTickBidAsk, - HistoricalTick, TickComputationData, TickData, TickDataType, @@ -474,7 +474,7 @@ def _on_tick_string(self, tick_string: TickStringData, last_time: datetime): }: timestamp = int(tick_string.value) - # only populate if timestamp isn't '0' (we don't want to report "last + # only populate if timestamp isn't '0' (we don't want to report "last # trade: 20,000 days ago") if timestamp: self.lastTimestamp = datetime.fromtimestamp( diff --git a/ib_async/util.py b/ib_async/util.py index 0ac39a88..d93b3f14 100644 --- a/ib_async/util.py +++ b/ib_async/util.py @@ -8,22 +8,16 @@ import signal import sys import time +from collections.abc import AsyncIterator, Awaitable, Iterator from dataclasses import fields, is_dataclass -from decimal import Decimal +from decimal import ROUND_HALF_UP, Decimal +from functools import wraps from typing import ( Any, - AsyncIterator, - Awaitable, Callable, Final, - Iterator, - List, - Optional, TypeAlias, - Union, ) -from functools import wraps -from decimal import ROUND_HALF_UP import eventkit as ev @@ -47,7 +41,7 @@ Time_t: TypeAlias = dt.time | dt.datetime -def df(objs, labels: Optional[List[str]] = None): +def df(objs, labels: list[str]|None = None): """ Create pandas DataFrame from the sequence of same-type objects. @@ -315,7 +309,7 @@ def formatSI(n: float) -> str: log = int(math.floor(math.log10(n))) i, j = divmod(log, 3) for _try in range(2): - templ = "%.{}f".format(2 - j) + templ = f"%.{2 - j}f" val = templ % (n * 10 ** (-3 * i)) if val != "1000": break @@ -341,7 +335,7 @@ def __exit__(self, *_args): print(self.title + " took " + formatSI(time.time() - self.t0) + "s") -def run(*awaitables: Awaitable, timeout: Optional[float] = None): +def run(*awaitables: Awaitable, timeout: float|None = None): """ By default run the event loop forever. @@ -572,7 +566,7 @@ def qt_step(): qt_step() -def formatIBDatetime(t: Union[dt.date, dt.datetime, str, None]) -> str: +def formatIBDatetime(t: dt.date| dt.datetime| str| None) -> str: """Format date or datetime to string that IB uses.""" if not t: s = "" @@ -591,7 +585,7 @@ def formatIBDatetime(t: Union[dt.date, dt.datetime, str, None]) -> str: return s -def parseIBDatetime(s: str) -> Union[dt.date, dt.datetime]: +def parseIBDatetime(s: str) -> dt.date| dt.datetime: """Parse string in IB date or datetime format to datetime.""" if len(s) == 8: # YYYYmmdd @@ -647,12 +641,13 @@ def listOfValues(cls): def isValidIntValue(val: int) -> bool: return val != UNSET_INTEGER + def quantize_decimals(places=2, rounding=ROUND_HALF_UP): """ Decorator that finds all Decimal fields in a returned dataclass object and quantizes them to a given number of decimal places. """ - quantizer = Decimal('0.1') ** places + quantizer = Decimal("0.1") ** places def decorator(func): @wraps(func) @@ -668,5 +663,7 @@ def wrapper(*args, **kwargs): quantized_value = value.quantize(quantizer, rounding=rounding) setattr(result, field.name, quantized_value) return result + return wrapper + return decorator diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 222ce685..aa7ffb6b 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -6,17 +6,15 @@ import logging import time from collections import defaultdict +from collections.abc import Hashable from dataclasses import dataclass, field from datetime import datetime from typing import ( TYPE_CHECKING, Any, Generic, - Hashable, - Optional, TypeAlias, TypeVar, - Union, cast, ) from weakref import WeakKeyDictionary @@ -196,7 +194,7 @@ def get_request_id(self, object_id: K) -> int | None: """ return self._request_by_object_id.get(object_id) - def get_request_id_by_object(self, obj: V) -> Optional[int]: + def get_request_id_by_object(self, obj: V) -> int | None: """Get reqquest_od by object. Only works if track_objects_weakly=True @@ -311,7 +309,7 @@ class Wrapper: """ # reference back to IB so wrapper can access API methods - ib: "IB" + ib: IB accountValues: dict[tuple, AccountValue] = field(init=False) """ (account, tag, currency, modelCode) -> AccountValue """ diff --git a/tests/tests_util.py b/tests/tests_util.py new file mode 100644 index 00000000..20f8a5cb --- /dev/null +++ b/tests/tests_util.py @@ -0,0 +1,739 @@ + +import asyncio +import datetime as dt +import enum +import logging +import math +import sys +import unittest +from dataclasses import dataclass, field +from decimal import ROUND_HALF_UP, Decimal +from unittest.mock import AsyncMock, MagicMock, call, patch + +from ib_async.util import ( + EPOCH, + NO_VALID_ID, + UNSET_DECIMAL, + UNSET_DOUBLE, + UNSET_INTEGER, + _fillDate, + dataclassAsDict, + dataclassAsTuple, + dataclassNonDefaults, + dataclassRepr, + dataclassUpdate, + decimalMaxString, + df, + floatMaxString, + formatIBDatetime, + formatSI, + getEnumTypeFromString, + getLoop, + globalErrorEvent, + isnamedtupleinstance, + isNan, + isValidIntValue, + listOfValues, + parseIBDatetime, + parseIBTimeStamp, + patchAsyncio, + quantize_decimals, + run, + schedule, + sleep, + timeit, + timeRange, + timeRangeAsync, + tree, + waitUntil, + waitUntilAsync, +) + + +# Dummy dataclasses for testing +@dataclass +class TestDataClass: + a: int + b: str = "default" + c: float = 1.0 + d: Decimal = Decimal("1.23") + e: list = field(default_factory=list) + f: dict = field(default_factory=dict) + +@dataclass +class AnotherTestDataClass: + x: int = 10 + y: str = "hello" + +@dataclass +class DataClassWithDecimal: + value: Decimal + +# Dummy Enum for testing +class TestEnum(enum.Enum): + ALPHA = "A", "AlphaValue" + BETA = "B", "BetaValue" + GAMMA = "C", "GammaValue" + + def __init__(self, char_val, description): + self.char_val = char_val + self.description = description + + @property + def value(self): + return self.char_val, self.description + + @classmethod + def from_char(cls, char_val): + for member in cls: + if member.char_val == char_val: + return member + raise ValueError(f"No member with char_val '{char_val}'") + + +class TestUtil(unittest.TestCase): + + def setUp(self): + # Reset globalErrorEvent before each test + globalErrorEvent.clear() + + def test_dataclassAsDict(self): + obj = TestDataClass(a=1, b="test") + expected = {"a": 1, "b": "test", "c": 1.0, "d": Decimal("1.23"), "e": [], "f": {}} + self.assertEqual(dataclassAsDict(obj), expected) + + with self.assertRaises(TypeError): + dataclassAsDict(123) + + def test_dataclassAsTuple(self): + obj = TestDataClass(a=1, b="test") + expected = (1, "test", 1.0, Decimal("1.23"), [], {}) + self.assertEqual(dataclassAsTuple(obj), expected) + + with self.assertRaises(TypeError): + dataclassAsTuple("not_a_dataclass") + + def test_dataclassNonDefaults(self): + obj1 = TestDataClass(a=1) + self.assertEqual(dataclassNonDefaults(obj1), {"a": 1}) + + obj2 = TestDataClass(a=1, b="non-default", c=2.0, d=Decimal("3.45")) + expected2 = {"a": 1, "b": "non-default", "c": 2.0, "d": Decimal("3.45")} + self.assertEqual(dataclassNonDefaults(obj2), expected2) + + obj3 = TestDataClass(a=1, e=[1,2]) + self.assertEqual(dataclassNonDefaults(obj3), {"a":1, "e":[1,2]}) + + with self.assertRaises(TypeError): + dataclassNonDefaults({"a": 1}) + + def test_dataclassUpdate(self): + obj = TestDataClass(a=1, b="original") + src_obj = AnotherTestDataClass(x=20, y="updated") + + # Test update from kwargs + dataclassUpdate(obj, a=5, b="new") + self.assertEqual(obj.a, 5) + self.assertEqual(obj.b, "new") + + # Test update from another dataclass (only matching fields) + dataclassUpdate(obj, src_obj, a=100) # src_obj doesn't have 'a' or 'b' + self.assertEqual(obj.a, 100) + self.assertEqual(obj.b, "new") # Should remain unchanged as src_obj doesn't have 'b' + self.assertEqual(obj.c, 1.0) # Should remain unchanged as src_obj doesn't have 'c' + + # Test update from dataclass where fields match + @dataclass + class MatchingSrc: + a: int = 0 + c: float = 0.0 + match_src = MatchingSrc(a=99, c=9.9) + dataclassUpdate(obj, match_src) + self.assertEqual(obj.a, 99) + self.assertEqual(obj.c, 9.9) + self.assertEqual(obj.b, "new") + + with self.assertRaises(TypeError): + dataclassUpdate("not_a_dataclass", a=1) + + def test_dataclassRepr(self): + obj1 = TestDataClass(a=1) + self.assertEqual(dataclassRepr(obj1), "TestDataClass(a=1)") + + obj2 = TestDataClass(a=1, b="custom") + self.assertEqual(dataclassRepr(obj2), "TestDataClass(a=1, b='custom')") + + obj3 = TestDataClass(a=1, c=2.5) + self.assertEqual(dataclassRepr(obj3), "TestDataClass(a=1, c=2.5)") + + obj4 = TestDataClass(a=1, e=[1,2,3]) + self.assertEqual(dataclassRepr(obj4), "TestDataClass(a=1, e=[1, 2, 3])") + + def test_isnamedtupleinstance(self): + from collections import namedtuple + Point = namedtuple("Point", "x y") + p = Point(1, 2) + self.assertTrue(isnamedtupleinstance(p)) + self.assertFalse(isnamedtupleinstance((1, 2))) + self.assertFalse(isnamedtupleinstance([1, 2])) + self.assertFalse(isnamedtupleinstance(TestDataClass(a=1))) + + def test_tree(self): + # Basic types + self.assertEqual(tree(1), 1) + self.assertEqual(tree("hello"), "hello") + self.assertEqual(tree(True), True) + self.assertEqual(tree(1.5), 1.5) + self.assertEqual(tree(b"bytes"), b"bytes") + + # Datetime/date + d = dt.date(2023, 1, 1) + self.assertEqual(tree(d), "2023-01-01") + dt_obj = dt.datetime(2023, 1, 1, 10, 30, 0) + self.assertEqual(tree(dt_obj), "2023-01-01T10:30:00") + + # Dict + self.assertEqual(tree({"a": 1, "b": "c"}), {"a": 1, "b": "c"}) + + # Namedtuple + from collections import namedtuple + Point = namedtuple("Point", "x y") + p = Point(1, "two") + self.assertEqual(tree(p), {"x": 1, "y": "two"}) + + # List, tuple, set + self.assertEqual(tree([1, 2, "a"]), [1, 2, "a"]) + self.assertEqual(tree((1, 2, "a")), [1, 2, "a"]) + self.assertEqual(tree({1, 2, 3}), [1, 2, 3]) # Note: sets become lists, order not guaranteed + + # Dataclass + obj = TestDataClass(a=1, b="custom") + expected_dataclass_tree = {"TestDataClass": {"a": 1, "b": "custom"}} + self.assertEqual(tree(obj), expected_dataclass_tree) + + # Nested structure + nested_obj = { + "num": 1, + "text": "test", + "date": dt.date(2024, 5, 10), + "data": TestDataClass(a=5, c=3.3), + "items": [Point(10, 20), {"key": "value"}], + } + expected_nested_tree = { + "num": 1, + "text": "test", + "date": "2024-05-10", + "data": {"TestDataClass": {"a": 5, "c": 3.3}}, + "items": [{"x": 10, "y": 20}, {"key": "value"}], + } + self.assertEqual(tree(nested_obj), expected_nested_tree) + + # Other objects + class CustomClass: + def __str__(self): + return "Custom" + self.assertEqual(tree(CustomClass()), "Custom") + + def test_isNan(self): + self.assertTrue(isNan(float("nan"))) + self.assertFalse(isNan(1.0)) + self.assertFalse(isNan(0.0)) + self.assertFalse(isNan(float("inf"))) + self.assertFalse(isNan(-1.0)) + + def test_formatSI(self): + self.assertEqual(formatSI(0), "0.00 ") + self.assertEqual(formatSI(1), "1.00 ") + self.assertEqual(formatSI(10), "10.0 ") + self.assertEqual(formatSI(100), "100 ") + self.assertEqual(formatSI(1000), "1.00 k") + self.assertEqual(formatSI(1234), "1.23 k") + self.assertEqual(formatSI(1234567), "1.23 M") + self.assertEqual(formatSI(0.1), "100 m") + self.assertEqual(formatSI(0.01), "10.0 m") + self.assertEqual(formatSI(0.001), "1.00 m") + self.assertEqual(formatSI(0.000000001), "1.00 n") + self.assertEqual(formatSI(-1234), "-1.23 k") + self.assertEqual(formatSI(1000000000000000000000000.0), "1.00 Y") # Yotta + self.assertEqual(formatSI(0.000000000000000000000001), "1.00 y") # yocto + self.assertEqual(formatSI(0.0000000000000000000000001), "0.00 ") # smaller than yocto + + @patch('builtins.print') + @patch('time.time', side_effect=[0, 1.5]) + def test_timeit(self, mock_time, mock_print): + with timeit("Test Run"): + pass + mock_print.assert_called_once_with("Test Run took 1.50 s") + + @patch('builtins.print') + @patch('time.time', side_effect=[0, 12345.678]) + def test_timeit_format_si(self, mock_time, mock_print): + with timeit("Long Run"): + pass + mock_print.assert_called_once_with("Long Run took 12.3 k s") + + def test_formatIBDatetime(self): + self.assertEqual(formatIBDatetime(None), "") + + # Datetime + dt_obj = dt.datetime(2023, 1, 15, 10, 30, 45, tzinfo=dt.timezone.utc) + self.assertEqual(formatIBDatetime(dt_obj), "20230115 10:30:45 UTC") + + dt_obj_ny = dt.datetime(2023, 1, 15, 5, 30, 45, tzinfo=dt.timezone(dt.timedelta(hours=-5))) # EST + # Should convert to UTC: 2023-01-15 10:30:45 UTC + self.assertEqual(formatIBDatetime(dt_obj_ny), "20230115 10:30:45 UTC") + + # Date + date_obj = dt.date(2023, 2, 1) + # Should convert to end of day UTC: 2023-02-01 23:59:59 UTC + self.assertEqual(formatIBDatetime(date_obj), "20230201 23:59:59 UTC") + + # String (pass-through) + self.assertEqual(formatIBDatetime("20230301 12:00:00"), "20230301 12:00:00") + + @patch('ib_async.util.ZoneInfo', side_effect=lambda x: dt.timezone.utc if x == 'Europe/Amsterdam' else dt.timezone.utc) # Mock ZoneInfo + def test_parseIBDatetime(self, MockZoneInfo): + from zoneinfo import ZoneInfo as RealZoneInfo # For direct comparison + + # YYYYmmdd + self.assertEqual(parseIBDatetime("20230101"), dt.date(2023, 1, 1)) + + # Timestamp as string + timestamp_str = str(int(dt.datetime(2023, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc).timestamp())) + self.assertEqual(parseIBDatetime(timestamp_str), dt.datetime(2023, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc)) + + # YYYYmmdd HH:MM:SS Europe/Amsterdam + with patch('ib_async.util.ZoneInfo', side_effect=lambda x: RealZoneInfo('Europe/Amsterdam') if x == 'Europe/Amsterdam' else dt.timezone.utc): + result = parseIBDatetime("20221125 10:00:00 Europe/Amsterdam") + expected = dt.datetime(2022, 11, 25, 10, 0, 0, tzinfo=RealZoneInfo('Europe/Amsterdam')) + self.assertEqual(result, expected) + + # YYYYmmdd HH:MM:SS + self.assertEqual(parseIBDatetime("20230101 12:30:45"), dt.datetime(2023, 1, 1, 12, 30, 45)) + + # YYYY-mm-dd HH:MM:SS.0 + self.assertEqual(parseIBDatetime("2023-01-01 12:30:45.0"), dt.datetime(2023, 1, 1, 12, 30, 45)) + self.assertEqual(parseIBDatetime("2023-01-01 12:30:45"), dt.datetime(2023, 1, 1, 12, 30, 45)) + + def test_parseIBTimeStamp(self): + timestamp = 1672531200 # 2023-01-01 00:00:00 UTC + expected_dt = dt.datetime(2023, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc) + self.assertEqual(parseIBTimeStamp(timestamp), expected_dt) + + # With different timezone + ny_tz = dt.timezone(dt.timedelta(hours=-5)) + expected_dt_ny = dt.datetime(2023, 1, 1, 0, 0, 0, tzinfo=ny_tz) + self.assertEqual(parseIBTimeStamp(timestamp, tz=ny_tz), expected_dt_ny) + + def test_decimalMaxString(self): + self.assertEqual(decimalMaxString(Decimal("123.45")), "123.45") + self.assertEqual(decimalMaxString(UNSET_DECIMAL), "") + self.assertEqual(decimalMaxString(Decimal("0")), "0") + + def test_floatMaxString(self): + self.assertEqual(floatMaxString(123.456789), "123.456789") + self.assertEqual(floatMaxString(123.000000), "123") + self.assertEqual(floatMaxString(UNSET_DOUBLE), "") + self.assertEqual(floatMaxString(0.0), "0") + self.assertEqual(floatMaxString(None), "") + + def test_getEnumTypeFromString(self): + self.assertEqual(getEnumTypeFromString(TestEnum, "A"), TestEnum.ALPHA) + self.assertEqual(getEnumTypeFromString(TestEnum, "B"), TestEnum.BETA) + self.assertEqual(getEnumTypeFromString(TestEnum, "C"), TestEnum.GAMMA) + # If not found, should return the first enum member + self.assertEqual(getEnumTypeFromString(TestEnum, "X"), TestEnum.ALPHA) + + def test_listOfValues(self): + expected_list = [TestEnum.ALPHA, TestEnum.BETA, TestEnum.GAMMA] + self.assertEqual(listOfValues(TestEnum), expected_list) + + def test_isValidIntValue(self): + self.assertTrue(isValidIntValue(10)) + self.assertTrue(isValidIntValue(0)) + self.assertFalse(isValidIntValue(UNSET_INTEGER)) + self.assertTrue(isValidIntValue(-1)) + + def test_quantize_decimals_decorator(self): + @quantize_decimals(places=2) + @dataclass + class MyResult: + price: Decimal + amount: Decimal + name: str + not_decimal: float = 1.234 + + res = MyResult(price=Decimal("12.3456"), amount=Decimal("7.891"), name="Test") + self.assertEqual(res.price, Decimal("12.35")) + self.assertEqual(res.amount, Decimal("7.89")) + self.assertEqual(res.name, "Test") + self.assertEqual(res.not_decimal, 1.234) # float should not be quantized + + # Test with different places and rounding + @quantize_decimals(places=0, rounding=ROUND_HALF_UP) + @dataclass + class MyIntResult: + count: Decimal + + res_int = MyIntResult(count=Decimal("5.6")) + self.assertEqual(res_int.count, Decimal("6")) + + # Test with a function that returns a non-dataclass + @quantize_decimals(places=2) + def return_non_dataclass(): + return "just a string" + + self.assertEqual(return_non_dataclass(), "just a string") + + # Test with a function that returns None + @quantize_decimals(places=2) + def return_none(): + return None + self.assertIsNone(return_none()) + + +class TestUtilAsync(unittest.IsolatedAsyncioTestCase): + + @patch('asyncio.get_running_loop') + def test_getLoop_running(self, mock_get_running_loop): + mock_loop = MagicMock() + mock_get_running_loop.return_value = mock_loop + self.assertEqual(getLoop(), mock_loop) + mock_get_running_loop.assert_called_once() + + @patch('asyncio.new_event_loop') + @patch('asyncio.set_event_loop') + @patch('asyncio.get_running_loop', side_effect=RuntimeError) + def test_getLoop_new(self, mock_get_running_loop, mock_set_event_loop, mock_new_event_loop): + mock_loop = MagicMock() + mock_new_event_loop.return_value = mock_loop + self.assertEqual(getLoop(), mock_loop) + mock_get_running_loop.assert_called_once() + mock_new_event_loop.assert_called_once() + mock_set_event_loop.assert_called_once_with(mock_loop) + + @patch('nest_asyncio.apply') + def test_patchAsyncio(self, mock_apply): + patchAsyncio() + mock_apply.assert_called_once() + + @patch('ib_async.util.getLoop') + async def test_run_no_awaitables(self, mock_getLoop): + mock_loop = AsyncMock() + mock_loop.is_running.return_value = False + mock_getLoop.return_value = mock_loop + + with patch('asyncio.all_tasks', return_value=[]): + run() + mock_loop.run_forever.assert_called_once() + # If no tasks, no cancellation attempts + mock_loop.run_until_complete.assert_not_called() + + mock_loop.reset_mock() + mock_loop.is_running.return_value = True + run() # Should do nothing if loop is already running + mock_loop.run_forever.assert_not_called() + + @patch('ib_async.util.getLoop') + @patch('asyncio.gather', new_callable=AsyncMock) + @patch('asyncio.ensure_future') + @patch('asyncio.wait_for', new_callable=AsyncMock) + async def test_run_with_awaitables(self, mock_wait_for, mock_ensure_future, mock_gather, mock_getLoop): + mock_loop = MagicMock() + mock_getLoop.return_value = mock_loop + mock_task = MagicMock() + mock_ensure_future.return_value = mock_task + mock_wait_for.return_value = MagicMock() # The future that wait_for returns + + # Test single awaitable + awaitable = AsyncMock(return_value="result1") + mock_loop.run_until_complete.return_value = "result1" + result = run(awaitable) + self.assertEqual(result, "result1") + mock_gather.assert_called_once_with(awaitable) # gather is called even for single + mock_ensure_future.assert_called_once() + mock_loop.run_until_complete.assert_called_once_with(mock_task) + mock_wait_for.assert_called_once_with(mock_gather.return_value, None) + mock_getLoop.assert_called() + + mock_gather.reset_mock() + mock_ensure_future.reset_mock() + mock_loop.run_until_complete.reset_mock() + mock_wait_for.reset_mock() + + # Test multiple awaitables + awaitable1 = AsyncMock(return_value="result1") + awaitable2 = AsyncMock(return_value="result2") + mock_loop.run_until_complete.return_value = ["result1", "result2"] + result = run(awaitable1, awaitable2) + self.assertEqual(result, ["result1", "result2"]) + mock_gather.assert_called_once_with(awaitable1, awaitable2) + mock_ensure_future.assert_called_once() + mock_loop.run_until_complete.assert_called_once_with(mock_task) + mock_wait_for.assert_called_once_with(mock_gather.return_value, None) + + mock_gather.reset_mock() + mock_ensure_future.reset_mock() + mock_loop.run_until_complete.reset_mock() + mock_wait_for.reset_mock() + + # Test with timeout + awaitable = AsyncMock(return_value="result_timeout") + mock_loop.run_until_complete.return_value = "result_timeout" + result = run(awaitable, timeout=5.0) + self.assertEqual(result, "result_timeout") + mock_wait_for.assert_called_once_with(mock_gather.return_value, 5.0) + + @patch('ib_async.util.getLoop') + @patch('asyncio.gather', new_callable=AsyncMock) + @patch('asyncio.ensure_future') + @patch('asyncio.all_tasks', return_value=[MagicMock()]) # Simulate active tasks + async def test_run_cancel_pending_tasks(self, mock_all_tasks, mock_ensure_future, mock_gather, mock_getLoop): + mock_loop = MagicMock() + mock_loop.is_running.return_value = False + mock_getLoop.return_value = mock_loop + + # Mock gather for cancellation + mock_cancel_future = MagicMock() + mock_gather.return_value = mock_cancel_future + mock_loop.run_until_complete.side_effect = [asyncio.CancelledError(), None] # First for gather.cancel, second for normal run + + run() # Call without awaitables, should try to cancel all tasks + mock_loop.run_forever.assert_called_once() + mock_all_tasks.assert_called_once() + mock_gather.assert_called_once_with(*mock_all_tasks.return_value) + mock_cancel_future.cancel.assert_called_once() + mock_loop.run_until_complete.assert_has_calls([call(mock_cancel_future)]) + + @patch('ib_async.util.getLoop') + @patch('asyncio.sleep') + async def test_sleep(self, mock_sleep, mock_getLoop): + mock_loop = MagicMock() + mock_getLoop.return_value = mock_loop + mock_sleep.return_value = None # asyncio.sleep is an awaitable that returns None + + result = sleep(0.1) + self.assertTrue(result) + mock_sleep.assert_awaited_once_with(0.1) + + def test_fillDate(self): + today = dt.date.today() + + # dt.time input + time_obj = dt.time(10, 30, 0) + expected_dt = dt.datetime(today.year, today.month, today.day, 10, 30, 0) + self.assertEqual(_fillDate(time_obj), expected_dt) + + # dt.datetime input + dt_obj = dt.datetime(2023, 1, 1, 11, 0, 0, tzinfo=dt.timezone.utc) + self.assertEqual(_fillDate(dt_obj), dt_obj) + + @patch('ib_async.util.getLoop') + def test_schedule(self, mock_getLoop): + mock_loop = MagicMock() + mock_getLoop.return_value = mock_loop + callback = MagicMock() + + now = dt.datetime.now() + target_time = now + dt.timedelta(seconds=10) + + schedule(target_time, callback, 1, 2, "test") + + # Check if call_later is called with approximately 10 seconds delay + # We need to account for tiny time differences during test execution + mock_loop.call_later.assert_called_once() + args, _ = mock_loop.call_later.call_args + delay = args[0] + self.assertAlmostEqual(delay, 10, delta=0.1) + self.assertEqual(args[1], callback) + self.assertEqual(args[2:], (1, 2, "test")) + + # Test with dt.time + mock_loop.reset_mock() + time_obj = dt.time(now.hour, (now.minute + 1) % 60, now.second) + schedule(time_obj, callback) + mock_loop.call_later.assert_called_once() + + @patch('ib_async.util.run') + @patch('datetime.datetime') + def test_waitUntil(self, mock_datetime, mock_run): + # Mock datetime.datetime.now() + mock_now = dt.datetime(2023, 1, 1, 10, 0, 0) + mock_datetime.now.return_value = mock_now + mock_datetime.side_effect = lambda *args, **kw: dt.datetime(*args, **kw) # Allow normal datetime creation + mock_datetime.today.return_value = dt.date(2023, 1, 1) + + target_time = dt.datetime(2023, 1, 1, 10, 0, 5) + waitUntil(target_time) + mock_run.assert_called_once() + args, _ = mock_run.call_args + # Expecting asyncio.sleep(5) + self.assertEqual(args[0].__name__, "sleep") # Accessing the coroutine object + self.assertEqual(args[0].cr_code.co_name, "sleep") # Verify it's the sleep coroutine + + mock_run.reset_mock() + # Test with dt.time + target_time_obj = dt.time(10, 0, 10) + waitUntil(target_time_obj) + mock_run.assert_called_once() + args, _ = mock_run.call_args + # Expecting asyncio.sleep(10) + self.assertEqual(args[0].__name__, "sleep") + self.assertEqual(args[0].cr_code.co_name, "sleep") + + + @patch('asyncio.sleep', new_callable=AsyncMock) + @patch('datetime.datetime') + async def test_waitUntilAsync(self, mock_datetime, mock_sleep): + mock_now = dt.datetime(2023, 1, 1, 10, 0, 0) + mock_datetime.now.return_value = mock_now + mock_datetime.side_effect = lambda *args, **kw: dt.datetime(*args, **kw) + mock_datetime.today.return_value = dt.date(2023, 1, 1) + + target_time = dt.datetime(2023, 1, 1, 10, 0, 5) + result = await waitUntilAsync(target_time) + self.assertTrue(result) + mock_sleep.assert_awaited_once_with(5) + + mock_sleep.reset_mock() + target_time_obj = dt.time(10, 0, 10) + result = await waitUntilAsync(target_time_obj) + self.assertTrue(result) + mock_sleep.assert_awaited_once_with(10) + + @patch('ib_async.util.waitUntil') + @patch('datetime.datetime') + def test_timeRange(self, mock_datetime, mock_waitUntil): + mock_now = dt.datetime(2023, 1, 1, 9, 58, 0) + mock_datetime.now.return_value = mock_now + mock_datetime.side_effect = lambda *args, **kw: dt.datetime(*args, **kw) + mock_datetime.today.return_value = dt.date(2023, 1, 1) + + start_time = dt.time(10, 0, 0) + end_time = dt.time(10, 0, 5) + step = 1 + + expected_times = [ + dt.datetime(2023, 1, 1, 10, 0, 0), + dt.datetime(2023, 1, 1, 10, 0, 1), + dt.datetime(2023, 1, 1, 10, 0, 2), + dt.datetime(2023, 1, 1, 10, 0, 3), + dt.datetime(2023, 1, 1, 10, 0, 4), + dt.datetime(2023, 1, 1, 10, 0, 5), + ] + + # Collect results from the generator + results = list(timeRange(start_time, end_time, step)) + + self.assertEqual(results, expected_times) + self.assertEqual(mock_waitUntil.call_count, len(expected_times)) + + # Test with start time already passed + mock_datetime.now.return_value = dt.datetime(2023, 1, 1, 10, 0, 2) + results2 = list(timeRange(start_time, end_time, step)) + self.assertEqual(results2, expected_times[2:]) + + + @patch('ib_async.util.waitUntilAsync') + @patch('datetime.datetime') + async def test_timeRangeAsync(self, mock_datetime, mock_waitUntilAsync): + mock_now = dt.datetime(2023, 1, 1, 9, 58, 0) + mock_datetime.now.return_value = mock_now + mock_datetime.side_effect = lambda *args, **kw: dt.datetime(*args, **kw) + mock_datetime.today.return_value = dt.date(2023, 1, 1) + + start_time = dt.time(10, 0, 0) + end_time = dt.time(10, 0, 5) + step = 1 + + expected_times = [ + dt.datetime(2023, 1, 1, 10, 0, 0), + dt.datetime(2023, 1, 1, 10, 0, 1), + dt.datetime(2023, 1, 1, 10, 0, 2), + dt.datetime(2023, 1, 1, 10, 0, 3), + dt.datetime(2023, 1, 1, 10, 0, 4), + dt.datetime(2023, 1, 1, 10, 0, 5), + ] + + results = [] + async for t in timeRangeAsync(start_time, end_time, step): + results.append(t) + + self.assertEqual(results, expected_times) + self.assertEqual(mock_waitUntilAsync.call_count, len(expected_times)) + + # Test with start time already passed + mock_datetime.now.return_value = dt.datetime(2023, 1, 1, 10, 0, 2) + results2 = [] + async for t in timeRangeAsync(start_time, end_time, step): + results2.append(t) + self.assertEqual(results2, expected_times[2:]) + + @patch('pandas.DataFrame') + @patch('pandas.read_sql') # Mocking to avoid actual DB interaction + def test_df(self, mock_read_sql, mock_dataframe): + # Mock pandas DataFrame creation + mock_instance = MagicMock() + mock_dataframe.from_records.return_value = mock_instance + mock_instance.__getitem__.return_value = MagicMock(values=[[1,2,3], [4,5,6]]) + mock_instance.drop.return_value = mock_instance + + # Test with dataclasses + obj1 = TestDataClass(a=1, b="one") + obj2 = TestDataClass(a=2, b="two") + + result_df = df([obj1, obj2]) + mock_dataframe.from_records.assert_called_once() + self.assertEqual(mock_dataframe.from_records.call_args[0][0], [(1, 'one', 1.0, Decimal('1.23'), [], {}), (2, 'two', 1.0, Decimal('1.23'), [], {})]) + self.assertEqual(result_df, mock_instance) + self.assertEqual(result_df.columns, ['a', 'b', 'c', 'd', 'e', 'f']) + + mock_dataframe.reset_mock() + # Test with DynamicObject (mocked) + class DynamicObjectMock: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + dyn_obj1 = DynamicObjectMock(id=1, name="Alpha") + dyn_obj2 = DynamicObjectMock(id=2, name="Beta") + + result_df_dyn = df([dyn_obj1, dyn_obj2]) + mock_dataframe.from_records.assert_called_once_with([{'id': 1, 'name': 'Alpha'}, {'id': 2, 'name': 'Beta'}]) + self.assertEqual(result_df_dyn, mock_instance) + + mock_dataframe.reset_mock() + # Test with namedtuple + from collections import namedtuple + NamedTupleMock = namedtuple("NamedTupleMock", "field1 field2") + nt_obj1 = NamedTupleMock(10, "ten") + nt_obj2 = NamedTupleMock(20, "twenty") + + result_df_nt = df([nt_obj1, nt_obj2]) + mock_dataframe.from_records.assert_called_once() + self.assertEqual(result_df_nt.columns, ('field1', 'field2')) + self.assertEqual(result_df_nt, mock_instance) + + mock_dataframe.reset_mock() + # Test with list of dicts + dict_obj1 = {"col1": 1, "col2": "a"} + dict_obj2 = {"col1": 2, "col2": "b"} + result_df_dict = df([dict_obj1, dict_obj2]) + mock_dataframe.from_records.assert_called_once_with([{'col1': 1, 'col2': 'a'}, {'col1': 2, 'col2': 'b'}]) + self.assertEqual(result_df_dict, mock_instance) + + mock_dataframe.reset_mock() + # Test with labels + obj = TestDataClass(a=1, b="one") + result_df_labels = df([obj], labels=["a"]) + mock_instance.drop.assert_called_once_with( + ['b', 'c', 'd', 'e', 'f'], axis=1 + ) + self.assertEqual(result_df_labels, mock_instance) + + mock_dataframe.reset_mock() + # Test empty list + self.assertIsNone(df([])) + +# To run tests +if __name__ == "__main__": + unittest.main() From 121ed669ca694bc72c8910d4d600c0c5062c064a Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:18:03 +0100 Subject: [PATCH 09/48] retrofit changes from 2.1.0 --- .github/workflows/test.yml | 14 +++++++--- ib_async/connection.py | 5 ++-- ib_async/flexreport.py | 42 +++++++++++++++++++++++++----- ib_async/util.py | 53 ++++++++++++++++++++++++++++---------- 4 files changed, 88 insertions(+), 26 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1b392414..5a2e315f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ "3.10", "3.11", "3.12", "3.13", "pypy3.10", "pypy3.11" ] + python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14", "pypy3.10", "pypy3.11" ] steps: - uses: actions/checkout@v4 @@ -18,10 +18,18 @@ jobs: with: python-version: ${{ matrix.python-version }} + - name: Install dependencies of dependencies + run: | + pip install pip poetry uv setuptools wheel -U + - name: Install dependencies run: | - pip install mypy types-dataclasses . + poetry install --with=dev - name: MyPy static code analysis run: | - mypy -p ib_async + poetry run mypy --pretty ib_async + + - name: Ruff check + run: | + poetry run ruff check diff --git a/ib_async/connection.py b/ib_async/connection.py index 56c2dd6a..27f19184 100644 --- a/ib_async/connection.py +++ b/ib_async/connection.py @@ -4,8 +4,6 @@ from eventkit import Event -from ib_async.util import getLoop - class Connection(asyncio.Protocol): """ @@ -37,7 +35,8 @@ async def connectAsync(self, host, port): self.disconnect() await self.disconnected self.reset() - loop = getLoop() + # Use get_running_loop() directly for optimal performance in async context + loop = asyncio.get_running_loop() self.transport, _ = await loop.create_connection(lambda: self, host, port) def disconnect(self): diff --git a/ib_async/flexreport.py b/ib_async/flexreport.py index 78991f1a..68caec19 100644 --- a/ib_async/flexreport.py +++ b/ib_async/flexreport.py @@ -1,9 +1,12 @@ """Access to account statement webservice.""" import logging +import os import time import xml.etree.ElementTree as et from contextlib import suppress +from typing import Final +from urllib.parse import urlparse from urllib.request import urlopen from ib_async import util @@ -11,6 +14,14 @@ _logger = logging.getLogger("ib_async.flexreport") +FLEXREPORT_URL: Final = ( + "https://ndcdyn.interactivebrokers.com/AccountManagement/" + "FlexWebService/SendRequest?" +) +""" +https://www.interactivebrokers.com/campus/ibkr-api-page/flex-web-service/#flex-generate-report +""" + class FlexError(Exception): pass @@ -33,6 +44,8 @@ def __init__(self, token=None, queryId=None, path=None): """ Download a report by giving a valid ``token`` and ``queryId``, or load from file by giving a valid ``path``. + + To overwrite default URL, set env variable ``IB_FLEXREPORT_URL``. """ if token and queryId: self.download(token, queryId) @@ -65,13 +78,30 @@ def df(self, topic: str, parseNumbers=True): """Same as extract but return the result as a pandas DataFrame.""" return util.df(self.extract(topic, parseNumbers)) + def get_url(self): + """Generate flexreport URL.""" + + def is_valid_url(url: str) -> bool: + try: + result = urlparse(url) + # Must have scheme (http/https) and netloc (domain) + return all([result.scheme, result.netloc]) + except Exception: + return False + + _url = os.getenv("IB_FLEXREPORT_URL", FLEXREPORT_URL) + if is_valid_url(_url): + return _url + raise FlexError( + "Invalid URL, please check that env variable IB_FLEXREPORT_URL is set correctly." + ) + def download(self, token, queryId): """Download report for the given ``token`` and ``queryId``.""" - url = ( - "https://gdcdyn.interactivebrokers.com" - f"/Universal/servlet/FlexStatementService.SendRequest?" - f"t={token}&q={queryId}&v=3" - ) + base_url = self.get_url() + query = f"t={token}&q={queryId}&v=3" + url = base_url + query + resp = urlopen(url) data = resp.read() @@ -94,7 +124,7 @@ def download(self, token, queryId): while True: time.sleep(1) - url = f"{baseUrl}?q={code}&t={token}" + url = f"{baseUrl}?q={code}&t={token}&v=3" resp = urlopen(url) self.data = resp.read() self.root = et.fromstring(self.data) diff --git a/ib_async/util.py b/ib_async/util.py index d93b3f14..f1d5eb77 100644 --- a/ib_async/util.py +++ b/ib_async/util.py @@ -18,15 +18,10 @@ Final, TypeAlias, ) +from zoneinfo import ZoneInfo import eventkit as ev -try: - from zoneinfo import ZoneInfo -except ImportError: - from backports.zoneinfo import ZoneInfo # type: ignore - - globalErrorEvent = ev.Event() """ Event to emit global exceptions. @@ -41,7 +36,7 @@ Time_t: TypeAlias = dt.time | dt.datetime -def df(objs, labels: list[str]|None = None): +def df(objs, labels: list[str] | None = None): """ Create pandas DataFrame from the sequence of same-type objects. @@ -335,7 +330,7 @@ def __exit__(self, *_args): print(self.title + " took " + formatSI(time.time() - self.t0) + "s") -def run(*awaitables: Awaitable, timeout: float|None = None): +def run(*awaitables: Awaitable, timeout: float | None = None): """ By default run the event loop forever. @@ -374,7 +369,8 @@ def run(*awaitables: Awaitable, timeout: float|None = None): if timeout: future = asyncio.wait_for(future, timeout) - task = asyncio.ensure_future(future) + # Pass loop explicitly to avoid deprecation warnings in Python 3.10+ + task = asyncio.ensure_future(future, loop=loop) def onError(_): task.cancel() @@ -505,13 +501,42 @@ def patchAsyncio(): nest_asyncio.apply() -@functools.cache def getLoop(): - """Get asyncio event loop or create one if it doesn't exist.""" + """ + Get asyncio event loop with smart fallback handling. + + This function is designed for use in synchronous contexts or when the + execution context is unknown. It will: + 1. Try to get the currently running event loop (if in async context) + 2. Fall back to getting the current thread's event loop via policy + 3. Create a new event loop if none exists or if the existing one is closed + + For performance-critical async code paths, prefer using + asyncio.get_running_loop() directly instead of this function. + + Note: This function does NOT cache the loop to avoid stale loop bugs + when loops are closed and recreated (e.g., in testing, Jupyter notebooks). + """ try: - # https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.get_running_loop + # Fast path: we're in an async context (coroutine or callback) loop = asyncio.get_running_loop() + return loop except RuntimeError: + pass + + # We're in a sync context or no loop is running + # Use the event loop policy to get the loop for this thread + # This avoids deprecation warnings from get_event_loop() in Python 3.10+ + try: + loop = asyncio.get_event_loop_policy().get_event_loop() + except RuntimeError: + # No event loop exists for this thread, create one + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + return loop + + # Check if the loop we got is closed - if so, create a new one + if loop.is_closed(): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) @@ -566,7 +591,7 @@ def qt_step(): qt_step() -def formatIBDatetime(t: dt.date| dt.datetime| str| None) -> str: +def formatIBDatetime(t: dt.date | dt.datetime | str | None) -> str: """Format date or datetime to string that IB uses.""" if not t: s = "" @@ -585,7 +610,7 @@ def formatIBDatetime(t: dt.date| dt.datetime| str| None) -> str: return s -def parseIBDatetime(s: str) -> dt.date| dt.datetime: +def parseIBDatetime(s: str) -> dt.date | dt.datetime: """Parse string in IB date or datetime format to datetime.""" if len(s) == 8: # YYYYmmdd From 5013f81deed7c74b6a9271820e7caf7cece9622b Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Thu, 18 Dec 2025 13:48:46 +0100 Subject: [PATCH 10/48] Changes - Update tests for utility functions in tests_util.py - remove quantize_decimals from trade_converters --- ib_async/contract.py | 19 +- .../protobuf_converters/trade_converters.py | 4 - tests/test_util.py | 671 ++++++++++++++++ tests/tests_util.py | 739 ------------------ 4 files changed, 681 insertions(+), 752 deletions(-) create mode 100644 tests/test_util.py delete mode 100644 tests/tests_util.py diff --git a/ib_async/contract.py b/ib_async/contract.py index 11310c48..f1c8f61e 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -31,6 +31,13 @@ class IneligibilityReason: description: str = field(default_factory=str) +@dataclass(slots=True) +class DeltaNeutralContract: + conId: int = 0 + delta: float = 0.0 + price: float = 0.0 + + @dataclass(slots=True) class Contract: """ @@ -126,7 +133,7 @@ class Contract: issuerId: str = "" comboLegsDescrip: str = "" comboLegs: list["ComboLeg"] = field(default_factory=list) - deltaNeutralContract: "DeltaNeutralContract" | None= None + deltaNeutralContract: DeltaNeutralContract | None = None @staticmethod def create(**kwargs) -> "Contract": @@ -197,7 +204,8 @@ def __hash__(self) -> int: if not self.isHashable(): raise ValueError( - f"Contract {self} can't be hashed because no 'conId' value exists. Qualify contract to populate 'conId'." + f"Contract {self} can't be hashed because no 'conId' value exists. " + "Qualify contract to populate 'conId'." ) if self.secType == "CONTFUT": @@ -582,13 +590,6 @@ class ComboLeg: exemptCode: int = -1 -@dataclass(slots=True) -class DeltaNeutralContract: - conId: int = 0 - delta: float = 0.0 - price: float = 0.0 - - @dataclass(slots=True, frozen=True) class TradingSession: start: dt.datetime diff --git a/ib_async/protobuf_converters/trade_converters.py b/ib_async/protobuf_converters/trade_converters.py index 29bb28d7..3c68357a 100644 --- a/ib_async/protobuf_converters/trade_converters.py +++ b/ib_async/protobuf_converters/trade_converters.py @@ -38,7 +38,6 @@ getEnumTypeFromString, isValidIntValue, parseIBDatetime, - quantize_decimals, ) from ..protobuf.CancelOrderRequest_pb2 import ( @@ -534,7 +533,6 @@ def createSoftDollarTierProto(order: Order) -> SoftDollarTierProto: return softDollarTierProto -@quantize_decimals() def createOrder( orderId: int, contractProto: ContractProto, orderProto: OrderProto ) -> Order: @@ -991,7 +989,6 @@ def createTagValueList(protoMap: dict[str, str]) -> list[TagValue]: return tagValueList -@quantize_decimals() def createOrderState(orderStateProto: OrderStateProto) -> OrderState: orderState = OrderState() if orderStateProto.HasField("status"): @@ -1122,7 +1119,6 @@ def createContractFromExecutionDetails( return createContract(exec_details_proto.contract) -@quantize_decimals() def createOrderStatus(orderStatusProto: OrderStatusProto) -> OrderStatus: orderStatus = OrderStatus() if orderStatusProto.HasField("orderId"): diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 00000000..c04f4979 --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,671 @@ +import asyncio +import datetime as dt +import enum +from dataclasses import dataclass, field +from decimal import ROUND_HALF_UP, Decimal +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from ib_async.util import ( + UNSET_DECIMAL, + UNSET_DOUBLE, + UNSET_INTEGER, + _fillDate, + dataclassAsDict, + dataclassAsTuple, + dataclassNonDefaults, + dataclassRepr, + dataclassUpdate, + decimalMaxString, + df, + floatMaxString, + formatIBDatetime, + formatSI, + getEnumTypeFromString, + getLoop, + globalErrorEvent, + isnamedtupleinstance, + isNan, + isValidIntValue, + listOfValues, + parseIBDatetime, + parseIBTimeStamp, + patchAsyncio, + quantize_decimals, + run, + schedule, + sleep, + timeit, + timeRange, + timeRangeAsync, + tree, + waitUntil, + waitUntilAsync, +) + + +# Dummy dataclasses for testing +@dataclass +class TestDataClass: + a: int + b: str = "default" + c: float = 1.0 + d: Decimal = Decimal("1.23") + e: list = field(default_factory=list) + f: dict = field(default_factory=dict) + + +@dataclass +class AnotherTestDataClass: + x: int = 10 + y: str = "hello" + + +@dataclass +class DataClassWithDecimal: + value: Decimal + + +# Dummy Enum for testing +class TestEnum(enum.Enum): + ALPHA = "A", "AlphaValue" + BETA = "B", "BetaValue" + GAMMA = "C", "GammaValue" + + def __init__(self, char_val, description): + self.char_val = char_val + self.description = description + + @property + def value(self): + return self.char_val, self.description + + @classmethod + def from_char(cls, char_val): + for member in cls: + if member.char_val == char_val: + return member + raise ValueError(f"No member with char_val '{char_val}'") + + +@pytest.fixture(autouse=True) +def clear_global_error_event(): + """Fixture to reset globalErrorEvent before each test.""" + globalErrorEvent.clear() + yield + + +def test_dataclassAsDict(): + obj = TestDataClass(a=1, b="test") + expected = { + "a": 1, + "b": "test", + "c": 1.0, + "d": Decimal("1.23"), + "e": [], + "f": {}, + } + assert dataclassAsDict(obj) == expected + + with pytest.raises(TypeError): + dataclassAsDict(123) + + +def test_dataclassAsTuple(): + obj = TestDataClass(a=1, b="test") + expected = (1, "test", 1.0, Decimal("1.23"), [], {}) + assert dataclassAsTuple(obj) == expected + + with pytest.raises(TypeError): + dataclassAsTuple("not_a_dataclass") + + +def test_dataclassNonDefaults(): + obj1 = TestDataClass(a=1) + assert dataclassNonDefaults(obj1) == {"a": 1} + + obj2 = TestDataClass(a=1, b="non-default", c=2.0, d=Decimal("3.45")) + expected2 = {"a": 1, "b": "non-default", "c": 2.0, "d": Decimal("3.45")} + assert dataclassNonDefaults(obj2) == expected2 + + obj3 = TestDataClass(a=1, e=[1, 2]) + assert dataclassNonDefaults(obj3) == {"a": 1, "e": [1, 2]} + + with pytest.raises(TypeError): + dataclassNonDefaults({"a": 1}) + + +def test_dataclassUpdate(): + obj = TestDataClass(a=1, b="original") + src_obj = AnotherTestDataClass(x=20, y="updated") + + # Test update from kwargs + dataclassUpdate(obj, a=5, b="new") + assert obj.a == 5 + assert obj.b == "new" + + # Test update from another dataclass (only matching fields) + dataclassUpdate(obj, src_obj, a=100) # src_obj doesn't have 'a' or 'b' + assert obj.a == 100 + assert obj.b == "new" + assert obj.c == 1.0 + + # Test update from dataclass where fields match + @dataclass + class MatchingSrc: + a: int = 0 + c: float = 0.0 + + match_src = MatchingSrc(a=99, c=9.9) + dataclassUpdate(obj, match_src) + assert obj.a == 99 + assert obj.c == 9.9 + assert obj.b == "new" + + with pytest.raises(TypeError): + dataclassUpdate("not_a_dataclass", a=1) + + +def test_dataclassRepr(): + obj1 = TestDataClass(a=1) + assert dataclassRepr(obj1) == "TestDataClass(a=1)" + + obj2 = TestDataClass(a=1, b="custom") + assert dataclassRepr(obj2) == "TestDataClass(a=1, b='custom')" + + obj3 = TestDataClass(a=1, c=2.5) + assert dataclassRepr(obj3) == "TestDataClass(a=1, c=2.5)" + + obj4 = TestDataClass(a=1, e=[1, 2, 3]) + assert dataclassRepr(obj4) == "TestDataClass(a=1, e=[1, 2, 3])" + + +def test_isnamedtupleinstance(): + from collections import namedtuple + + Point = namedtuple("Point", "x y") + p = Point(1, 2) + assert isnamedtupleinstance(p) + assert not isnamedtupleinstance((1, 2)) + assert not isnamedtupleinstance([1, 2]) + assert not isnamedtupleinstance(TestDataClass(a=1)) + + +def test_tree(): + # Basic types + assert tree(1) == 1 + assert tree("hello") == "hello" + assert tree(True) is True + assert tree(1.5) == 1.5 + assert tree(b"bytes") == b"bytes" + + # Datetime/date + d = dt.date(2023, 1, 1) + assert tree(d) == "2023-01-01" + dt_obj = dt.datetime(2023, 1, 1, 10, 30, 0) + assert tree(dt_obj) == "2023-01-01T10:30:00" + + # Dict + assert tree({"a": 1, "b": "c"}) == {"a": 1, "b": "c"} + + # Namedtuple + from collections import namedtuple + + Point = namedtuple("Point", "x y") + p = Point(1, "two") + assert tree(p) == {"x": 1, "y": "two"} + + # List, tuple, set + assert tree([1, 2, "a"]) == [1, 2, "a"] + assert tree((1, 2, "a")) == [1, 2, "a"] + assert sorted(tree({1, 2, 3})) == [1, 2, 3] + + # Dataclass + obj = TestDataClass(a=1, b="custom") + expected_dataclass_tree = {"TestDataClass": {"a": 1, "b": "custom"}} + assert tree(obj) == expected_dataclass_tree + + # Nested structure + nested_obj = { + "num": 1, + "text": "test", + "date": dt.date(2024, 5, 10), + "data": TestDataClass(a=5, c=3.3), + "items": [Point(10, 20), {"key": "value"}], + } + expected_nested_tree = { + "num": 1, + "text": "test", + "date": "2024-05-10", + "data": {"TestDataClass": {"a": 5, "c": 3.3}}, + "items": [{"x": 10, "y": 20}, {"key": "value"}], + } + assert tree(nested_obj) == expected_nested_tree + + # Other objects + class CustomClass: + def __str__(self): + return "Custom" + + assert tree(CustomClass()) == "Custom" + + +def test_isNan(): + assert isNan(float("nan")) + assert not isNan(1.0) + assert not isNan(0.0) + assert not isNan(float("inf")) + assert not isNan(-1.0) + + +def test_formatSI(): + assert formatSI(0) == "0 " + assert formatSI(1) == "1 " + assert formatSI(10) == "10 " + assert formatSI(100) == "100 " + assert formatSI(1000) == "1.00 k" + assert formatSI(1234) == "1.23 k" + assert formatSI(1234567) == "1.23 M" + assert formatSI(0.1) == "100 m" + assert formatSI(0.01) == "10.0 m" + assert formatSI(0.001) == "1.00 m" + assert formatSI(0.000000001) == "1.00 n" + assert formatSI(-1234) == "-1.23 k" + assert formatSI(1000000000000000000000000.0) == "1.00 Y" # Yotta + assert ( + formatSI(0.000000000000000000000001) == "0.00 " + ) # yocto - incorrect in implementation + assert formatSI(0.0000000000000000000000001) == "0.00 " # smaller than yocto + + +@patch("builtins.print") +@patch("time.time", side_effect=[0, 1.5]) +def test_timeit(mock_time, mock_print): + with timeit("Test Run"): + pass + mock_print.assert_called_once_with("Test Run took 1.50 s") + + +@patch("builtins.print") +@patch("time.time", side_effect=[0, 12345.678]) +def test_timeit_format_si(mock_time, mock_print): + with timeit("Long Run"): + pass + mock_print.assert_called_once_with("Long Run took 12.3 ks") + + +def test_formatIBDatetime(): + assert formatIBDatetime(None) == "" + + # Datetime + dt_obj = dt.datetime(2023, 1, 15, 10, 30, 45, tzinfo=dt.timezone.utc) + assert formatIBDatetime(dt_obj) == "20230115 10:30:45 UTC" + + dt_obj_ny = dt.datetime( + 2023, 1, 15, 5, 30, 45, tzinfo=dt.timezone(dt.timedelta(hours=-5)) + ) # EST + assert formatIBDatetime(dt_obj_ny) == "20230115 10:30:45 UTC" + + # Date + date_obj = dt.date(2023, 2, 1) + assert formatIBDatetime(date_obj) == "20230201 22:59:59 UTC" + + # String (pass-through) + assert formatIBDatetime("20230301 12:00:00") == "20230301 12:00:00" + + +@patch("ib_async.util.ZoneInfo", create=True) +def test_parseIBDatetime(MockZoneInfo): + from zoneinfo import ZoneInfo as RealZoneInfo + + MockZoneInfo.side_effect = lambda x: RealZoneInfo(x) + + # YYYYmmdd + assert parseIBDatetime("20230101") == dt.date(2023, 1, 1) + + # Timestamp as string + timestamp_str = str( + int(dt.datetime(2023, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc).timestamp()) + ) + assert parseIBDatetime(timestamp_str) == dt.datetime( + 2023, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc + ) + + # YYYYmmdd HH:MM:SS Europe/Amsterdam + result = parseIBDatetime("20221125 10:00:00 Europe/Amsterdam") + expected = dt.datetime( + 2022, 11, 25, 10, 0, 0, tzinfo=RealZoneInfo("Europe/Amsterdam") + ) + assert result == expected + MockZoneInfo.assert_called_with("Europe/Amsterdam") + + # YYYYmmdd HH:MM:SS + assert parseIBDatetime("20230101 12:30:45") == dt.datetime(2023, 1, 1, 12, 30, 45) + + # YYYY-mm-dd HH:MM:SS.0 + assert parseIBDatetime("2023-01-01 12:30:45.0") == dt.datetime( + 2023, 1, 1, 12, 30, 45 + ) + assert parseIBDatetime("2023-01-01 12:30:45") == dt.datetime(2023, 1, 1, 12, 30, 45) + + +def test_parseIBTimeStamp(): + timestamp = 1672531200 # 2023-01-01 00:00:00 UTC + expected_dt = dt.datetime(2023, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc) + assert parseIBTimeStamp(timestamp) == expected_dt + + # With different timezone + ny_tz = dt.timezone(dt.timedelta(hours=-5)) + # timestamp is UTC, so parsing it with a -5h offset will result in a time 5h earlier + expected_dt_ny = dt.datetime(2022, 12, 31, 19, 0, 0, tzinfo=ny_tz) + assert parseIBTimeStamp(timestamp, tz=ny_tz) == expected_dt_ny + + +def test_decimalMaxString(): + assert decimalMaxString(Decimal("123.45")) == "123.45" + assert decimalMaxString(UNSET_DECIMAL) == "" + assert decimalMaxString(Decimal("0")) == "0" + + +def test_floatMaxString(): + assert floatMaxString(123.456789) == "123.456789" + assert floatMaxString(123.000000) == "123" + assert floatMaxString(UNSET_DOUBLE) == "" + assert floatMaxString(0.0) == "0" + assert floatMaxString(None) == "" + + +def test_getEnumTypeFromString(): + assert getEnumTypeFromString(TestEnum, "A") == TestEnum.ALPHA + assert getEnumTypeFromString(TestEnum, "B") == TestEnum.BETA + assert getEnumTypeFromString(TestEnum, "C") == TestEnum.GAMMA + # If not found, should return the first enum member + assert getEnumTypeFromString(TestEnum, "X") == TestEnum.ALPHA + + +def test_listOfValues(): + expected_list = [TestEnum.ALPHA, TestEnum.BETA, TestEnum.GAMMA] + assert listOfValues(TestEnum) == expected_list + + +def test_isValidIntValue(): + assert isValidIntValue(10) + assert isValidIntValue(0) + assert not isValidIntValue(UNSET_INTEGER) + assert isValidIntValue(-1) + + +def test_quantize_decimals_decorator(): + @quantize_decimals(places=2) + @dataclass + class MyResult: + price: Decimal + amount: Decimal + name: str + not_decimal: float = 1.234 + + res = MyResult(price=Decimal("12.3456"), amount=Decimal("7.891"), name="Test") + assert res.price == Decimal("12.35") + assert res.amount == Decimal("7.89") + assert res.name == "Test" + assert res.not_decimal == 1.234 # float should not be quantized + + # Test with different places and rounding + @quantize_decimals(places=0, rounding=ROUND_HALF_UP) + @dataclass + class MyIntResult: + count: Decimal + + res_int = MyIntResult(count=Decimal("5.6")) + assert res_int.count == Decimal("6") + + # Test with a function that returns a non-dataclass + @quantize_decimals(places=2) + def return_non_dataclass(): + return "just a string" + + assert return_non_dataclass() == "just a string" + + # Test with a function that returns None + @quantize_decimals(places=2) + def return_none(): + return None + + assert return_none() is None + + +@pytest.mark.asyncio +@patch("asyncio.get_running_loop") +async def test_getLoop_running(mock_get_running_loop): + mock_loop = MagicMock() + mock_get_running_loop.return_value = mock_loop + assert getLoop() == mock_loop + mock_get_running_loop.assert_called_once() + + +@pytest.mark.asyncio +@patch("nest_asyncio.apply") +async def test_patchAsyncio(mock_apply): + patchAsyncio() + mock_apply.assert_called_once() + + +def test_fillDate(): + today = dt.date.today() + + # dt.time input + time_obj = dt.time(10, 30, 0) + expected_dt = dt.datetime(today.year, today.month, today.day, 10, 30, 0) + assert _fillDate(time_obj) == expected_dt + + # dt.datetime input + dt_obj = dt.datetime(2023, 1, 1, 11, 0, 0, tzinfo=dt.timezone.utc) + assert _fillDate(dt_obj) == dt_obj + + +@patch("ib_async.util.getLoop") +def test_schedule(mock_getLoop): + mock_loop = MagicMock() + mock_getLoop.return_value = mock_loop + callback = MagicMock() + + now = dt.datetime.now() + target_time = now + dt.timedelta(seconds=10) + + schedule(target_time, callback, 1, 2, "test") + + mock_loop.call_later.assert_called_once() + args, _ = mock_loop.call_later.call_args + delay = args[0] + assert delay == pytest.approx(10, abs=0.1) + assert args[1] == callback + assert args[2:] == (1, 2, "test") + + # Test with dt.time + mock_loop.reset_mock() + time_obj = dt.time(now.hour, (now.minute + 1) % 60, now.second) + schedule(time_obj, callback) + mock_loop.call_later.assert_called_once() + + +@patch("ib_async.util.run") +def test_waitUntil(mock_run): + with patch("datetime.datetime", wraps=dt.datetime) as mock_dt: + mock_now = dt.datetime(2023, 1, 1, 10, 0, 0) + mock_dt.now.return_value = mock_now + + target_time = dt.datetime(2023, 1, 1, 10, 0, 5) + waitUntil(target_time) + + # The argument to run should be an awaitable (coroutine from sleep) + mock_run.assert_called_once() + (sleep_coro,) = mock_run.call_args[0] + # Inspecting coroutines is tricky, but we can check it's from sleep + assert sleep_coro.__name__ == "sleep" + + with patch("datetime.datetime", wraps=dt.datetime) as mock_dt: + mock_run.reset_mock() + mock_now = dt.datetime(2023, 1, 1, 10, 0, 0) + mock_dt.now.return_value = mock_now + mock_dt.today.return_value = mock_now.date() + target_time_obj = dt.time(10, 0, 10) + waitUntil(target_time_obj) + mock_run.assert_called_once() + + +@pytest.mark.asyncio +@patch("asyncio.sleep", new_callable=AsyncMock) +async def test_waitUntilAsync(mock_sleep): + with patch("datetime.datetime", wraps=dt.datetime) as mock_dt: + mock_now = dt.datetime(2023, 1, 1, 10, 0, 0) + mock_dt.now.return_value = mock_now + + # Mock _fillDate for the first part of the test + with patch("ib_async.util._fillDate") as mock_util_fillDate: + mock_util_fillDate.side_effect = lambda t: ( + dt.datetime(2023, 1, 1, t.hour, t.minute, t.second) + if isinstance(t, dt.time) + else t + ) + target_time = dt.datetime(2023, 1, 1, 10, 0, 5) + result = await waitUntilAsync(target_time) + assert result is True + mock_sleep.assert_awaited_once_with(5) + + # Reset mock for the second part of the test + mock_sleep.reset_mock() + mock_dt.now.return_value = dt.datetime( + 2023, 1, 1, 10, 0, 0 + ) # Reset mock_dt.now + with patch("ib_async.util._fillDate") as mock_util_fillDate: + mock_util_fillDate.side_effect = lambda t: ( + dt.datetime(2023, 1, 1, t.hour, t.minute, t.second) + if isinstance(t, dt.time) + else t + ) + target_time_obj = dt.time(10, 0, 10) + result = await waitUntilAsync(target_time_obj) + assert result is True + mock_sleep.assert_awaited_once_with(10) + + +@patch("ib_async.util.waitUntil") +def test_timeRange(mock_waitUntil): + start_dt = dt.datetime(2023, 1, 1, 9, 58, 0) + with ( + patch("datetime.datetime", wraps=dt.datetime) as mock_dt, + patch("ib_async.util._fillDate") as mock_util_fillDate, + ): + mock_dt.now.return_value = start_dt + mock_util_fillDate.side_effect = lambda t: ( + dt.datetime(2023, 1, 1, t.hour, t.minute, t.second) + if isinstance(t, dt.time) + else t + ) + + start_time = dt.time(10, 0, 0) + end_time = dt.time(10, 0, 5) + step = 1 + + expected_times = [ + dt.datetime(2023, 1, 1, 10, 0, 0), + dt.datetime(2023, 1, 1, 10, 0, 1), + dt.datetime(2023, 1, 1, 10, 0, 2), + dt.datetime(2023, 1, 1, 10, 0, 3), + dt.datetime(2023, 1, 1, 10, 0, 4), + dt.datetime(2023, 1, 1, 10, 0, 5), + ] + + results = list(timeRange(start_time, end_time, step)) + assert results == expected_times + assert mock_waitUntil.call_count == len(expected_times) + + with patch("datetime.datetime", wraps=dt.datetime) as mock_dt: + mock_dt.now.return_value = dt.datetime(2023, 1, 1, 10, 0, 2) + with patch("ib_async.util._fillDate") as mock_util_fillDate: + mock_util_fillDate.side_effect = lambda t: ( + dt.datetime(2023, 1, 1, t.hour, t.minute, t.second) + if isinstance(t, dt.time) + else t + ) + results2 = list(timeRange(start_time, end_time, step)) + assert results2 == expected_times[2:] + + +@pytest.mark.asyncio +@patch("ib_async.util.waitUntilAsync", new_callable=AsyncMock) +async def test_timeRangeAsync(mock_waitUntilAsync): + start_dt = dt.datetime(2023, 1, 1, 9, 58, 0) + with ( + patch("datetime.datetime", wraps=dt.datetime) as mock_dt, + patch("ib_async.util._fillDate") as mock_util_fillDate, + ): + mock_dt.now.return_value = start_dt + mock_util_fillDate.side_effect = lambda t: ( + dt.datetime(2023, 1, 1, t.hour, t.minute, t.second) + if isinstance(t, dt.time) + else t + ) + + start_time = dt.time(10, 0, 0) + end_time = dt.time(10, 0, 5) + step = 1 + + expected_times = [ + dt.datetime(2023, 1, 1, 10, 0, 0), + dt.datetime(2023, 1, 1, 10, 0, 1), + dt.datetime(2023, 1, 1, 10, 0, 2), + dt.datetime(2023, 1, 1, 10, 0, 3), + dt.datetime(2023, 1, 1, 10, 0, 4), + dt.datetime(2023, 1, 1, 10, 0, 5), + ] + + results = [t async for t in timeRangeAsync(start_time, end_time, step)] + assert results == expected_times + assert mock_waitUntilAsync.call_count == len(expected_times) + + with patch("datetime.datetime", wraps=dt.datetime) as mock_dt: + mock_waitUntilAsync.reset_mock() + mock_dt.now.return_value = dt.datetime(2023, 1, 1, 10, 0, 2) + with patch("ib_async.util._fillDate") as mock_util_fillDate: + mock_util_fillDate.side_effect = lambda t: ( + dt.datetime(2023, 1, 1, t.hour, t.minute, t.second) + if isinstance(t, dt.time) + else t + ) + results2 = [t async for t in timeRangeAsync(start_time, end_time, step)] + assert results2 == expected_times[2:] + + +@patch("pandas.DataFrame") +@patch("pandas.read_sql") +def test_df(mock_read_sql, mock_dataframe): + mock_instance = MagicMock() + mock_dataframe.from_records.return_value = mock_instance + mock_instance.__getitem__.return_value = MagicMock(values=[[1, 2, 3], [4, 5, 6]]) + mock_instance.drop.return_value = mock_instance + + # Test with dataclasses + obj1 = TestDataClass(a=1, b="one") + obj2 = TestDataClass(a=2, b="two") + + result_df = df([obj1, obj2]) + mock_dataframe.from_records.assert_called_once() + assert list(mock_dataframe.from_records.call_args[0][0]) == [ + (1, "one", 1.0, Decimal("1.23"), [], {}), + (2, "two", 1.0, Decimal("1.23"), [], {}), + ] + assert result_df == mock_instance + assert result_df.columns == ["a", "b", "c", "d", "e", "f"] + + mock_dataframe.reset_mock() + + # Test with list of dicts + dict_obj1 = {"col1": 1, "col2": "a"} + dict_obj2 = {"col1": 2, "col2": "b"} + result_df_dict = df([dict_obj1, dict_obj2]) + mock_dataframe.from_records.assert_called_once_with( + [{"col1": 1, "col2": "a"}, {"col1": 2, "col2": "b"}] + ) + assert result_df_dict == mock_instance diff --git a/tests/tests_util.py b/tests/tests_util.py deleted file mode 100644 index 20f8a5cb..00000000 --- a/tests/tests_util.py +++ /dev/null @@ -1,739 +0,0 @@ - -import asyncio -import datetime as dt -import enum -import logging -import math -import sys -import unittest -from dataclasses import dataclass, field -from decimal import ROUND_HALF_UP, Decimal -from unittest.mock import AsyncMock, MagicMock, call, patch - -from ib_async.util import ( - EPOCH, - NO_VALID_ID, - UNSET_DECIMAL, - UNSET_DOUBLE, - UNSET_INTEGER, - _fillDate, - dataclassAsDict, - dataclassAsTuple, - dataclassNonDefaults, - dataclassRepr, - dataclassUpdate, - decimalMaxString, - df, - floatMaxString, - formatIBDatetime, - formatSI, - getEnumTypeFromString, - getLoop, - globalErrorEvent, - isnamedtupleinstance, - isNan, - isValidIntValue, - listOfValues, - parseIBDatetime, - parseIBTimeStamp, - patchAsyncio, - quantize_decimals, - run, - schedule, - sleep, - timeit, - timeRange, - timeRangeAsync, - tree, - waitUntil, - waitUntilAsync, -) - - -# Dummy dataclasses for testing -@dataclass -class TestDataClass: - a: int - b: str = "default" - c: float = 1.0 - d: Decimal = Decimal("1.23") - e: list = field(default_factory=list) - f: dict = field(default_factory=dict) - -@dataclass -class AnotherTestDataClass: - x: int = 10 - y: str = "hello" - -@dataclass -class DataClassWithDecimal: - value: Decimal - -# Dummy Enum for testing -class TestEnum(enum.Enum): - ALPHA = "A", "AlphaValue" - BETA = "B", "BetaValue" - GAMMA = "C", "GammaValue" - - def __init__(self, char_val, description): - self.char_val = char_val - self.description = description - - @property - def value(self): - return self.char_val, self.description - - @classmethod - def from_char(cls, char_val): - for member in cls: - if member.char_val == char_val: - return member - raise ValueError(f"No member with char_val '{char_val}'") - - -class TestUtil(unittest.TestCase): - - def setUp(self): - # Reset globalErrorEvent before each test - globalErrorEvent.clear() - - def test_dataclassAsDict(self): - obj = TestDataClass(a=1, b="test") - expected = {"a": 1, "b": "test", "c": 1.0, "d": Decimal("1.23"), "e": [], "f": {}} - self.assertEqual(dataclassAsDict(obj), expected) - - with self.assertRaises(TypeError): - dataclassAsDict(123) - - def test_dataclassAsTuple(self): - obj = TestDataClass(a=1, b="test") - expected = (1, "test", 1.0, Decimal("1.23"), [], {}) - self.assertEqual(dataclassAsTuple(obj), expected) - - with self.assertRaises(TypeError): - dataclassAsTuple("not_a_dataclass") - - def test_dataclassNonDefaults(self): - obj1 = TestDataClass(a=1) - self.assertEqual(dataclassNonDefaults(obj1), {"a": 1}) - - obj2 = TestDataClass(a=1, b="non-default", c=2.0, d=Decimal("3.45")) - expected2 = {"a": 1, "b": "non-default", "c": 2.0, "d": Decimal("3.45")} - self.assertEqual(dataclassNonDefaults(obj2), expected2) - - obj3 = TestDataClass(a=1, e=[1,2]) - self.assertEqual(dataclassNonDefaults(obj3), {"a":1, "e":[1,2]}) - - with self.assertRaises(TypeError): - dataclassNonDefaults({"a": 1}) - - def test_dataclassUpdate(self): - obj = TestDataClass(a=1, b="original") - src_obj = AnotherTestDataClass(x=20, y="updated") - - # Test update from kwargs - dataclassUpdate(obj, a=5, b="new") - self.assertEqual(obj.a, 5) - self.assertEqual(obj.b, "new") - - # Test update from another dataclass (only matching fields) - dataclassUpdate(obj, src_obj, a=100) # src_obj doesn't have 'a' or 'b' - self.assertEqual(obj.a, 100) - self.assertEqual(obj.b, "new") # Should remain unchanged as src_obj doesn't have 'b' - self.assertEqual(obj.c, 1.0) # Should remain unchanged as src_obj doesn't have 'c' - - # Test update from dataclass where fields match - @dataclass - class MatchingSrc: - a: int = 0 - c: float = 0.0 - match_src = MatchingSrc(a=99, c=9.9) - dataclassUpdate(obj, match_src) - self.assertEqual(obj.a, 99) - self.assertEqual(obj.c, 9.9) - self.assertEqual(obj.b, "new") - - with self.assertRaises(TypeError): - dataclassUpdate("not_a_dataclass", a=1) - - def test_dataclassRepr(self): - obj1 = TestDataClass(a=1) - self.assertEqual(dataclassRepr(obj1), "TestDataClass(a=1)") - - obj2 = TestDataClass(a=1, b="custom") - self.assertEqual(dataclassRepr(obj2), "TestDataClass(a=1, b='custom')") - - obj3 = TestDataClass(a=1, c=2.5) - self.assertEqual(dataclassRepr(obj3), "TestDataClass(a=1, c=2.5)") - - obj4 = TestDataClass(a=1, e=[1,2,3]) - self.assertEqual(dataclassRepr(obj4), "TestDataClass(a=1, e=[1, 2, 3])") - - def test_isnamedtupleinstance(self): - from collections import namedtuple - Point = namedtuple("Point", "x y") - p = Point(1, 2) - self.assertTrue(isnamedtupleinstance(p)) - self.assertFalse(isnamedtupleinstance((1, 2))) - self.assertFalse(isnamedtupleinstance([1, 2])) - self.assertFalse(isnamedtupleinstance(TestDataClass(a=1))) - - def test_tree(self): - # Basic types - self.assertEqual(tree(1), 1) - self.assertEqual(tree("hello"), "hello") - self.assertEqual(tree(True), True) - self.assertEqual(tree(1.5), 1.5) - self.assertEqual(tree(b"bytes"), b"bytes") - - # Datetime/date - d = dt.date(2023, 1, 1) - self.assertEqual(tree(d), "2023-01-01") - dt_obj = dt.datetime(2023, 1, 1, 10, 30, 0) - self.assertEqual(tree(dt_obj), "2023-01-01T10:30:00") - - # Dict - self.assertEqual(tree({"a": 1, "b": "c"}), {"a": 1, "b": "c"}) - - # Namedtuple - from collections import namedtuple - Point = namedtuple("Point", "x y") - p = Point(1, "two") - self.assertEqual(tree(p), {"x": 1, "y": "two"}) - - # List, tuple, set - self.assertEqual(tree([1, 2, "a"]), [1, 2, "a"]) - self.assertEqual(tree((1, 2, "a")), [1, 2, "a"]) - self.assertEqual(tree({1, 2, 3}), [1, 2, 3]) # Note: sets become lists, order not guaranteed - - # Dataclass - obj = TestDataClass(a=1, b="custom") - expected_dataclass_tree = {"TestDataClass": {"a": 1, "b": "custom"}} - self.assertEqual(tree(obj), expected_dataclass_tree) - - # Nested structure - nested_obj = { - "num": 1, - "text": "test", - "date": dt.date(2024, 5, 10), - "data": TestDataClass(a=5, c=3.3), - "items": [Point(10, 20), {"key": "value"}], - } - expected_nested_tree = { - "num": 1, - "text": "test", - "date": "2024-05-10", - "data": {"TestDataClass": {"a": 5, "c": 3.3}}, - "items": [{"x": 10, "y": 20}, {"key": "value"}], - } - self.assertEqual(tree(nested_obj), expected_nested_tree) - - # Other objects - class CustomClass: - def __str__(self): - return "Custom" - self.assertEqual(tree(CustomClass()), "Custom") - - def test_isNan(self): - self.assertTrue(isNan(float("nan"))) - self.assertFalse(isNan(1.0)) - self.assertFalse(isNan(0.0)) - self.assertFalse(isNan(float("inf"))) - self.assertFalse(isNan(-1.0)) - - def test_formatSI(self): - self.assertEqual(formatSI(0), "0.00 ") - self.assertEqual(formatSI(1), "1.00 ") - self.assertEqual(formatSI(10), "10.0 ") - self.assertEqual(formatSI(100), "100 ") - self.assertEqual(formatSI(1000), "1.00 k") - self.assertEqual(formatSI(1234), "1.23 k") - self.assertEqual(formatSI(1234567), "1.23 M") - self.assertEqual(formatSI(0.1), "100 m") - self.assertEqual(formatSI(0.01), "10.0 m") - self.assertEqual(formatSI(0.001), "1.00 m") - self.assertEqual(formatSI(0.000000001), "1.00 n") - self.assertEqual(formatSI(-1234), "-1.23 k") - self.assertEqual(formatSI(1000000000000000000000000.0), "1.00 Y") # Yotta - self.assertEqual(formatSI(0.000000000000000000000001), "1.00 y") # yocto - self.assertEqual(formatSI(0.0000000000000000000000001), "0.00 ") # smaller than yocto - - @patch('builtins.print') - @patch('time.time', side_effect=[0, 1.5]) - def test_timeit(self, mock_time, mock_print): - with timeit("Test Run"): - pass - mock_print.assert_called_once_with("Test Run took 1.50 s") - - @patch('builtins.print') - @patch('time.time', side_effect=[0, 12345.678]) - def test_timeit_format_si(self, mock_time, mock_print): - with timeit("Long Run"): - pass - mock_print.assert_called_once_with("Long Run took 12.3 k s") - - def test_formatIBDatetime(self): - self.assertEqual(formatIBDatetime(None), "") - - # Datetime - dt_obj = dt.datetime(2023, 1, 15, 10, 30, 45, tzinfo=dt.timezone.utc) - self.assertEqual(formatIBDatetime(dt_obj), "20230115 10:30:45 UTC") - - dt_obj_ny = dt.datetime(2023, 1, 15, 5, 30, 45, tzinfo=dt.timezone(dt.timedelta(hours=-5))) # EST - # Should convert to UTC: 2023-01-15 10:30:45 UTC - self.assertEqual(formatIBDatetime(dt_obj_ny), "20230115 10:30:45 UTC") - - # Date - date_obj = dt.date(2023, 2, 1) - # Should convert to end of day UTC: 2023-02-01 23:59:59 UTC - self.assertEqual(formatIBDatetime(date_obj), "20230201 23:59:59 UTC") - - # String (pass-through) - self.assertEqual(formatIBDatetime("20230301 12:00:00"), "20230301 12:00:00") - - @patch('ib_async.util.ZoneInfo', side_effect=lambda x: dt.timezone.utc if x == 'Europe/Amsterdam' else dt.timezone.utc) # Mock ZoneInfo - def test_parseIBDatetime(self, MockZoneInfo): - from zoneinfo import ZoneInfo as RealZoneInfo # For direct comparison - - # YYYYmmdd - self.assertEqual(parseIBDatetime("20230101"), dt.date(2023, 1, 1)) - - # Timestamp as string - timestamp_str = str(int(dt.datetime(2023, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc).timestamp())) - self.assertEqual(parseIBDatetime(timestamp_str), dt.datetime(2023, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc)) - - # YYYYmmdd HH:MM:SS Europe/Amsterdam - with patch('ib_async.util.ZoneInfo', side_effect=lambda x: RealZoneInfo('Europe/Amsterdam') if x == 'Europe/Amsterdam' else dt.timezone.utc): - result = parseIBDatetime("20221125 10:00:00 Europe/Amsterdam") - expected = dt.datetime(2022, 11, 25, 10, 0, 0, tzinfo=RealZoneInfo('Europe/Amsterdam')) - self.assertEqual(result, expected) - - # YYYYmmdd HH:MM:SS - self.assertEqual(parseIBDatetime("20230101 12:30:45"), dt.datetime(2023, 1, 1, 12, 30, 45)) - - # YYYY-mm-dd HH:MM:SS.0 - self.assertEqual(parseIBDatetime("2023-01-01 12:30:45.0"), dt.datetime(2023, 1, 1, 12, 30, 45)) - self.assertEqual(parseIBDatetime("2023-01-01 12:30:45"), dt.datetime(2023, 1, 1, 12, 30, 45)) - - def test_parseIBTimeStamp(self): - timestamp = 1672531200 # 2023-01-01 00:00:00 UTC - expected_dt = dt.datetime(2023, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc) - self.assertEqual(parseIBTimeStamp(timestamp), expected_dt) - - # With different timezone - ny_tz = dt.timezone(dt.timedelta(hours=-5)) - expected_dt_ny = dt.datetime(2023, 1, 1, 0, 0, 0, tzinfo=ny_tz) - self.assertEqual(parseIBTimeStamp(timestamp, tz=ny_tz), expected_dt_ny) - - def test_decimalMaxString(self): - self.assertEqual(decimalMaxString(Decimal("123.45")), "123.45") - self.assertEqual(decimalMaxString(UNSET_DECIMAL), "") - self.assertEqual(decimalMaxString(Decimal("0")), "0") - - def test_floatMaxString(self): - self.assertEqual(floatMaxString(123.456789), "123.456789") - self.assertEqual(floatMaxString(123.000000), "123") - self.assertEqual(floatMaxString(UNSET_DOUBLE), "") - self.assertEqual(floatMaxString(0.0), "0") - self.assertEqual(floatMaxString(None), "") - - def test_getEnumTypeFromString(self): - self.assertEqual(getEnumTypeFromString(TestEnum, "A"), TestEnum.ALPHA) - self.assertEqual(getEnumTypeFromString(TestEnum, "B"), TestEnum.BETA) - self.assertEqual(getEnumTypeFromString(TestEnum, "C"), TestEnum.GAMMA) - # If not found, should return the first enum member - self.assertEqual(getEnumTypeFromString(TestEnum, "X"), TestEnum.ALPHA) - - def test_listOfValues(self): - expected_list = [TestEnum.ALPHA, TestEnum.BETA, TestEnum.GAMMA] - self.assertEqual(listOfValues(TestEnum), expected_list) - - def test_isValidIntValue(self): - self.assertTrue(isValidIntValue(10)) - self.assertTrue(isValidIntValue(0)) - self.assertFalse(isValidIntValue(UNSET_INTEGER)) - self.assertTrue(isValidIntValue(-1)) - - def test_quantize_decimals_decorator(self): - @quantize_decimals(places=2) - @dataclass - class MyResult: - price: Decimal - amount: Decimal - name: str - not_decimal: float = 1.234 - - res = MyResult(price=Decimal("12.3456"), amount=Decimal("7.891"), name="Test") - self.assertEqual(res.price, Decimal("12.35")) - self.assertEqual(res.amount, Decimal("7.89")) - self.assertEqual(res.name, "Test") - self.assertEqual(res.not_decimal, 1.234) # float should not be quantized - - # Test with different places and rounding - @quantize_decimals(places=0, rounding=ROUND_HALF_UP) - @dataclass - class MyIntResult: - count: Decimal - - res_int = MyIntResult(count=Decimal("5.6")) - self.assertEqual(res_int.count, Decimal("6")) - - # Test with a function that returns a non-dataclass - @quantize_decimals(places=2) - def return_non_dataclass(): - return "just a string" - - self.assertEqual(return_non_dataclass(), "just a string") - - # Test with a function that returns None - @quantize_decimals(places=2) - def return_none(): - return None - self.assertIsNone(return_none()) - - -class TestUtilAsync(unittest.IsolatedAsyncioTestCase): - - @patch('asyncio.get_running_loop') - def test_getLoop_running(self, mock_get_running_loop): - mock_loop = MagicMock() - mock_get_running_loop.return_value = mock_loop - self.assertEqual(getLoop(), mock_loop) - mock_get_running_loop.assert_called_once() - - @patch('asyncio.new_event_loop') - @patch('asyncio.set_event_loop') - @patch('asyncio.get_running_loop', side_effect=RuntimeError) - def test_getLoop_new(self, mock_get_running_loop, mock_set_event_loop, mock_new_event_loop): - mock_loop = MagicMock() - mock_new_event_loop.return_value = mock_loop - self.assertEqual(getLoop(), mock_loop) - mock_get_running_loop.assert_called_once() - mock_new_event_loop.assert_called_once() - mock_set_event_loop.assert_called_once_with(mock_loop) - - @patch('nest_asyncio.apply') - def test_patchAsyncio(self, mock_apply): - patchAsyncio() - mock_apply.assert_called_once() - - @patch('ib_async.util.getLoop') - async def test_run_no_awaitables(self, mock_getLoop): - mock_loop = AsyncMock() - mock_loop.is_running.return_value = False - mock_getLoop.return_value = mock_loop - - with patch('asyncio.all_tasks', return_value=[]): - run() - mock_loop.run_forever.assert_called_once() - # If no tasks, no cancellation attempts - mock_loop.run_until_complete.assert_not_called() - - mock_loop.reset_mock() - mock_loop.is_running.return_value = True - run() # Should do nothing if loop is already running - mock_loop.run_forever.assert_not_called() - - @patch('ib_async.util.getLoop') - @patch('asyncio.gather', new_callable=AsyncMock) - @patch('asyncio.ensure_future') - @patch('asyncio.wait_for', new_callable=AsyncMock) - async def test_run_with_awaitables(self, mock_wait_for, mock_ensure_future, mock_gather, mock_getLoop): - mock_loop = MagicMock() - mock_getLoop.return_value = mock_loop - mock_task = MagicMock() - mock_ensure_future.return_value = mock_task - mock_wait_for.return_value = MagicMock() # The future that wait_for returns - - # Test single awaitable - awaitable = AsyncMock(return_value="result1") - mock_loop.run_until_complete.return_value = "result1" - result = run(awaitable) - self.assertEqual(result, "result1") - mock_gather.assert_called_once_with(awaitable) # gather is called even for single - mock_ensure_future.assert_called_once() - mock_loop.run_until_complete.assert_called_once_with(mock_task) - mock_wait_for.assert_called_once_with(mock_gather.return_value, None) - mock_getLoop.assert_called() - - mock_gather.reset_mock() - mock_ensure_future.reset_mock() - mock_loop.run_until_complete.reset_mock() - mock_wait_for.reset_mock() - - # Test multiple awaitables - awaitable1 = AsyncMock(return_value="result1") - awaitable2 = AsyncMock(return_value="result2") - mock_loop.run_until_complete.return_value = ["result1", "result2"] - result = run(awaitable1, awaitable2) - self.assertEqual(result, ["result1", "result2"]) - mock_gather.assert_called_once_with(awaitable1, awaitable2) - mock_ensure_future.assert_called_once() - mock_loop.run_until_complete.assert_called_once_with(mock_task) - mock_wait_for.assert_called_once_with(mock_gather.return_value, None) - - mock_gather.reset_mock() - mock_ensure_future.reset_mock() - mock_loop.run_until_complete.reset_mock() - mock_wait_for.reset_mock() - - # Test with timeout - awaitable = AsyncMock(return_value="result_timeout") - mock_loop.run_until_complete.return_value = "result_timeout" - result = run(awaitable, timeout=5.0) - self.assertEqual(result, "result_timeout") - mock_wait_for.assert_called_once_with(mock_gather.return_value, 5.0) - - @patch('ib_async.util.getLoop') - @patch('asyncio.gather', new_callable=AsyncMock) - @patch('asyncio.ensure_future') - @patch('asyncio.all_tasks', return_value=[MagicMock()]) # Simulate active tasks - async def test_run_cancel_pending_tasks(self, mock_all_tasks, mock_ensure_future, mock_gather, mock_getLoop): - mock_loop = MagicMock() - mock_loop.is_running.return_value = False - mock_getLoop.return_value = mock_loop - - # Mock gather for cancellation - mock_cancel_future = MagicMock() - mock_gather.return_value = mock_cancel_future - mock_loop.run_until_complete.side_effect = [asyncio.CancelledError(), None] # First for gather.cancel, second for normal run - - run() # Call without awaitables, should try to cancel all tasks - mock_loop.run_forever.assert_called_once() - mock_all_tasks.assert_called_once() - mock_gather.assert_called_once_with(*mock_all_tasks.return_value) - mock_cancel_future.cancel.assert_called_once() - mock_loop.run_until_complete.assert_has_calls([call(mock_cancel_future)]) - - @patch('ib_async.util.getLoop') - @patch('asyncio.sleep') - async def test_sleep(self, mock_sleep, mock_getLoop): - mock_loop = MagicMock() - mock_getLoop.return_value = mock_loop - mock_sleep.return_value = None # asyncio.sleep is an awaitable that returns None - - result = sleep(0.1) - self.assertTrue(result) - mock_sleep.assert_awaited_once_with(0.1) - - def test_fillDate(self): - today = dt.date.today() - - # dt.time input - time_obj = dt.time(10, 30, 0) - expected_dt = dt.datetime(today.year, today.month, today.day, 10, 30, 0) - self.assertEqual(_fillDate(time_obj), expected_dt) - - # dt.datetime input - dt_obj = dt.datetime(2023, 1, 1, 11, 0, 0, tzinfo=dt.timezone.utc) - self.assertEqual(_fillDate(dt_obj), dt_obj) - - @patch('ib_async.util.getLoop') - def test_schedule(self, mock_getLoop): - mock_loop = MagicMock() - mock_getLoop.return_value = mock_loop - callback = MagicMock() - - now = dt.datetime.now() - target_time = now + dt.timedelta(seconds=10) - - schedule(target_time, callback, 1, 2, "test") - - # Check if call_later is called with approximately 10 seconds delay - # We need to account for tiny time differences during test execution - mock_loop.call_later.assert_called_once() - args, _ = mock_loop.call_later.call_args - delay = args[0] - self.assertAlmostEqual(delay, 10, delta=0.1) - self.assertEqual(args[1], callback) - self.assertEqual(args[2:], (1, 2, "test")) - - # Test with dt.time - mock_loop.reset_mock() - time_obj = dt.time(now.hour, (now.minute + 1) % 60, now.second) - schedule(time_obj, callback) - mock_loop.call_later.assert_called_once() - - @patch('ib_async.util.run') - @patch('datetime.datetime') - def test_waitUntil(self, mock_datetime, mock_run): - # Mock datetime.datetime.now() - mock_now = dt.datetime(2023, 1, 1, 10, 0, 0) - mock_datetime.now.return_value = mock_now - mock_datetime.side_effect = lambda *args, **kw: dt.datetime(*args, **kw) # Allow normal datetime creation - mock_datetime.today.return_value = dt.date(2023, 1, 1) - - target_time = dt.datetime(2023, 1, 1, 10, 0, 5) - waitUntil(target_time) - mock_run.assert_called_once() - args, _ = mock_run.call_args - # Expecting asyncio.sleep(5) - self.assertEqual(args[0].__name__, "sleep") # Accessing the coroutine object - self.assertEqual(args[0].cr_code.co_name, "sleep") # Verify it's the sleep coroutine - - mock_run.reset_mock() - # Test with dt.time - target_time_obj = dt.time(10, 0, 10) - waitUntil(target_time_obj) - mock_run.assert_called_once() - args, _ = mock_run.call_args - # Expecting asyncio.sleep(10) - self.assertEqual(args[0].__name__, "sleep") - self.assertEqual(args[0].cr_code.co_name, "sleep") - - - @patch('asyncio.sleep', new_callable=AsyncMock) - @patch('datetime.datetime') - async def test_waitUntilAsync(self, mock_datetime, mock_sleep): - mock_now = dt.datetime(2023, 1, 1, 10, 0, 0) - mock_datetime.now.return_value = mock_now - mock_datetime.side_effect = lambda *args, **kw: dt.datetime(*args, **kw) - mock_datetime.today.return_value = dt.date(2023, 1, 1) - - target_time = dt.datetime(2023, 1, 1, 10, 0, 5) - result = await waitUntilAsync(target_time) - self.assertTrue(result) - mock_sleep.assert_awaited_once_with(5) - - mock_sleep.reset_mock() - target_time_obj = dt.time(10, 0, 10) - result = await waitUntilAsync(target_time_obj) - self.assertTrue(result) - mock_sleep.assert_awaited_once_with(10) - - @patch('ib_async.util.waitUntil') - @patch('datetime.datetime') - def test_timeRange(self, mock_datetime, mock_waitUntil): - mock_now = dt.datetime(2023, 1, 1, 9, 58, 0) - mock_datetime.now.return_value = mock_now - mock_datetime.side_effect = lambda *args, **kw: dt.datetime(*args, **kw) - mock_datetime.today.return_value = dt.date(2023, 1, 1) - - start_time = dt.time(10, 0, 0) - end_time = dt.time(10, 0, 5) - step = 1 - - expected_times = [ - dt.datetime(2023, 1, 1, 10, 0, 0), - dt.datetime(2023, 1, 1, 10, 0, 1), - dt.datetime(2023, 1, 1, 10, 0, 2), - dt.datetime(2023, 1, 1, 10, 0, 3), - dt.datetime(2023, 1, 1, 10, 0, 4), - dt.datetime(2023, 1, 1, 10, 0, 5), - ] - - # Collect results from the generator - results = list(timeRange(start_time, end_time, step)) - - self.assertEqual(results, expected_times) - self.assertEqual(mock_waitUntil.call_count, len(expected_times)) - - # Test with start time already passed - mock_datetime.now.return_value = dt.datetime(2023, 1, 1, 10, 0, 2) - results2 = list(timeRange(start_time, end_time, step)) - self.assertEqual(results2, expected_times[2:]) - - - @patch('ib_async.util.waitUntilAsync') - @patch('datetime.datetime') - async def test_timeRangeAsync(self, mock_datetime, mock_waitUntilAsync): - mock_now = dt.datetime(2023, 1, 1, 9, 58, 0) - mock_datetime.now.return_value = mock_now - mock_datetime.side_effect = lambda *args, **kw: dt.datetime(*args, **kw) - mock_datetime.today.return_value = dt.date(2023, 1, 1) - - start_time = dt.time(10, 0, 0) - end_time = dt.time(10, 0, 5) - step = 1 - - expected_times = [ - dt.datetime(2023, 1, 1, 10, 0, 0), - dt.datetime(2023, 1, 1, 10, 0, 1), - dt.datetime(2023, 1, 1, 10, 0, 2), - dt.datetime(2023, 1, 1, 10, 0, 3), - dt.datetime(2023, 1, 1, 10, 0, 4), - dt.datetime(2023, 1, 1, 10, 0, 5), - ] - - results = [] - async for t in timeRangeAsync(start_time, end_time, step): - results.append(t) - - self.assertEqual(results, expected_times) - self.assertEqual(mock_waitUntilAsync.call_count, len(expected_times)) - - # Test with start time already passed - mock_datetime.now.return_value = dt.datetime(2023, 1, 1, 10, 0, 2) - results2 = [] - async for t in timeRangeAsync(start_time, end_time, step): - results2.append(t) - self.assertEqual(results2, expected_times[2:]) - - @patch('pandas.DataFrame') - @patch('pandas.read_sql') # Mocking to avoid actual DB interaction - def test_df(self, mock_read_sql, mock_dataframe): - # Mock pandas DataFrame creation - mock_instance = MagicMock() - mock_dataframe.from_records.return_value = mock_instance - mock_instance.__getitem__.return_value = MagicMock(values=[[1,2,3], [4,5,6]]) - mock_instance.drop.return_value = mock_instance - - # Test with dataclasses - obj1 = TestDataClass(a=1, b="one") - obj2 = TestDataClass(a=2, b="two") - - result_df = df([obj1, obj2]) - mock_dataframe.from_records.assert_called_once() - self.assertEqual(mock_dataframe.from_records.call_args[0][0], [(1, 'one', 1.0, Decimal('1.23'), [], {}), (2, 'two', 1.0, Decimal('1.23'), [], {})]) - self.assertEqual(result_df, mock_instance) - self.assertEqual(result_df.columns, ['a', 'b', 'c', 'd', 'e', 'f']) - - mock_dataframe.reset_mock() - # Test with DynamicObject (mocked) - class DynamicObjectMock: - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - dyn_obj1 = DynamicObjectMock(id=1, name="Alpha") - dyn_obj2 = DynamicObjectMock(id=2, name="Beta") - - result_df_dyn = df([dyn_obj1, dyn_obj2]) - mock_dataframe.from_records.assert_called_once_with([{'id': 1, 'name': 'Alpha'}, {'id': 2, 'name': 'Beta'}]) - self.assertEqual(result_df_dyn, mock_instance) - - mock_dataframe.reset_mock() - # Test with namedtuple - from collections import namedtuple - NamedTupleMock = namedtuple("NamedTupleMock", "field1 field2") - nt_obj1 = NamedTupleMock(10, "ten") - nt_obj2 = NamedTupleMock(20, "twenty") - - result_df_nt = df([nt_obj1, nt_obj2]) - mock_dataframe.from_records.assert_called_once() - self.assertEqual(result_df_nt.columns, ('field1', 'field2')) - self.assertEqual(result_df_nt, mock_instance) - - mock_dataframe.reset_mock() - # Test with list of dicts - dict_obj1 = {"col1": 1, "col2": "a"} - dict_obj2 = {"col1": 2, "col2": "b"} - result_df_dict = df([dict_obj1, dict_obj2]) - mock_dataframe.from_records.assert_called_once_with([{'col1': 1, 'col2': 'a'}, {'col1': 2, 'col2': 'b'}]) - self.assertEqual(result_df_dict, mock_instance) - - mock_dataframe.reset_mock() - # Test with labels - obj = TestDataClass(a=1, b="one") - result_df_labels = df([obj], labels=["a"]) - mock_instance.drop.assert_called_once_with( - ['b', 'c', 'd', 'e', 'f'], axis=1 - ) - self.assertEqual(result_df_labels, mock_instance) - - mock_dataframe.reset_mock() - # Test empty list - self.assertIsNone(df([])) - -# To run tests -if __name__ == "__main__": - unittest.main() From c564c541f7e49e80d604218b6780d2d1af0e4240 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:19:02 +0100 Subject: [PATCH 11/48] Changes feat(news): Add news bulletin handling and related protobuf converters fix: fix comboLegs contract protobuf converters feat: implement __repr__ with dataclassRepr for ContractDetails fix: align ticker with main branch. EFP can't be implemented as there is no proto message. --- ib_async/client.py | 14 ++- ib_async/contract.py | 3 + ib_async/decoder.py | 10 ++ ib_async/ib.py | 2 +- ib_async/objects.py | 37 +++++- .../contract_converters.py | 4 +- .../protobuf_converters/news_converters.py | 43 +++++++ ib_async/ticker.py | 114 ++++++++++++------ ib_async/wrapper.py | 14 +-- 9 files changed, 194 insertions(+), 47 deletions(-) create mode 100644 ib_async/protobuf_converters/news_converters.py diff --git a/ib_async/client.py b/ib_async/client.py index 4b968d18..137df3f9 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -95,6 +95,10 @@ createMarketDataTypeRequestProto, createTickByTickRequestProto, ) +from .protobuf_converters.news_converters import ( + createCancelNewsBulletinsProto, + createNewsBulletinsRequestProto, +) from .protobuf_converters.subscription_converters import ( createCancelPnLProto, createCancelScannerSubscriptionProto, @@ -591,10 +595,16 @@ def cancelMktDepth(self, reqId, isSmartDepth): self.send(11, 1, reqId, isSmartDepth) def reqNewsBulletins(self, allMsgs): - self.send(12, 1, allMsgs) + self.sendProto( + MessageId.OUT.REQ_NEWS_BULLETINS, + createNewsBulletinsRequestProto(allMsgs), + ) def cancelNewsBulletins(self): - self.send(13, 1) + self.sendProto( + MessageId.OUT.CANCEL_NEWS_BULLETINS, + createCancelNewsBulletinsProto(), + ) def setServerLogLevel(self, logLevel): self.send(14, 1, logLevel) diff --git a/ib_async/contract.py b/ib_async/contract.py index f1c8f61e..23ae9bf5 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -668,6 +668,9 @@ class ContractDetails: eventContractDescription1: str = "" eventContractDescription2: str = "" + __repr__ = dataclassRepr + __str__ = dataclassRepr + def tradingSessions(self) -> list[TradingSession]: return self._parseSessions(self.tradingHours) diff --git a/ib_async/decoder.py b/ib_async/decoder.py index df909ff8..9825289a 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -2,6 +2,8 @@ import logging +from ib_async.protobuf_converters.news_converters import createNewsBulletin + from .contract import ( Contract, ContractDescription, @@ -72,6 +74,7 @@ from .protobuf.ManagedAccounts_pb2 import ManagedAccounts as ManagedAccountsProto from .protobuf.MarketDataType_pb2 import MarketDataType as MarketDataTypeProto from .protobuf.MarketRule_pb2 import MarketRule as MarketRuleProto +from .protobuf.NewsBulletin_pb2 import NewsBulletin as NewsBulletinProto from .protobuf.NextValidId_pb2 import NextValidId as NextValidIdProto from .protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto from .protobuf.OpenOrdersEnd_pb2 import OpenOrdersEnd as OpenOrderEndProto @@ -287,6 +290,7 @@ class Decoder: MessageId.IN.PNL_SINGLE: (PnLSingleProto, "pnlSingleProto"), MessageId.IN.USER_INFO: (UserInfoProto, "userInfoProto"), MessageId.IN.SMART_COMPONENTS: (SmartComponentsProto, "smartComponentsProto"), + MessageId.IN.NEWS_BULLETINS: (NewsBulletinProto, "newsBulletinProto") } def __init__(self, wrapper: Wrapper, serverVersion: int): @@ -584,6 +588,12 @@ def smartComponentsProto(self, msg: SmartComponentsProto): components = createSmartComponents(msg) self.wrapper.smartComponents(reqId, components) + def newsBulletinProto(self, msg: NewsBulletinProto): + msgId = msg.reqId if msg.HasField("msgId") else NO_VALID_ID + newsBulletin = createNewsBulletin(msg) + self.wrapper.updateNewsBulletin(msgId, newsBulletin) + + ##################### legacy methods ########################################## def bondContractDetails(self, fields): diff --git a/ib_async/ib.py b/ib_async/ib.py index 187e3463..1f7a9e9f 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -682,7 +682,7 @@ def newsTicks(self) -> list[NewsTick]: def newsBulletins(self) -> list[NewsBulletin]: """List of IB news bulletins.""" - return list(self.wrapper.msgId2NewsBulletin.values()) + return list(self.wrapper.newsBulletins.values()) def reqTickers( self, *contracts: Contract, regulatorySnapshot: bool = False diff --git a/ib_async/objects.py b/ib_async/objects.py index be561243..dbc87151 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -851,8 +851,41 @@ class IBDefaults: emptyPrice: Any = -1 emptySize: Any = 0 - # optionally replace ib_async default for all instance variable values before popualted from API updates + # optionally replace ib_async default for all instance variable values before + # popualted from API updates unset: Any = nan - # optionally change the timezone used for log history events in objects (no impact on orders or data processing) + # optionally change the timezone used for log history events in objects (no impact + # on orders or data processing) timezone: tzinfo = timezone.utc + + +@dataclass(slots=True, frozen=True) +class EfpData: + """ + Exchange for Physical (EFP) futures data. + + EFP allows trading a position in a single stock for a position + in the corresponding single stock future. + """ + + basisPoints: float + """Annualized basis points (financing rate comparable to broker rates)""" + + formattedBasisPoints: str + """Basis points formatted as percentage string""" + + impliedFuture: float + """ The implied Futures price""" + + holdDays: int + """Number of days until the future's last trade date""" + + futureLastTradeDate: str + """Expiration date of the single stock future""" + + dividendImpact: float + """Dividend impact on the annualized basis points interest rate""" + + dividendsToLastTradeDate: float + """Expected dividends until future expiration""" diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py index 754469b8..2d18f918 100644 --- a/ib_async/protobuf_converters/contract_converters.py +++ b/ib_async/protobuf_converters/contract_converters.py @@ -37,6 +37,7 @@ SmartComponentsRequest as SmartComponentsRequestProto, ) from ..util import ( + UNSET_DOUBLE, floatMaxString, getEnumTypeFromString, isValidIntValue, @@ -497,9 +498,10 @@ def createComboLegProtoList( comboLegProtoList = [] if comboLegs: for i, comboLeg in enumerate(comboLegs): + perLegPrice = UNSET_DOUBLE if orderComboLegs and i < len(orderComboLegs): perLegPrice = float(orderComboLegs[i].price) - comboLegProto = createComboLegProto(comboLeg, perLegPrice) + comboLegProto = createComboLegProto(comboLeg, perLegPrice) if comboLegProto is not None: comboLegProtoList.append(comboLegProto) return comboLegProtoList diff --git a/ib_async/protobuf_converters/news_converters.py b/ib_async/protobuf_converters/news_converters.py new file mode 100644 index 00000000..a59fea11 --- /dev/null +++ b/ib_async/protobuf_converters/news_converters.py @@ -0,0 +1,43 @@ +from ..objects import NewsBulletin +from ..protobuf.CancelNewsBulletins_pb2 import ( + CancelNewsBulletins as CancelNewsBulletinsProto, +) +from ..protobuf.NewsBulletin_pb2 import NewsBulletin as NewsBulletinProto +from ..protobuf.NewsBulletinsRequest_pb2 import ( + NewsBulletinsRequest as NewsBulletinsRequestProto, +) + + +def createNewsBulletinsRequestProto(allMessages: bool) -> NewsBulletinsRequestProto: + newsBulletinsRequestProto = NewsBulletinsRequestProto() + if allMessages: + newsBulletinsRequestProto.allMessages = allMessages + return newsBulletinsRequestProto + + +def createCancelNewsBulletinsProto() -> CancelNewsBulletinsProto: + cancelNewsBulletinsProto = CancelNewsBulletinsProto() + return cancelNewsBulletinsProto + + +def createNewsBulletin(newsBulletinProto: NewsBulletinProto) -> NewsBulletin: + msgId = ( + newsBulletinProto.newsMsgId if newsBulletinProto.HasField("newsMsgId") else 0 + ) + msgType = ( + newsBulletinProto.newsMsgType + if newsBulletinProto.HasField("newsMsgType") + else 0 + ) + message = ( + newsBulletinProto.newsMessage + if newsBulletinProto.HasField("newsMessage") + else "" + ) + originExch = ( + newsBulletinProto.originatingExch + if newsBulletinProto.HasField("originatingExch") + else "" + ) + + return NewsBulletin(msgId, msgType, message, originExch) diff --git a/ib_async/ticker.py b/ib_async/ticker.py index 5c7a0bf3..858b7d0d 100644 --- a/ib_async/ticker.py +++ b/ib_async/ticker.py @@ -13,6 +13,7 @@ from ib_async.objects import ( Dividends, DOMLevel, + EfpData, FundamentalRatios, HistoricalTick, HistoricalTickBidAsk, @@ -57,14 +58,17 @@ TickType.ASK_YIELD: "askYield", TickType.DELAYED_YIELD_ASK: "askYield", TickType.LAST_YIELD: "lastYield", - TickType.ETF_NAV_CLOSE: "etf_nav_close", - TickType.ETF_NAV_PRIOR_CLOSE: "etf_nav_prior_close", - TickType.ETF_NAV_BID: "etf_nav_bid", - TickType.ETF_NAV_ASK: "etf_nav_ask", - TickType.ETF_NAV_LAST: "etf_nav_last", - TickType.ETF_FROZEN_NAV_LAST: "etf_frozen_nav_last", - TickType.ETF_NAV_HIGH: "etf_nav_high", - TickType.ETF_NAV_LOW: "etf_nav_low", + TickType.LAST_RTH_TRADE: "lastRthTrade", + TickType.CREDITMAN_MARK_PRICE: "creditmanMarkPrice", + TickType.CREDITMAN_SLOW_MARK_PRICE: "creditmanSlowMarkPrice", + TickType.ETF_NAV_CLOSE: "etfNavClose", + TickType.ETF_NAV_PRIOR_CLOSE: "etfNavPriorClose", + TickType.ETF_NAV_BID: "etfNavBid", + TickType.ETF_NAV_ASK: "etfNavAsk", + TickType.ETF_NAV_LAST: "etfNavLast", + TickType.ETF_FROZEN_NAV_LAST: "etfFrozenNavLast", + TickType.ETF_NAV_HIGH: "etfNavHigh", + TickType.ETF_NAV_LOW: "etfNavLow", } @@ -98,6 +102,9 @@ TickType.TRADE_RATE: "tradeRate", TickType.VOLUME_RATE: "volumeRate", TickType.RT_HISTORICAL_VOL: "rtHistVolatility", + TickType.BOND_FACTOR_MULTIPLIER: "bondFactorMultiplier", + TickType.ESTIMATED_IPO_MIDPOINT: "estimatedIpoMidpoint", + TickType.FINAL_IPO_LAST: "finalIpoLast", } GREEKS_TICK_MAP: Final[TickDict] = { @@ -109,6 +116,16 @@ TickType.DELAYED_LAST_OPTION: "lastGreeks", TickType.MODEL_OPTION: "modelGreeks", TickType.DELAYED_MODEL_OPTION: "modelGreeks", + TickType.CUST_OPTION_COMPUTATION: "custGreeks", +} + +TICK_STRING_MAP: Final[TickDict] = { + TickType.OPTION_BID_EXCH: "optionBidExch", + TickType.OPTION_ASK_EXCH: "optionAskExch", + TickType.BID_EXCH: "bidExchange", + TickType.ASK_EXCH: "askExchange", + TickType.LAST_EXCH: "lastExchange", + TickType.LAST_REG_TIME: "lastRegTime", } _logger = logging.getLogger("ib_async.ticker") @@ -196,6 +213,8 @@ class Ticker: putVolume: float = nan callVolume: float = nan avOptionVolume: float = nan + optionBidExch: str = "" + optionAskExch: str = "" histVolatility: float = nan impliedVolatility: float = nan dividends: None | Dividends = None @@ -213,20 +232,35 @@ class Ticker: askGreeks: None | OptionComputation = None lastGreeks: None | OptionComputation = None modelGreeks: None | OptionComputation = None + custGreeks: OptionComputation | None = None + bidEfp: EfpData | None = None + askEfp: EfpData | None = None + lastEfp: EfpData | None = None + openEfp: EfpData | None = None + highEfp: EfpData | None = None + lowEfp: EfpData | None = None + closeEfp: EfpData | None = None auctionVolume: float = nan auctionPrice: float = nan auctionImbalance: float = nan regulatoryImbalance: float = nan bboExchange: str = "" snapshotPermissions: int = 0 - etf_nav_close: float | Decimal = nan - etf_nav_prior_close: float | Decimal = nan - etf_nav_bid: float | Decimal = nan - etf_nav_ask: float | Decimal = nan - etf_nav_last: float | Decimal = nan - etf_frozen_nav_last: float | Decimal = nan - etf_nav_high: float | Decimal = nan - etf_nav_low: float | Decimal = nan + bondFactorMultiplier: float = nan + creditmanMarkPrice: float = nan + creditmanSlowMarkPrice: float = nan + reutersMutualFunds: str = "" + etfNavClose: float | Decimal = nan + etfNavPriorClose: float | Decimal = nan + etfNavBid: float | Decimal = nan + etfNavAsk: float | Decimal = nan + etfNavLast: float | Decimal = nan + etfFrozenNavLast: float | Decimal = nan + etfNavHigh: float | Decimal = nan + etfNavLow: float | Decimal = nan + socialMarketAnalytics: str = "" + estimatedIpoMidpoint: float = nan + finalIpoLast: float = nan defaults: IBDefaults = field(default_factory=IBDefaults, repr=False) created: bool = field(default=False, repr=False) @@ -297,14 +331,28 @@ def __post_init__(self): self.auctionPrice = self.defaults.unset self.auctionImbalance = self.defaults.unset self.regulatoryImbalance = self.defaults.unset - self.etf_nav_close = self.defaults.unset - self.etf_nav_prior_close = self.defaults.unset - self.etf_nav_bid = self.defaults.unset - self.etf_nav_ask = self.defaults.unset - self.etf_nav_last = self.defaults.unset - self.etf_frozen_nav_last = self.defaults.unset - self.etf_nav_high = self.defaults.unset - self.etf_nav_low = self.defaults.unset + self.etfNavClose = self.defaults.unset + self.etfNavPriorClose = self.defaults.unset + self.etfNavBid = self.defaults.unset + self.etfNavAsk = self.defaults.unset + self.etfNavLast = self.defaults.unset + self.etfFrozenNavLast = self.defaults.unset + self.etfNavHigh = self.defaults.unset + self.etfNavLow = self.defaults.unset + self.bidExchange = self.defaults.unset + self.askExchange = self.defaults.unset + self.lastExchange = self.defaults.unset + self.optionBidExch = self.defaults.unset + self.optionAskExch = self.defaults.unset + self.bboExchange = self.defaults.unset + self.snapshotPermissions = self.defaults.unset + self.bondFactorMultiplier = self.defaults.unset + self.creditmanMarkPrice = self.defaults.unset + self.creditmanSlowMarkPrice = self.defaults.unset + self.reutersMutualFunds = self.defaults.unset + self.socialMarketAnalytics = self.defaults.unset + self.estimatedIpoMidpoint = self.defaults.unset + self.finalIpoLast = self.defaults.unset self.created = True def __eq__(self, other): @@ -319,7 +367,7 @@ def __hash__(self): def _on_ticker_data(self, tick_data: TickDataType, last_time: datetime): """get ticker data updates and dispatch to the right handler.""" if isinstance(tick_data, TickPriceData): - self._on_price_size_tick(tick_data, last_time) + self._on_price_tick(tick_data, last_time) elif isinstance(tick_data, TickSizeData): self._on_size_tick(tick_data, last_time) elif isinstance(tick_data, TickStringData): @@ -337,7 +385,7 @@ def _on_ticker_data(self, tick_data: TickDataType, last_time: datetime): else: _logger.error("Ticker %s. Unknown tick data: %s", self.contract, tick_data) - def _on_price_size_tick(self, tick_price: TickPriceData, last_time: datetime): + def _on_price_tick(self, tick_price: TickPriceData, last_time: datetime): price = tick_price.price size = tick_price.size @@ -394,7 +442,8 @@ def _on_price_size_tick(self, tick_price: TickPriceData, last_time: datetime): else: assert tick_price.tickType in PRICE_TICK_MAP, ( - f"Received tick {tick_price.tickType=} {tick_price.price=} but we don't have an attribute mapping for it? Triggered from {self.contract=}" + f"Received tick {tick_price.tickType=} {tick_price.price=} but we don't" + f" have an attribute mapping for it? Triggered from {self.contract=}" ) setattr(self, PRICE_TICK_MAP[tick_price.tickType], tick_price.price) @@ -451,7 +500,8 @@ def _on_size_tick(self, tick_size: TickSizeData, last_time: datetime): self.lastSize = tick_size.size else: assert tick_size.tickType in SIZE_TICK_MAP, ( - f"Received tick {tick_size.tickType=} {tick_size.size=} but we don't have an attribute mapping for it? Triggered from {self.contract=}" + f"Received tick {tick_size.tickType=} {tick_size.size=} but we don't" + f" have an attribute mapping for it? Triggered from {self.contract=}" ) setattr(self, SIZE_TICK_MAP[tick_size.tickType], tick_size.size) @@ -462,12 +512,8 @@ def _on_size_tick(self, tick_size: TickSizeData, last_time: datetime): def _on_tick_string(self, tick_string: TickStringData, last_time: datetime): try: - if tick_string.tickType == TickType.BID_EXCH: - self.bidExchange = tick_string.value - elif tick_string.tickType == TickType.ASK_EXCH: - self.askExchange = tick_string.value - elif tick_string.tickType == TickType.LAST_EXCH: - self.lastExchange = tick_string.value + if tick_string.tickType in TICK_STRING_MAP: + setattr(self, TICK_STRING_MAP[tick_string.tickType], tick_string.value) elif tick_string.tickType in { TickType.LAST_TIMESTAMP, TickType.DELAYED_LAST_TIMESTAMP, diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index aa7ffb6b..e6a188a7 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -305,7 +305,8 @@ def __init__(self, reqId: int, code: int, message: str): class Wrapper: """Wrapper implementation for use with the IB class. - Wrapper keeps track of `state`, accounts, accoutn values, positions, etc. and respond to requests, subscriptions and streming data. + Wrapper keeps track of `state`, accounts, accoutn values, positions, etc. and + respond to requests, subscriptions and streming data. """ # reference back to IB so wrapper can access API methods @@ -337,7 +338,7 @@ class Wrapper: newsTicks: list[NewsTick] = field(init=False) - msgId2NewsBulletin: dict[int, NewsBulletin] = field(init=False) + newsBulletins: dict[int, NewsBulletin] = field(init=False) """ msgId -> NewsBulletin """ tickers: BiDict[int, Ticker] = field(init=False) @@ -402,7 +403,7 @@ def reset(self): self._isReady = False self.fills = {} self.newsTicks = [] - self.msgId2NewsBulletin = {} + self.newsBulletins = {} self.tickers = BiDict[int, Ticker]() self.pendingTickers = set() self.Pnl = BiDict[tuple[str, str], PnL]() @@ -1046,11 +1047,10 @@ def historicalNewsEnd(self, reqId, _hasMore: bool): self._endReq(reqId) def updateNewsBulletin( - self, msgId: int, msgType: int, message: str, origExchange: str + self, msgId: int, newsBulletin:NewsBulletin ): - bulletin = NewsBulletin(msgId, msgType, message, origExchange) - self.msgId2NewsBulletin[msgId] = bulletin - self.ib.newsBulletinEvent.emit(bulletin) + self.newsBulletins[msgId] = newsBulletin + self.ib.newsBulletinEvent.emit(newsBulletin) def receiveFA(self, _faDataType: int, faXmlData: str): self._endReq("requestFA", faXmlData) From b7f1c89cd3d06ca40cfc0fd050432e36e0dca2b8 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Fri, 19 Dec 2025 17:03:51 +0100 Subject: [PATCH 12/48] fix ContractDetails --- ib_async/contract.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ib_async/contract.py b/ib_async/contract.py index 23ae9bf5..d3901cae 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -668,9 +668,9 @@ class ContractDetails: eventContractDescription1: str = "" eventContractDescription2: str = "" - __repr__ = dataclassRepr - __str__ = dataclassRepr - + __repr__ = util.dataclassRepr + __str__ = util.dataclassRepr + def tradingSessions(self) -> list[TradingSession]: return self._parseSessions(self.tradingHours) From 8e3884af5c1de2808d4a74bab1811fae6220a59c Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Fri, 19 Dec 2025 20:38:26 +0100 Subject: [PATCH 13/48] Changes: - refactor: reorganize imports, use relative imports consistenly - feat: news methods implemented, reqNewsProviders, reqHistoricalNews, reqNewsArticle, reqNewsBulletins, cancelNewsBulletins --- ib_async/client.py | 44 +++--- ib_async/decoder.py | 56 +++++++- ib_async/ib.py | 81 ++++++----- ib_async/message.py | 2 + ib_async/objects.py | 16 +-- ib_async/order.py | 2 +- .../protobuf_converters/news_converters.py | 135 +++++++++++++++++- ib_async/ticker.py | 6 +- ib_async/wrapper.py | 39 ++--- 9 files changed, 291 insertions(+), 90 deletions(-) diff --git a/ib_async/client.py b/ib_async/client.py index 137df3f9..9ddd63e5 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -9,13 +9,12 @@ from eventkit import Event from google.protobuf.message import Message -from ib_async.contract import Contract -from ib_async.order import Order, OrderCancel - from .connection import Connection +from .contract import Contract from .decoder import Decoder from .message import MessageId from .objects import ConnectionStats, WshEventData +from .order import Order, OrderCancel from .protobuf.AccountSummaryRequest_pb2 import ( AccountSummaryRequest as AccountSummaryRequestProto, ) @@ -97,7 +96,10 @@ ) from .protobuf_converters.news_converters import ( createCancelNewsBulletinsProto, + createHistoricalNewsRequestProto, + createNewsArticleRequestProto, createNewsBulletinsRequestProto, + createNewsProvidersRequestProto, ) from .protobuf_converters.subscription_converters import ( createCancelPnLProto, @@ -334,9 +336,7 @@ def setConnectOptions(self, connectOptions: str): """ self.connectOptions = connectOptions.encode() - def connect( - self, host: str, port: int, clientId: int, timeout: float|None = 2.0 - ): + def connect(self, host: str, port: int, clientId: int, timeout: float | None = 2.0): """ Connect to a running TWS or IB gateway application. @@ -934,10 +934,18 @@ def reqSmartComponents(self, reqId, bboExchange): ) def reqNewsArticle(self, reqId, providerCode, articleId, newsArticleOptions): - self.send(84, reqId, providerCode, articleId, newsArticleOptions) + self.sendProto( + MessageId.OUT.REQ_NEWS_ARTICLE, + createNewsArticleRequestProto( + reqId, providerCode, articleId, newsArticleOptions + ), + ) def reqNewsProviders(self): - self.send(85) + self.sendProto( + MessageId.OUT.REQ_NEWS_PROVIDERS, + createNewsProvidersRequestProto(), + ) def reqHistoricalNews( self, @@ -949,15 +957,17 @@ def reqHistoricalNews( totalResults, historicalNewsOptions, ): - self.send( - 86, - reqId, - conId, - providerCodes, - startDateTime, - endDateTime, - totalResults, - historicalNewsOptions, + self.sendProto( + MessageId.OUT.REQ_HISTORICAL_NEWS, + createHistoricalNewsRequestProto( + reqId, + conId, + providerCodes, + startDateTime, + endDateTime, + totalResults, + historicalNewsOptions, + ), ) def reqHeadTimeStamp(self, reqId, contract, whatToShow, useRTH, formatDate): diff --git a/ib_async/decoder.py b/ib_async/decoder.py index 9825289a..b6c22d8f 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -2,8 +2,6 @@ import logging -from ib_async.protobuf_converters.news_converters import createNewsBulletin - from .contract import ( Contract, ContractDescription, @@ -61,6 +59,8 @@ from .protobuf.HistoricalDataUpdate_pb2 import ( HistoricalDataUpdate as HistoricalDataUpdateProto, ) +from .protobuf.HistoricalNews_pb2 import HistoricalNews as HistoricalNewsProto +from .protobuf.HistoricalNewsEnd_pb2 import HistoricalNewsEnd as HistoricalNewsEndProto from .protobuf.HistoricalSchedule_pb2 import ( HistoricalSchedule as HistoricalScheduleProto, ) @@ -74,7 +74,9 @@ from .protobuf.ManagedAccounts_pb2 import ManagedAccounts as ManagedAccountsProto from .protobuf.MarketDataType_pb2 import MarketDataType as MarketDataTypeProto from .protobuf.MarketRule_pb2 import MarketRule as MarketRuleProto +from .protobuf.NewsArticle_pb2 import NewsArticle as NewsArticleProto from .protobuf.NewsBulletin_pb2 import NewsBulletin as NewsBulletinProto +from .protobuf.NewsProviders_pb2 import NewsProviders as NewsProvidersProto from .protobuf.NextValidId_pb2 import NextValidId as NextValidIdProto from .protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto from .protobuf.OpenOrdersEnd_pb2 import OpenOrdersEnd as OpenOrderEndProto @@ -98,6 +100,7 @@ from .protobuf.SymbolSamples_pb2 import SymbolSamples as SymbolSamplesProto from .protobuf.TickByTickData_pb2 import TickByTickData as TickByTickDataProto from .protobuf.TickGeneric_pb2 import TickGeneric as TickGenericProto +from .protobuf.TickNews_pb2 import TickNews as TickNewsProto from .protobuf.TickOptionComputation_pb2 import ( TickOptionComputation as TickOptionComputationProto, ) @@ -136,6 +139,13 @@ createTickOptionComputation, createTickParams, ) +from .protobuf_converters.news_converters import ( + createHistoricalNews, + createNewProviders, + createNewsArticle, + createNewsBulletin, + createTickNews, +) from .protobuf_converters.subscription_converters import createScannerDataList from .protobuf_converters.trade_converters import ( createCommissionReport, @@ -218,7 +228,7 @@ class Decoder: AccountSummaryEndProto, "accountSummaryEndProto", ), - # Handles order status updates + # Handles order updates MessageId.IN.ORDER_STATUS: (OrderStatusProto, "orderStatusProto"), MessageId.IN.COMPLETED_ORDER: (CompletedOrderProto, "completedOrderProto"), MessageId.IN.COMPLETED_ORDERS_END: ( @@ -226,21 +236,26 @@ class Decoder: "completedOrdersEndProto", ), MessageId.IN.ORDER_BOUND: (OrderBoundProto, "orderBoundProto"), + # Handles portfolio value updates MessageId.IN.PORTFOLIO_VALUE: (PortfolioValueProto, "updatePortfolioProto"), MessageId.IN.ACCT_UPDATE_TIME: ( AccountUpdateTimeProto, "updateAccountTimeProto", ), + # Handles incoming market data types MessageId.IN.MARKET_DATA_TYPE: (MarketDataTypeProto, "marketDataTypeProto"), + # Handles incoming commission and fees report MessageId.IN.COMMISSION_AND_FEES_REPORT: ( CommissionReportProto, "commissionReportProto", ), + # Handles incoming current time MessageId.IN.CURRENT_TIME: (CurrentTimeProto, "currentTimeProto"), MessageId.IN.CURRENT_TIME_IN_MILLIS: ( CurrentTimeInMillisProto, "currentTimeMiliProto", ), + # Handles incoming historical data MessageId.IN.HEAD_TIMESTAMP: (HeadTimestampProto, "headTimestampProto"), MessageId.IN.HISTORICAL_DATA: (HistoricalDataProto, "historicalDataProto"), MessageId.IN.HISTORICAL_DATA_END: ( @@ -265,6 +280,7 @@ class Decoder: HistoricalScheduleProto, "historicalScheduleProto", ), + # Handles incoming tick data MessageId.IN.REAL_TIME_BARS: (RealTimeBarTickProto, "realTimeBarTickProto"), MessageId.IN.TICK_REQ_PARAMS: (TickReqParamsProto, "tickReqParamsProto"), MessageId.IN.TICK_PRICE: (TickPriceProto, "tickDeliveryProto"), @@ -281,16 +297,26 @@ class Decoder: "tickByTickDataProto", ), MessageId.IN.FUNDAMENTAL_DATA: (FundamentalsDataProto, "fundamentalsDataProto"), + # handel scanner data MessageId.IN.SCANNER_PARAMETERS: ( ScannerParametersProto, "scannerParametersProto", ), MessageId.IN.SCANNER_DATA: (ScannerDataProto, "scannerDataProto"), + # handle pnl MessageId.IN.PNL: (PnLProto, "pnlProto"), MessageId.IN.PNL_SINGLE: (PnLSingleProto, "pnlSingleProto"), MessageId.IN.USER_INFO: (UserInfoProto, "userInfoProto"), MessageId.IN.SMART_COMPONENTS: (SmartComponentsProto, "smartComponentsProto"), - MessageId.IN.NEWS_BULLETINS: (NewsBulletinProto, "newsBulletinProto") + # handle news + MessageId.IN.NEWS_BULLETINS: (NewsBulletinProto, "newsBulletinProto"), + MessageId.IN.NEWS_PROVIDERS: (NewsProvidersProto, "newsProvidersProto"), + MessageId.IN.HISTORICAL_NEWS: (HistoricalNewsProto, "historicalNewsProto"), + MessageId.IN.HISTORICAL_NEWS_END: ( + HistoricalNewsEndProto, + "historicalNewsEndProto"), + MessageId.IN.NEWS_ARTICLE: (NewsArticleProto, "newsArticleProto"), + MessageId.IN.TICK_NEWS: (TickNewsProto, "tickNewsProto"), } def __init__(self, wrapper: Wrapper, serverVersion: int): @@ -593,7 +619,29 @@ def newsBulletinProto(self, msg: NewsBulletinProto): newsBulletin = createNewsBulletin(msg) self.wrapper.updateNewsBulletin(msgId, newsBulletin) + def newsProvidersProto(self, msg: NewsProvidersProto): + newsProviders = createNewProviders(msg) + self.wrapper.newsProviders(newsProviders) + + def historicalNewsProto(self, msg: HistoricalNewsProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + historicalNews = createHistoricalNews(msg) + self.wrapper.historicalNews(reqId, historicalNews) + + def historicalNewsEndProto(self, msg: HistoricalNewsEndProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + self.wrapper.historicalNewsEnd(reqId, msg.hasMore) + + def newsArticleProto(self, msg: NewsArticleProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + newsArticle = createNewsArticle(msg) + self.wrapper.newsArticle(reqId,newsArticle) + def tickNewsProto(self, msg: TickNewsProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + tickNews = createTickNews(msg) + self.wrapper.tickNews(reqId, tickNews) + ##################### legacy methods ########################################## def bondContractDetails(self, fields): diff --git a/ib_async/ib.py b/ib_async/ib.py index 1f7a9e9f..08abbe46 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -14,10 +14,10 @@ from eventkit import Event -import ib_async.util as util -from ib_async.client import Client -from ib_async.contract import Contract, ContractDescription, ContractDetails -from ib_async.objects import ( +from . import util +from .client import Client +from .contract import Contract, ContractDescription, ContractDetails +from .objects import ( AccountValue, BarDataList, DepthMktDataDescription, @@ -47,7 +47,7 @@ TradeLogEntry, WshEventData, ) -from ib_async.order import ( +from .order import ( BracketOrder, LimitOrder, Order, @@ -57,8 +57,8 @@ StopOrder, Trade, ) -from ib_async.ticker import Ticker -from ib_async.wrapper import Wrapper +from .ticker import Ticker +from .wrapper import Wrapper _T = TypeVar("_T") @@ -662,7 +662,7 @@ def pendingTickers(self) -> list[Ticker]: """Get a list of all tickers that have pending ticks or domTicks.""" return list(self.wrapper.pendingTickers) - def realtimeBars(self) -> list[BarDataList| RealTimeBarList]: + def realtimeBars(self) -> list[BarDataList | RealTimeBarList]: """ Get a list of all live updated bars. These can be 5 second realtime bars or live updated historical bars. @@ -840,7 +840,7 @@ def placeOrder(self, contract: Contract, order: Order) -> Trade: def cancelOrder( self, order: Order, orderCancel: OrderCancel | None = None - ) -> Trade|None: + ) -> Trade | None: """ Cancel the order and return the Trade it belongs to. @@ -997,7 +997,7 @@ def reqCompletedOrders(self, apiOnly: bool) -> list[Trade]: """ return self._run(self.reqCompletedOrdersAsync(apiOnly)) - def reqExecutions(self, execFilter: ExecutionFilter| None) -> list[Fill]: + def reqExecutions(self, execFilter: ExecutionFilter | None) -> list[Fill]: """ It is recommended to use :meth:`.fills` or :meth:`.executions` instead. @@ -1213,7 +1213,7 @@ def cancelRealTimeBars(self, bars: RealTimeBarList): def reqHistoricalData( self, contract: Contract, - endDateTime: datetime.datetime| datetime.date| str| None, + endDateTime: datetime.datetime | datetime.date | str | None, durationStr: str, barSizeSetting: str, whatToShow: str, @@ -1296,7 +1296,7 @@ def reqHistoricalSchedule( self, contract: Contract, numDays: int, - endDateTime: datetime.datetime| datetime.date| str| None = "", + endDateTime: datetime.datetime | datetime.date | str | None = "", useRTH: bool = True, ) -> HistoricalSchedule: """ @@ -1321,8 +1321,8 @@ def reqHistoricalSchedule( def reqHistoricalTicks( self, contract: Contract, - startDateTime: str| datetime.date, - endDateTime: str| datetime.date, + startDateTime: str | datetime.date, + endDateTime: str | datetime.date, numberOfTicks: int, whatToShow: str, useRth: bool, @@ -1901,11 +1901,11 @@ def reqHistoricalNews( self, conId: int, providerCodes: str, - startDateTime: str| datetime.date, - endDateTime: str| datetime.date, + startDateTime: str | datetime.date, + endDateTime: str | datetime.date, totalResults: int, historicalNewsOptions: list[TagValue] = [], - ) -> HistoricalNews: + ) -> list[HistoricalNews]: """ Get historical news headline. @@ -2093,7 +2093,7 @@ async def connectAsync( host: str = "127.0.0.1", port: int = 7497, clientId: int = 1, - timeout: float|None = 4, + timeout: float | None = 4, readonly: bool = False, account: str = "", raiseSyncErrors: bool = False, @@ -2509,7 +2509,7 @@ def reqMarketRuleAsync(self, marketRuleId: int) -> Awaitable[list[PriceIncrement async def reqHistoricalDataAsync( self, contract: Contract, - endDateTime: datetime.datetime| datetime.date| str| None, + endDateTime: datetime.datetime | datetime.date | str | None, durationStr: str, barSizeSetting: str, whatToShow: str, @@ -2570,7 +2570,7 @@ def reqHistoricalScheduleAsync( self, contract: Contract, numDays: int, - endDateTime: datetime.datetime| datetime.date| str| None = "", + endDateTime: datetime.datetime | datetime.date | str | None = "", useRTH: bool = True, ) -> Awaitable[HistoricalSchedule]: reqId = self.client.getReqId() @@ -2598,8 +2598,8 @@ def reqHistoricalScheduleAsync( def reqHistoricalTicksAsync( self, contract: Contract, - startDateTime: str| datetime.date, - endDateTime: str| datetime.date, + startDateTime: str | datetime.date, + endDateTime: str | datetime.date, numberOfTicks: int, whatToShow: str, useRth: bool, @@ -2723,7 +2723,7 @@ async def calculateImpliedVolatilityAsync( optionPrice: float, underPrice: float, implVolOptions: list[TagValue] = [], - ) -> OptionComputation|None: + ) -> OptionComputation | None: reqId = self.client.getReqId() self.client.calculateImpliedVolatility( reqId, contract, optionPrice, underPrice, implVolOptions @@ -2749,7 +2749,7 @@ async def calculateOptionPriceAsync( volatility: float, underPrice: float, optPrcOptions: list[TagValue] = [], - ) -> OptionComputation|None: + ) -> OptionComputation | None: reqId = self.client.getReqId() self.client.calculateOptionPrice( reqId, contract, volatility, underPrice, optPrcOptions @@ -2789,7 +2789,12 @@ def reqSecDefOptParamsAsync( ) def reqNewsProvidersAsync(self) -> Awaitable[list[NewsProvider]]: - future = self.wrapper.startReq("newsProviders") + future = ( + self.wrapper.response_bus.filter(lambda name, _: name == "newsProviders") + .takewhile(lambda name, data: data is not None) + .pluck(1) + .map(self._raise_if_error) + ) self.client.reqNewsProviders() return future @@ -2798,7 +2803,13 @@ def reqNewsArticleAsync( ) -> Awaitable[NewsArticle]: reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId) + future = ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) + self.client.reqNewsArticle(reqId, providerCode, articleId, newsArticleOptions) return future @@ -2806,22 +2817,28 @@ async def reqHistoricalNewsAsync( self, conId: int, providerCodes: str, - startDateTime: str| datetime.date, - endDateTime: str| datetime.date, + startDateTime: str | datetime.date, + endDateTime: str | datetime.date, totalResults: int, historicalNewsOptions: list[TagValue] = [], - ) -> HistoricalNews|None: + ) -> list[HistoricalNews] | None: reqId = self.client.getReqId() - future = self.wrapper.startReq(reqId) start = util.formatIBDatetime(startDateTime) end = util.formatIBDatetime(endDateTime) self.client.reqHistoricalNews( reqId, conId, providerCodes, start, end, totalResults, historicalNewsOptions ) + future = ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .takewhile(lambda rId, data: data is not None) + .pluck(1) + .map(self._raise_if_error) + .list() + ) try: - await asyncio.wait_for(future, 4) - return future.result() + result = await asyncio.wait_for(future, 4) + return result except asyncio.TimeoutError: self._logger.error("reqHistoricalNewsAsync: Timeout") return None diff --git a/ib_async/message.py b/ib_async/message.py index a8f7f83a..cd57d570 100644 --- a/ib_async/message.py +++ b/ib_async/message.py @@ -1,3 +1,5 @@ +"""Protobuf message ID constants.""" + from enum import IntEnum diff --git a/ib_async/objects.py b/ib_async/objects.py index dbc87151..6ccecb3b 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -474,12 +474,6 @@ class HistogramData: count: int = 0 -@dataclass(slots=True) -class NewsProvider: - code: str = "" - name: str = "" - - @dataclass class DepthMktDataDescription: exchange: str = "" @@ -661,6 +655,12 @@ class Dividends: nextAmount: float | None +@dataclass(slots=True, frozen=True) +class NewsProvider: + code: str = "" + name: str = "" + + @dataclass(slots=True, frozen=True) class NewsArticle: articleType: int @@ -851,11 +851,11 @@ class IBDefaults: emptyPrice: Any = -1 emptySize: Any = 0 - # optionally replace ib_async default for all instance variable values before + # optionally replace ib_async default for all instance variable values before # popualted from API updates unset: Any = nan - # optionally change the timezone used for log history events in objects (no impact + # optionally change the timezone used for log history events in objects (no impact # on orders or data processing) timezone: tzinfo = timezone.utc diff --git a/ib_async/order.py b/ib_async/order.py index 69ea77b0..070d9fb6 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -283,7 +283,7 @@ class OrderStatus: @property def total(self) -> float | Decimal: """Helper property to return the total size of this requested order.""" - return self.filled + self.remaining + return self.filled + self.remaining # type: ignore PendingSubmit: ClassVar[str] = "PendingSubmit" PendingCancel: ClassVar[str] = "PendingCancel" diff --git a/ib_async/protobuf_converters/news_converters.py b/ib_async/protobuf_converters/news_converters.py index a59fea11..75602ed3 100644 --- a/ib_async/protobuf_converters/news_converters.py +++ b/ib_async/protobuf_converters/news_converters.py @@ -1,11 +1,31 @@ -from ..objects import NewsBulletin +"""News objects converter.""" + +from datetime import datetime +from typing import cast +from ..contract import TagValue +from ..objects import HistoricalNews, NewsArticle, NewsBulletin, NewsProvider, NewsTick from ..protobuf.CancelNewsBulletins_pb2 import ( CancelNewsBulletins as CancelNewsBulletinsProto, ) +from ..protobuf.HistoricalNews_pb2 import HistoricalNews as HistoricalNewsProto +from ..protobuf.HistoricalNewsRequest_pb2 import ( + HistoricalNewsRequest as HistoricalNewsRequestProto, +) +from ..protobuf.NewsArticle_pb2 import NewsArticle as NewsArticleProto +from ..protobuf.NewsArticleRequest_pb2 import ( + NewsArticleRequest as NewsArticleRequestProto, +) from ..protobuf.NewsBulletin_pb2 import NewsBulletin as NewsBulletinProto from ..protobuf.NewsBulletinsRequest_pb2 import ( NewsBulletinsRequest as NewsBulletinsRequestProto, ) +from ..protobuf.NewsProviders_pb2 import NewsProviders as NewsProvidersProto +from ..protobuf.NewsProvidersRequest_pb2 import ( + NewsProvidersRequest as NewsProvidersRequestProto, +) +from ..protobuf.TickNews_pb2 import TickNews as TickNewsProto +from ..protobuf_converters.base_converters import fillTagValueList +from ..util import EPOCH, isValidIntValue, parseIBDatetime def createNewsBulletinsRequestProto(allMessages: bool) -> NewsBulletinsRequestProto: @@ -41,3 +61,116 @@ def createNewsBulletin(newsBulletinProto: NewsBulletinProto) -> NewsBulletin: ) return NewsBulletin(msgId, msgType, message, originExch) + + +def createNewsProvidersRequestProto() -> NewsProvidersRequestProto: + newsProvidersRequestProto = NewsProvidersRequestProto() + return newsProvidersRequestProto + + +def createNewsArticleRequestProto( + reqId: int, + providerCode: str, + articleId: str, + newsArticleOptionsList: list[TagValue], +) -> NewsArticleRequestProto: + newsArticleRequestProto = NewsArticleRequestProto() + if isValidIntValue(reqId): + newsArticleRequestProto.reqId = reqId + if providerCode: + newsArticleRequestProto.providerCode = providerCode + if articleId: + newsArticleRequestProto.articleId = articleId + fillTagValueList(newsArticleOptionsList, newsArticleRequestProto.newsArticleOptions) + return newsArticleRequestProto + + +def createHistoricalNewsRequestProto( + reqId: int, + conId: int, + providerCodes: str, + startDateTime: str, + endDateTime: str, + totalResults: int, + historicalNewsOptionsList: list[TagValue], +) -> HistoricalNewsRequestProto: + historicalNewsRequestProto = HistoricalNewsRequestProto() + if isValidIntValue(reqId): + historicalNewsRequestProto.reqId = reqId + if isValidIntValue(conId): + historicalNewsRequestProto.conId = conId + if providerCodes: + historicalNewsRequestProto.providerCodes = providerCodes + if startDateTime: + historicalNewsRequestProto.startDateTime = startDateTime + if endDateTime: + historicalNewsRequestProto.endDateTime = endDateTime + if isValidIntValue(totalResults): + historicalNewsRequestProto.totalResults = totalResults + fillTagValueList( + historicalNewsOptionsList, historicalNewsRequestProto.historicalNewsOptions + ) + return historicalNewsRequestProto + + +def createNewProviders(newsProvidersProto: NewsProvidersProto) -> list[NewsProvider]: + newsProviders = [] + if newsProvidersProto.newsProviders: + for newsProviderProto in newsProvidersProto.newsProviders: + code = ( + newsProviderProto.providerCode + if newsProviderProto.HasField("providerCode") + else "" + ) + name = ( + newsProviderProto.providerName + if newsProviderProto.HasField("providerName") + else "" + ) + newsProvider = NewsProvider(code, name) + newsProviders.append(newsProvider) + return newsProviders + + +def createNewsArticle(newsArticleProto: NewsArticleProto) -> NewsArticle: + articleType = ( + newsArticleProto.articleType if newsArticleProto.HasField("articleType") else 0 + ) + articleText = ( + newsArticleProto.articleText if newsArticleProto.HasField("articleText") else "" + ) + return NewsArticle(articleType, articleText) + + +def createHistoricalNews(historicalNewsProto: HistoricalNewsProto) -> HistoricalNews: + time = ( + parseIBDatetime(historicalNewsProto.time) + if historicalNewsProto.HasField("time") + else EPOCH + ) + time = cast(datetime, time) + providerCode = ( + historicalNewsProto.providerCode + if historicalNewsProto.HasField("providerCode") + else "" + ) + articleId = ( + historicalNewsProto.articleId + if historicalNewsProto.HasField("articleId") + else "" + ) + headline = ( + historicalNewsProto.headline if historicalNewsProto.HasField("headline") else "" + ) + return HistoricalNews(time, providerCode, articleId, headline) + + +def createTickNews(tickNewsProto: TickNewsProto) -> NewsTick: + timestamp = tickNewsProto.timestamp if tickNewsProto.HasField("timestamp") else 0 + providerCode = ( + tickNewsProto.providerCode if tickNewsProto.HasField("providerCode") else "" + ) + articleId = tickNewsProto.articleId if tickNewsProto.HasField("articleId") else "" + headline = tickNewsProto.headline if tickNewsProto.HasField("headline") else "" + extraData = tickNewsProto.extraData if tickNewsProto.HasField("extraData") else "" + return NewsTick(timestamp, providerCode, articleId, headline, extraData) diff --git a/ib_async/ticker.py b/ib_async/ticker.py index 858b7d0d..66684d5d 100644 --- a/ib_async/ticker.py +++ b/ib_async/ticker.py @@ -9,8 +9,8 @@ from eventkit import Event, Op -from ib_async.contract import Contract -from ib_async.objects import ( +from .contract import Contract +from .objects import ( Dividends, DOMLevel, EfpData, @@ -30,7 +30,7 @@ TickStringData, TickType, ) -from ib_async.util import dataclassRepr, isNan, parseIBDatetime +from .util import dataclassRepr, isNan, parseIBDatetime nan = float("nan") TickDict: TypeAlias = dict[TickType, str] diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index e6a188a7..ac395428 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -21,14 +21,14 @@ import eventkit as ev -from ib_async.contract import ( +from .contract import ( Contract, ContractDescription, ContractDetails, DeltaNeutralContract, ScanData, ) -from ib_async.objects import ( +from .objects import ( AccountValue, BarData, BarDataList, @@ -62,9 +62,9 @@ TickParams, TradeLogEntry, ) -from ib_async.order import Order, OrderState, OrderStatus, Trade -from ib_async.ticker import Ticker -from ib_async.util import ( +from .order import Order, OrderState, OrderStatus, Trade +from .ticker import Ticker +from .util import ( EPOCH, dataclassUpdate, getLoop, @@ -73,7 +73,7 @@ ) if TYPE_CHECKING: - from ib_async.ib import IB + from .ib import IB OrderKeyType: TypeAlias = int | tuple[int, int] @@ -1015,33 +1015,24 @@ def securityDefinitionOptionParameterEnd(self, reqId: int): self._endReq(reqId) def newsProviders(self, newsProviders: list[NewsProvider]): - newsProviders = [NewsProvider(code=p.code, name=p.name) for p in newsProviders] - self._endReq("newsProviders", newsProviders) + self.response_bus.emit("newsProviders", newsProviders) + self._endReq("newsProviders") def tickNews( self, _reqId: int, - timeStamp: int, - providerCode: str, - articleId: str, - headline: str, - extraData: str, + newsTick: NewsTick, ): - news = NewsTick(timeStamp, providerCode, articleId, headline, extraData) - self.newsTicks.append(news) - self.ib.tickNewsEvent.emit(news) + self.newsTicks.append(newsTick) + self.ib.tickNewsEvent.emit(newsTick) - def newsArticle(self, reqId: int, articleType: int, articleText: str): - article = NewsArticle(articleType, articleText) - self._endReq(reqId, article) + def newsArticle(self, reqId: int, newsArticle:NewsArticle): + self.response_bus.emit(reqId,newsArticle) def historicalNews( - self, reqId: int, time: str, providerCode: str, articleId: str, headline: str + self, reqId: int, historicalNews: HistoricalNews ): - dt = parseIBDatetime(time) - dt = cast(datetime, dt) - article = HistoricalNews(dt, providerCode, articleId, headline) - self._results[reqId].append(article) + self.response_bus.emit(reqId, historicalNews) def historicalNewsEnd(self, reqId, _hasMore: bool): self._endReq(reqId) From becd0be50fe7da56a07c1c99647f60b1597639c7 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Mon, 29 Dec 2025 14:37:58 +0100 Subject: [PATCH 14/48] implement requestFAAsync and replaceFA --- ib_async/client.py | 51 ++++++++----------- ib_async/decoder.py | 17 +++++++ .../protobuf_converters/account_converters.py | 36 +++++++++++++ .../protobuf_converters/base_converters.py | 9 ++++ ib_async/wrapper.py | 6 ++- 5 files changed, 89 insertions(+), 30 deletions(-) diff --git a/ib_async/client.py b/ib_async/client.py index 9ddd63e5..0052172e 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -66,8 +66,11 @@ createAccountDataRequestProto, createAccountMultiRequestProto, createCancelAccMultiRequestProto, + createFAReplaceProto, + createFARequestProto, createUserInfoRequestProto, ) +from .protobuf_converters.base_converters import createSetServerLogLevelRequestProto from .protobuf_converters.contract_converters import ( createContractProto, createMarketRuleRequestProto, @@ -391,7 +394,7 @@ def disconnect(self): self.reset() def _prefix(self, msg): - # prefix a message with its length + """Prefix a message with its length.""" return struct.pack(f">I{len(msg)}s", len(msg), msg) def _onSocketHasData(self, data): @@ -434,7 +437,8 @@ def _onSocketHasData(self, data): if self._serverVersion < 201: # MIN_SERVER_VER_PROTOBUF self._onSocketDisconnected( - f"Server version {self._serverVersion} does not support Protobuf. Disconnecting." + f"Server version {self._serverVersion} does not support" + " Protobuf. Disconnecting." ) return @@ -483,6 +487,14 @@ def _onSocketDisconnected(self, msg): if wasReady: self.apiEnd.emit() + def startApi(self): + startApiRequestProto = StartApiRequestProto() + if self.clientId >= 0: # Assuming 0 or negative clientId is invalid + startApiRequestProto.clientId = self.clientId + if self.optCapab: # Only set if not an empty string + startApiRequestProto.optionalCapabilities = self.optCapab + self.sendProto(MessageId.OUT.START_API, startApiRequestProto) + # client request methods # the message type id is sent first, often followed by a version number @@ -553,22 +565,6 @@ def reqContractDetails(self, reqId, contract): contractDetailsRequestProto, ) - def reqContractDetailsLegacy(self, reqId, contract): - fields = [ - 9, - 8, - reqId, - contract, - contract.includeExpired, - contract.secIdType, - contract.secId, - ] - - if self.serverVersion() >= 176: - fields += [contract.issuerId] - - self.send(*fields) - def reqMktDepth(self, reqId, contract, numRows, isSmartDepth, mktDepthOptions): self.send( 10, @@ -607,7 +603,10 @@ def cancelNewsBulletins(self): ) def setServerLogLevel(self, logLevel): - self.send(14, 1, logLevel) + self.sendProto( + MessageId.OUT.SET_SERVER_LOGLEVEL, + createSetServerLogLevelRequestProto(logLevel), + ) def reqAutoOpenOrders(self, bAutoBind: bool): autoOpenOrdersRequestProto = AutoOpenOrdersRequestProto() @@ -630,10 +629,12 @@ def reqManagedAccts(self): self.sendProto(MessageId.OUT.REQ_MANAGED_ACCTS, proto) def requestFA(self, faData): - self.send(18, 1, faData) + faRequestProto = createFARequestProto(faData) + self.sendProto(MessageId.OUT.REQ_FA, faRequestProto) def replaceFA(self, reqId, faData, cxml): - self.send(19, 1, faData, cxml, reqId) + faReplaceProto = createFAReplaceProto(reqId, faData, cxml) + self.sendProto(MessageId.OUT.REPLACE_FA, faReplaceProto) def reqHistoricalData( self, @@ -854,14 +855,6 @@ def updateDisplayGroup(self, reqId, contractInfo): def unsubscribeFromGroupEvents(self, reqId): self.send(70, 1, reqId) - def startApi(self): - startApiRequestProto = StartApiRequestProto() - if self.clientId >= 0: # Assuming 0 or negative clientId is invalid - startApiRequestProto.clientId = self.clientId - if self.optCapab: # Only set if not an empty string - startApiRequestProto.optionalCapabilities = self.optCapab - self.sendProto(MessageId.OUT.START_API, startApiRequestProto) - def verifyAndAuthRequest(self, apiName, apiVersion, opaqueIsvKey): self.send(72, 1, apiName, apiVersion, opaqueIsvKey) diff --git a/ib_async/decoder.py b/ib_async/decoder.py index b6c22d8f..7c676525 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -88,6 +88,8 @@ from .protobuf.Position_pb2 import Position as PositionProto from .protobuf.PositionEnd_pb2 import PositionEnd as PositionEndProto from .protobuf.RealTimeBarTick_pb2 import RealTimeBarTick as RealTimeBarTickProto +from .protobuf.ReceiveFA_pb2 import ReceiveFA as ReceiveFAProto +from .protobuf.ReplaceFAEnd_pb2 import ReplaceFAEnd as ReplaceFAEndProto from .protobuf.ScannerData_pb2 import ScannerData as ScannerDataProto from .protobuf.ScannerParameters_pb2 import ScannerParameters as ScannerParametersProto from .protobuf.SecDefOptParameter_pb2 import ( @@ -114,8 +116,10 @@ createAccountSummary, createAccountValue, createAccountValueFromUpdateMulti, + createFAmsg, createPortfolioItem, createPosition, + createReplaceFAEnd, ) from .protobuf_converters.contract_converters import ( createContractDescription, @@ -317,6 +321,9 @@ class Decoder: "historicalNewsEndProto"), MessageId.IN.NEWS_ARTICLE: (NewsArticleProto, "newsArticleProto"), MessageId.IN.TICK_NEWS: (TickNewsProto, "tickNewsProto"), + # Financial Advisor, FA messages + MessageId.IN.RECEIVE_FA: (ReceiveFAProto, "receiveFAProto"), + MessageId.IN.REPLACE_FA_END: (ReplaceFAEndProto, "replaceFAEndProto"), } def __init__(self, wrapper: Wrapper, serverVersion: int): @@ -642,6 +649,16 @@ def tickNewsProto(self, msg: TickNewsProto): tickNews = createTickNews(msg) self.wrapper.tickNews(reqId, tickNews) + def receiveFAProto(self, msg: ReceiveFAProto): + faDataType,xml = createFAmsg(msg) + self.wrapper.receiveFA(faDataType, xml) + + def replaceFAEndProto(self, msg: ReplaceFAEndProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + text = createReplaceFAEnd(msg) + self.wrapper.replaceFAEnd(reqId,text) + + ##################### legacy methods ########################################## def bondContractDetails(self, fields): diff --git a/ib_async/protobuf_converters/account_converters.py b/ib_async/protobuf_converters/account_converters.py index 1e0d2696..b63eb8fe 100644 --- a/ib_async/protobuf_converters/account_converters.py +++ b/ib_async/protobuf_converters/account_converters.py @@ -19,9 +19,13 @@ from ..protobuf.CancelAccountUpdatesMulti_pb2 import ( CancelAccountUpdatesMulti as CancelAccountUpdatesMultiProto, ) +from ..protobuf.FAReplace_pb2 import FAReplace as FAReplaceProto +from ..protobuf.FARequest_pb2 import FARequest as FARequestProto from ..protobuf.IdsRequest_pb2 import IdsRequest as IdsRequestProto from ..protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto from ..protobuf.Position_pb2 import Position as PositionProto +from ..protobuf.ReceiveFA_pb2 import ReceiveFA as ReceiveFAProto +from ..protobuf.ReplaceFAEnd_pb2 import ReplaceFAEnd as ReplaceFAEndProto from .contract_converters import createContract @@ -179,3 +183,35 @@ def createUserInfoRequestProto(reqId: int) -> IdsRequestProto: if isValidIntValue(reqId): idsRequestProto.numIds = reqId return idsRequestProto + + +def createFARequestProto(faDataType: int) -> FARequestProto: + faRequestProto = FARequestProto() + if isValidIntValue(faDataType): + faRequestProto.faDataType = faDataType + return faRequestProto + + +def createFAReplaceProto(reqId: int, faDataType: int, xml: str) -> FAReplaceProto: + faReplaceProto = FAReplaceProto() + if isValidIntValue(reqId): + faReplaceProto.reqId = reqId + if isValidIntValue(faDataType): + faReplaceProto.faDataType = faDataType + if xml: + faReplaceProto.xml = xml + return faReplaceProto + + +def createFAmsg(msg: ReceiveFAProto) -> tuple[int, str]: + faDataType = ( + msg.faDataType if msg.HasField("faDataType") else 0 + ) + xml = msg.xml if msg.HasField("xml") else "" + + return faDataType, xml + +def createReplaceFAEnd(msg: ReplaceFAEndProto) -> str: + text = msg.text if msg.HasField('text') else "" + return text + \ No newline at end of file diff --git a/ib_async/protobuf_converters/base_converters.py b/ib_async/protobuf_converters/base_converters.py index 45a4980b..acd3b018 100644 --- a/ib_async/protobuf_converters/base_converters.py +++ b/ib_async/protobuf_converters/base_converters.py @@ -1,6 +1,10 @@ """Base converters for protobuf messages.""" from ..contract import TagValue +from ..protobuf.SetServerLogLevelRequest_pb2 import ( + SetServerLogLevelRequest as SetServerLogLevelRequestProto, +) +from ..util import isValidIntValue class ClientException(Exception): @@ -15,3 +19,8 @@ def fillTagValueList(tagValueList: list[TagValue], orderProtoMap: dict): if tagValueList is not None and tagValueList: for tagValue in tagValueList: orderProtoMap[tagValue.tag] = tagValue.value + +def createSetServerLogLevelRequestProto(logLevel: int) -> SetServerLogLevelRequestProto: + setServerLogLevelRequestProto = SetServerLogLevelRequestProto() + if isValidIntValue(logLevel): setServerLogLevelRequestProto.logLevel = logLevel + return setServerLogLevelRequestProto diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index ac395428..8b3ab0c8 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -1044,8 +1044,12 @@ def updateNewsBulletin( self.ib.newsBulletinEvent.emit(newsBulletin) def receiveFA(self, _faDataType: int, faXmlData: str): - self._endReq("requestFA", faXmlData) + self.response_bus.emit("requestFA", faXmlData) + self._endReq("requestFA") + def replaceFAEnd(self, reqId:int,text:str): + self._logger.info("Replace FA Response: %s, %s", reqId, text) + def currentTime(self, time: int): dt = datetime.fromtimestamp(time, self.defaultTimezone) self.response_bus.emit("currentTime", dt) From 57b8d134f7b82d77bb624d4ee5d28843340f22ac Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Mon, 29 Dec 2025 18:12:46 +0100 Subject: [PATCH 15/48] Changes ======= - Add support for position multi requests - add support for soft dollar tiers - add support for family codes --- ib_async/client.py | 30 +++- ib_async/decoder.py | 65 ++++---- ib_async/ib.py | 64 +++++++- ib_async/objects.py | 9 ++ .../protobuf_converters/account_converters.py | 140 +++++++++++++++++- ib_async/util.py | 6 +- ib_async/wrapper.py | 34 +++-- 7 files changed, 288 insertions(+), 60 deletions(-) diff --git a/ib_async/client.py b/ib_async/client.py index 0052172e..c9d9e840 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -66,8 +66,12 @@ createAccountDataRequestProto, createAccountMultiRequestProto, createCancelAccMultiRequestProto, + createCancelPositionsMultiRequestProto, + createFamilyCodesRequestProto, createFAReplaceProto, createFARequestProto, + createPositionsMultiRequestProto, + createSoftDollarTiersRequestProto, createUserInfoRequestProto, ) from .protobuf_converters.base_converters import createSetServerLogLevelRequestProto @@ -826,6 +830,18 @@ def cancelPositions(self): CancelPositionsProto(), ) + def reqPositionsMulti(self, reqId, account, modelCode): + self.sendProto( + MessageId.OUT.REQ_POSITIONS_MULTI, + createPositionsMultiRequestProto(reqId, account, modelCode), + ) + + def cancelPositionsMulti(self, reqId): + self.sendProto( + MessageId.OUT.CANCEL_POSITIONS_MULTI, + createCancelPositionsMultiRequestProto(reqId), + ) + def reqAccountSummary(self, reqId, groupName, tags): proto = AccountSummaryRequestProto(reqId=reqId, group=groupName, tags=tags) self.sendProto(MessageId.OUT.REQ_ACCOUNT_SUMMARY, proto) @@ -861,11 +877,7 @@ def verifyAndAuthRequest(self, apiName, apiVersion, opaqueIsvKey): def verifyAndAuthMessage(self, apiData, xyzResponse): self.send(73, 1, apiData, xyzResponse) - def reqPositionsMulti(self, reqId, account, modelCode): - self.send(74, 1, reqId, account, modelCode) - def cancelPositionsMulti(self, reqId): - self.send(75, 1, reqId) def reqAccountUpdatesMulti( self, reqId: int, account: str, modelCode: str, ledgerAndNLV: bool @@ -905,10 +917,16 @@ def reqSecDefOptParams( ) def reqSoftDollarTiers(self, reqId): - self.send(79, reqId) + self.sendProto( + MessageId.OUT.REQ_SOFT_DOLLAR_TIERS, + createSoftDollarTiersRequestProto(reqId), + ) def reqFamilyCodes(self): - self.send(80) + self.sendProto( + MessageId.OUT.REQ_FAMILY_CODES, + createFamilyCodesRequestProto(), + ) def reqMatchingSymbols(self, reqId, pattern): matchingSymbolsRequestProto = createMatchingSymbolsRequestProto(reqId, pattern) diff --git a/ib_async/decoder.py b/ib_async/decoder.py index 7c676525..f7e10141 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -11,12 +11,10 @@ from .message import MessageId from .objects import ( DepthMktDataDescription, - FamilyCode, HistogramData, HistoricalTickType, NewsProvider, PriceIncrement, - SoftDollarTier, TagValue, ) from .order import OrderStatus @@ -51,6 +49,7 @@ from .protobuf.ExecutionDetailsEnd_pb2 import ( ExecutionDetailsEnd as ExecutionDetailsEndProto, ) +from .protobuf.FamilyCodes_pb2 import FamilyCodes as FamilyCodesProto from .protobuf.FundamentalsData_pb2 import FundamentalsData as FundamentalsDataProto from .protobuf.HeadTimestamp_pb2 import HeadTimestamp as HeadTimestampProto from .protobuf.HistogramData_pb2 import HistogramData as HistogramDataProto @@ -87,6 +86,10 @@ from .protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto from .protobuf.Position_pb2 import Position as PositionProto from .protobuf.PositionEnd_pb2 import PositionEnd as PositionEndProto +from .protobuf.PositionMulti_pb2 import PositionMulti as PositionMultiProto +from .protobuf.PositionsMultiRequest_pb2 import ( + PositionsMultiRequest as PositionMultiEndProto, +) from .protobuf.RealTimeBarTick_pb2 import RealTimeBarTick as RealTimeBarTickProto from .protobuf.ReceiveFA_pb2 import ReceiveFA as ReceiveFAProto from .protobuf.ReplaceFAEnd_pb2 import ReplaceFAEnd as ReplaceFAEndProto @@ -99,6 +102,7 @@ SecDefOptParameterEnd as SecDefOptParameterEndProto, ) from .protobuf.SmartComponents_pb2 import SmartComponents as SmartComponentsProto +from .protobuf.SoftDollarTiers_pb2 import SoftDollarTiers as SoftDollarTiersProto from .protobuf.SymbolSamples_pb2 import SymbolSamples as SymbolSamplesProto from .protobuf.TickByTickData_pb2 import TickByTickData as TickByTickDataProto from .protobuf.TickGeneric_pb2 import TickGeneric as TickGenericProto @@ -119,7 +123,9 @@ createFAmsg, createPortfolioItem, createPosition, + createPositionMulti, createReplaceFAEnd, + createSoftDollarTiers, ) from .protobuf_converters.contract_converters import ( createContractDescription, @@ -217,6 +223,13 @@ class Decoder: AccountUpdateMultiEndProto, "accountUpdateMultiEndProto", ), + MessageId.IN.POSITION_MULTI: (PositionMultiProto, "positionMultiProto"), + MessageId.IN.POSITION_MULTI_END: ( + PositionMultiEndProto, + "positionMultiEndProto", + ), + MessageId.IN.FAMILY_CODES: (FamilyCodesProto, "familyCodesProto"), + MessageId.IN.SOFT_DOLLAR_TIERS: (SoftDollarTiersProto, "softDollarTiersProto"), # Handles incoming open order updates MessageId.IN.OPEN_ORDER: (OpenOrderProto, "openOrderProto"), MessageId.IN.OPEN_ORDER_END: (OpenOrderEndProto, "openOrderEndProto"), @@ -318,7 +331,8 @@ class Decoder: MessageId.IN.HISTORICAL_NEWS: (HistoricalNewsProto, "historicalNewsProto"), MessageId.IN.HISTORICAL_NEWS_END: ( HistoricalNewsEndProto, - "historicalNewsEndProto"), + "historicalNewsEndProto", + ), MessageId.IN.NEWS_ARTICLE: (NewsArticleProto, "newsArticleProto"), MessageId.IN.TICK_NEWS: (TickNewsProto, "tickNewsProto"), # Financial Advisor, FA messages @@ -434,6 +448,14 @@ def accountUpdateMultiProto(self, msg: AccountUpdateMultiProto): def accountUpdateMultiEndProto(self, msg: AccountUpdateMultiEndProto): self.wrapper.accountUpdateMultiEnd(msg.reqId) + def positionMultiProto(self, msg: PositionMultiProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + positionMulti = createPositionMulti(msg) + self.wrapper.positionMulti(reqId, positionMulti) + + def positionMultiEndProto(self, msg: PositionMultiEndProto): + self.wrapper.positionMultiEnd(msg.reqId) + def accountSummaryProto(self, msg: AccountSummaryProto): accountValue = createAccountSummary(msg) self.wrapper.accountSummary(msg.reqId, accountValue) @@ -642,22 +664,29 @@ def historicalNewsEndProto(self, msg: HistoricalNewsEndProto): def newsArticleProto(self, msg: NewsArticleProto): reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID newsArticle = createNewsArticle(msg) - self.wrapper.newsArticle(reqId,newsArticle) - + self.wrapper.newsArticle(reqId, newsArticle) + def tickNewsProto(self, msg: TickNewsProto): reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID tickNews = createTickNews(msg) self.wrapper.tickNews(reqId, tickNews) def receiveFAProto(self, msg: ReceiveFAProto): - faDataType,xml = createFAmsg(msg) + faDataType, xml = createFAmsg(msg) self.wrapper.receiveFA(faDataType, xml) - + def replaceFAEndProto(self, msg: ReplaceFAEndProto): reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID text = createReplaceFAEnd(msg) - self.wrapper.replaceFAEnd(reqId,text) + self.wrapper.replaceFAEnd(reqId, text) + def softDollarTiersProto(self, msg: SoftDollarTiersProto): + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + tiers = createSoftDollarTiers(msg) + self.wrapper.softDollarTiers(reqId, tiers) + + def familyCodesProto(self, msg: FamilyCodesProto): + self.wrapper.familyCodes(msg) ##################### legacy methods ########################################## @@ -749,26 +778,6 @@ def deltaNeutralValidation(self, fields): DeltaNeutralContract(int(conId), float(delta or 0), float(price or 0)), ) - def softDollarTiers(self, fields): - _, reqId, n, *fields = fields - get = iter(fields).__next__ - - tiers = [ - SoftDollarTier(name=get(), val=get(), displayName=get()) - for _ in range(int(n)) - ] - - self.wrapper.softDollarTiers(int(reqId), tiers) - - def familyCodes(self, fields): - _, n, *fields = fields - get = iter(fields).__next__ - - familyCodes = [ - FamilyCode(accountID=get(), familyCodeStr=get()) for _ in range(int(n)) - ] - - self.wrapper.familyCodes(familyCodes) def mktDepthExchanges(self, fields): _, n, *fields = fields diff --git a/ib_async/ib.py b/ib_async/ib.py index 08abbe46..41f58934 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -38,6 +38,7 @@ PnLSingle, PortfolioItem, Position, + PositionMulti, PriceIncrement, RealTimeBarList, ScanDataList, @@ -205,6 +206,9 @@ class IB: * ``positionEvent`` (position: :class:`.Position`): A position has changed. + * ``positionMultiEvent`` (positionMulti: :class:`.PositionMulti`): + A positionMulti has changed. + * ``accountValueEvent`` (value: :class:`.AccountValue`): An account value has changed. @@ -571,6 +575,27 @@ def positions(self, account: str = "") -> list[Position]: return [v for d in self.wrapper.positions.values() for v in d.values()] + def positionsMulti(self, account: str = "", modelCode="") -> list[PositionMulti]: + """ + List of positions for the given account, + or of all accounts if account is left blank. + + Args: + account: If specified, filter for this account name. + """ + if account: + return list( + v + for v in self.wrapper.positionsMulti[account].values() + if (not modelCode or v.modelCode == modelCode) + ) + return [ + v + for d in self.wrapper.positionsMulti.values() + for v in d.values() + if (not modelCode or v.modelCode == modelCode) + ] + def pnl(self, account="", modelCode="") -> list[PnL]: """ List of subscribed :class:`.PnL` objects (profit and loss), @@ -1021,6 +1046,20 @@ def reqPositions(self) -> list[Position]: """ return self._run(self.reqPositionsAsync()) + def reqPositionsMulti( + self, account: str = "", modelCode: str = "" + ) -> list[PositionMulti]: + """ + Requests position subscription for account and/or model Initially all positions are returned, and then updates are returned for any position changes in real time. + + This method is blocking. + + Args: + account: If specified, return positions for this account. + modelCode: If specified, return positions for this account model. + """ + return self._run(self.reqPositionsMultiAsync(account)) + def reqPnL(self, account: str, modelCode: str = "") -> PnL: """ Start a subscription for profit and loss events. @@ -1984,6 +2023,7 @@ def replaceFA(self, faDataType: int, xml: str): """ reqId = self.client.getReqId() self.client.replaceFA(reqId, faDataType, xml) + self._logger.info("Replace FA request sent, %s", reqId) def reqWshMetaData(self): """ @@ -2434,6 +2474,19 @@ def reqPositionsAsync(self) -> Awaitable[list[Position]]: .map(self._raise_if_error) ) + def reqPositionsMultiAsync( + self, account: str, modelCode: str = "" + ) -> Awaitable[list[Position]]: + reqId = self.client.getReqId() + self.client.reqPositionsMulti(reqId, account, modelCode) + return ( + self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) + .takewhile(lambda rId, data: data is not None) + .pluck(1) + .map(self._raise_if_error) + .list() + ) + def reqContractDetailsAsync( self, contract: Contract ) -> "Awaitable[list[ContractDetails]]": @@ -2844,11 +2897,16 @@ async def reqHistoricalNewsAsync( return None async def requestFAAsync(self, faDataType: int): - future = self.wrapper.startReq("requestFA") + future = ( + self.wrapper.response_bus.filter(lambda name, _: name == "requestFA") + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) self.client.requestFA(faDataType) try: - await asyncio.wait_for(future, 4) - return future.result() + result = await asyncio.wait_for(future, 4) + return result except asyncio.TimeoutError: self._logger.error("requestFAAsync: Timeout") diff --git a/ib_async/objects.py b/ib_async/objects.py index 6ccecb3b..91939615 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -629,6 +629,15 @@ class Position: avgCost: float +@dataclass(slots=True, frozen=True) +class PositionMulti: + account: str + contract: Contract + position: float + avgCost: float + modelCode: str + + @dataclass(slots=True) class Fill: contract: Contract diff --git a/ib_async/protobuf_converters/account_converters.py b/ib_async/protobuf_converters/account_converters.py index b63eb8fe..6cb036a5 100644 --- a/ib_async/protobuf_converters/account_converters.py +++ b/ib_async/protobuf_converters/account_converters.py @@ -2,9 +2,16 @@ Account data protobuf converters. """ -from ib_async.util import isValidIntValue +from ib_async.util import UNSET_DOUBLE, isValidIntValue -from ..objects import AccountValue, PortfolioItem, Position +from ..objects import ( + AccountValue, + FamilyCode, + PortfolioItem, + Position, + PositionMulti, + SoftDollarTier, +) from ..protobuf.AccountDataRequest_pb2 import ( AccountDataRequest as AccountDataRequestProto, ) @@ -19,13 +26,30 @@ from ..protobuf.CancelAccountUpdatesMulti_pb2 import ( CancelAccountUpdatesMulti as CancelAccountUpdatesMultiProto, ) +from ..protobuf.CancelPositionsMulti_pb2 import ( + CancelPositionsMulti as CancelPositionsMultiProto, +) +from ..protobuf.FamilyCode_pb2 import FamilyCode as FamilyCodeProto +from ..protobuf.FamilyCodes_pb2 import FamilyCodes as FamilyCodesProto +from ..protobuf.FamilyCodesRequest_pb2 import ( + FamilyCodesRequest as FamilyCodesRequestProto, +) from ..protobuf.FAReplace_pb2 import FAReplace as FAReplaceProto from ..protobuf.FARequest_pb2 import FARequest as FARequestProto from ..protobuf.IdsRequest_pb2 import IdsRequest as IdsRequestProto from ..protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto from ..protobuf.Position_pb2 import Position as PositionProto +from ..protobuf.PositionMulti_pb2 import PositionMulti as PositionMultiProto +from ..protobuf.PositionsMultiRequest_pb2 import ( + PositionsMultiRequest as PositionsMultiRequestProto, +) from ..protobuf.ReceiveFA_pb2 import ReceiveFA as ReceiveFAProto from ..protobuf.ReplaceFAEnd_pb2 import ReplaceFAEnd as ReplaceFAEndProto +from ..protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto +from ..protobuf.SoftDollarTiersRequest_pb2 import ( + SoftDollarTiersRequest as SoftDollarTiersRequestProto, +) +from .base_converters import ClientException from .contract_converters import createContract @@ -204,14 +228,116 @@ def createFAReplaceProto(reqId: int, faDataType: int, xml: str) -> FAReplaceProt def createFAmsg(msg: ReceiveFAProto) -> tuple[int, str]: - faDataType = ( - msg.faDataType if msg.HasField("faDataType") else 0 - ) + faDataType = msg.faDataType if msg.HasField("faDataType") else 0 xml = msg.xml if msg.HasField("xml") else "" return faDataType, xml + def createReplaceFAEnd(msg: ReplaceFAEndProto) -> str: - text = msg.text if msg.HasField('text') else "" + text = msg.text if msg.HasField("text") else "" return text - \ No newline at end of file + + +def createPositionsMultiRequestProto( + reqId: int, account: str, modelCode: str +) -> PositionsMultiRequestProto: + positionsMultiRequestProto = PositionsMultiRequestProto() + if isValidIntValue(reqId): + positionsMultiRequestProto.reqId = reqId + if account: + positionsMultiRequestProto.account = account + if modelCode: + positionsMultiRequestProto.modelCode = modelCode + return positionsMultiRequestProto + + +def createCancelPositionsMultiRequestProto(reqId: int) -> CancelPositionsMultiProto: + cancelPositionsMultiProto = CancelPositionsMultiProto() + if isValidIntValue(reqId): + cancelPositionsMultiProto.reqId = reqId + return cancelPositionsMultiProto + + +def createPositionMulti(positionMultiProto: PositionMultiProto) -> PositionMulti: + account = ( + positionMultiProto.account if positionMultiProto.HasField("account") else "" + ) + modelCode = ( + positionMultiProto.modelCode if positionMultiProto.HasField("modelCode") else "" + ) + + # decode contract fields + if not positionMultiProto.HasField("contract"): + raise ClientException(999, "Invalid postion contract.", "") + contract = createContract(positionMultiProto.contract) + + position = ( + float(positionMultiProto.position) + if positionMultiProto.HasField("position") + else UNSET_DOUBLE + ) + avgCost = ( + positionMultiProto.avgCost if positionMultiProto.HasField("avgCost") else 0 + ) + + return PositionMulti(account, contract, position, avgCost, modelCode) + + +def createFamilyCodesRequestProto() -> FamilyCodesRequestProto: + familyCodesRequestProto = FamilyCodesRequestProto() + return familyCodesRequestProto + + +def createFamilyCode(familyCodeProto: FamilyCodeProto) -> FamilyCode: + if familyCodeProto and familyCodeProto.HasField("accountId"): + accountID = familyCodeProto.accountId + if familyCodeProto and familyCodeProto.HasField("familyCode"): + familyCodeStr = familyCodeProto.familyCode + + return FamilyCode(accountID, familyCodeStr) + + +def createFamilyCodes(familyCodesProto: FamilyCodesProto) -> list[FamilyCode]: + familyCodes = [] + if familyCodesProto.familyCodes: + for familCodeProto in familyCodesProto.familyCodes: + familyCode = createFamilyCode(familCodeProto) + familyCodes.append(familyCode) + return familyCodes + + +def createSoftDollarTiersRequestProto(reqId: int) -> SoftDollarTiersRequestProto: + softDollarTiersRequestProto = SoftDollarTiersRequestProto() + if isValidIntValue(reqId): + softDollarTiersRequestProto.reqId = reqId + return softDollarTiersRequestProto + + +def createSoftDollarTier(softDollarTierProto: SoftDollarTierProto) -> SoftDollarTier: + name = "" + value = "" + displayName = "" + softDollarTier = None + if softDollarTierProto is not None: + if softDollarTierProto.HasField("name"): + name = softDollarTierProto.name + if softDollarTierProto.HasField("value"): + value = softDollarTierProto.value + if softDollarTierProto.HasField("displayName"): + displayName = softDollarTierProto.displayName + softDollarTier = SoftDollarTier(name, value, displayName) + + return softDollarTier + + +def createSoftDollarTiers( + softDollarTiersProto: SoftDollarTiersRequestProto, +) -> list[SoftDollarTier]: + tiers = [] + if softDollarTiersProto.softDollarTiers: + for softDollarTierProto in softDollarTiersProto.softDollarTiers: + tier = createSoftDollarTier(softDollarTierProto) + if tier is not None: + tiers.append(tier) + return tiers diff --git a/ib_async/util.py b/ib_async/util.py index f1d5eb77..41b2879d 100644 --- a/ib_async/util.py +++ b/ib_async/util.py @@ -2,7 +2,6 @@ import asyncio import datetime as dt -import functools import logging import math import signal @@ -348,10 +347,7 @@ def run(*awaitables: Awaitable, timeout: float | None = None): loop.run_forever() result = None - if sys.version_info >= (3, 7): - all_tasks = asyncio.all_tasks(loop) # type: ignore - else: - all_tasks = asyncio.Task.all_tasks() # type: ignore + all_tasks = asyncio.all_tasks(loop) # type: ignore if all_tasks: # cancel pending tasks diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 8b3ab0c8..7ebf5c9e 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -15,7 +15,6 @@ Generic, TypeAlias, TypeVar, - cast, ) from weakref import WeakKeyDictionary @@ -52,6 +51,7 @@ PnLSingle, PortfolioItem, Position, + PositionMulti, PriceIncrement, RealTimeBar, RealTimeBarList, @@ -323,6 +323,9 @@ class Wrapper: positions: dict[str, dict[int, Position]] = field(init=False) """ account -> conId -> Position """ + + positionsMulti: dict[str, dict[int, PositionMulti]] = field(init=False) + """ account -> conId -> PositionMulti """ trades: BiDict[OrderKeyType, Trade] = field(init=False) """ @@ -398,6 +401,7 @@ def reset(self): self.acctSummary = {} self.portfolio = defaultdict(dict) self.positions = defaultdict(dict) + self.positionsMulti = defaultdict(dict) self.trades = BiDict[OrderKeyType, Trade]() self.subscriptions = BiDict[int, SubscriptionType]() self._isReady = False @@ -573,10 +577,13 @@ def updatePortfolio(self, portfolioItem: PortfolioItem): self.ib.updatePortfolioEvent.emit(portfolioItem) def position(self, position: Position): - account_positions = self.positions[position.account] + # get/create dict for account + account_positions = self.positions[position.account] if position.position == 0: + # remove position account_positions.pop(position.contract.conId, None) else: + # add position account_positions[position.contract.conId] = position if self._isReady: @@ -589,16 +596,21 @@ def positionEnd(self): def positionMulti( self, reqId: int, - account: str, - modelCode: str, - contract: Contract, - pos: float, - avgCost: float, + postionMulti: PositionMulti ): - pass + account_positionsMulti = self.positionsMulti[postionMulti.account] + if postionMulti.position == 0: + account_positionsMulti.pop(postionMulti.contract.conId, None) + else: + account_positionsMulti[postionMulti.contract.conId] = postionMulti + + if self._isReady: + self.ib.positionEvent.emit(postionMulti) + self.response_bus.emit(reqId, postionMulti) def positionMultiEnd(self, reqId: int): - pass + self._endReq(reqId) + def pnl( self, reqId: int, dailyPnL: float, unrealizedPnL: float, realizedPnL: float @@ -1091,10 +1103,10 @@ def userInfo(self, reqId: int, whiteBrandingId: str): self.response_bus.emit(reqId, whiteBrandingId) def softDollarTiers(self, reqId: int, tiers: list[SoftDollarTier]): - pass + self._logger.info("reqId: %s, softDollarTiers: %s", reqId,tiers) def familyCodes(self, familyCodes: list[FamilyCode]): - pass + self._logger.info("familyCodes: %s", familyCodes) def error( self, From c71c9e31ba33743b922dd4270ed9803bd9026375 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:10:32 +0100 Subject: [PATCH 16/48] Enhance protobuf handling and event management - Updated protobuf message handling in decoder.py to support bond contract details. - Refactored createDeltaNeutralContract in contract_converters.py for improved clarity. - Added positionMultiEvent to ib.py for enhanced event tracking. - Adjusted wrapper.py to emit positionMultiEvent. - Improved error handling in market_data_converters.py for tick data. --- ib_async/contract.py | 4 +- ib_async/decoder.py | 93 +++---------------- ib_async/ib.py | 2 + .../contract_converters.py | 21 +++-- .../market_data_converters.py | 36 ++++--- ib_async/util.py | 3 +- ib_async/wrapper.py | 3 +- 7 files changed, 54 insertions(+), 108 deletions(-) diff --git a/ib_async/contract.py b/ib_async/contract.py index d3901cae..5895dc57 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -25,13 +25,13 @@ class FundDistributionPolicyIndicator(Enum): IncomeFund = ("Y", "Income Fund") -@dataclass +@dataclass(slots=True, frozen=True) class IneligibilityReason: id_: str = field(default_factory=str) description: str = field(default_factory=str) -@dataclass(slots=True) +@dataclass(slots=True,frozen=True) class DeltaNeutralContract: conId: int = 0 delta: float = 0.0 diff --git a/ib_async/decoder.py b/ib_async/decoder.py index f7e10141..a2ffc7e9 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -1,4 +1,4 @@ -"""Deserialize and dispatch messages.""" +"""Deserialize and dispatch protobuf messages.""" import logging @@ -180,6 +180,10 @@ class Decoder: MessageId.IN.NEXT_VALID_ID: (NextValidIdProto, "nextValidIdProto"), # Handles incoming contract details for reqContractDetails MessageId.IN.CONTRACT_DATA: (ContractDataProto, "contractDetailsProto"), + MessageId.IN.BOND_CONTRACT_DATA: ( + ContractDataProto, + "bondContractDetailsProto", + ), # Signals the end of a contract details stream MessageId.IN.CONTRACT_DATA_END: ( ContractDataEndProto, @@ -688,87 +692,13 @@ def softDollarTiersProto(self, msg: SoftDollarTiersProto): def familyCodesProto(self, msg: FamilyCodesProto): self.wrapper.familyCodes(msg) - ##################### legacy methods ########################################## - def bondContractDetails(self, fields): - cd = ContractDetails() - cd.contract = c = Contract() - if self.serverVersion < 164: - fields.pop(0) - - ( - _, - reqId, - c.symbol, - c.secType, - cd.cusip, - cd.coupon, - lastTimes, - cd.issueDate, - cd.ratings, - cd.bondType, - cd.couponType, - cd.convertible, - cd.callable, - cd.putable, - cd.descAppend, - c.exchange, - c.currency, - cd.marketName, - c.tradingClass, - c.conId, - cd.minTick, - *fields, - ) = fields - - if self.serverVersion < 164: - fields.pop(0) # obsolete mdSizeMultiplier - - ( - cd.orderTypes, - cd.validExchanges, - cd.nextOptionDate, - cd.nextOptionType, - cd.nextOptionPartial, - cd.notes, - cd.longName, - cd.evRule, - cd.evMultiplier, - numSecIds, - *fields, - ) = fields - - numSecIds = int(numSecIds) - if numSecIds > 0: - cd.secIdList = [] - for _ in range(numSecIds): - tag, value, *fields = fields - cd.secIdList += [TagValue(tag, value)] - - cd.aggGroup, cd.marketRuleIds, *fields = fields - if self.serverVersion >= 164: - ( - cd.minSize, - cd.sizeIncrement, - cd.suggestedSizeIncrement, - # cd.minCashQtySize, - *fields, - ) = fields - - times = lastTimes.split("-" if "-" in lastTimes else None) - - if len(times) > 0: - cd.maturity = times[0] - - if len(times) > 1: - cd.lastTradeTime = times[1] - - if len(times) > 2: - cd.timeZoneId = times[2] - - self.parse(cd) - self.parse(c) - self.wrapper.bondContractDetails(int(reqId), cd) + def bondContractDetailsProto(self, msg:ContractDataProto): + reqId = msg.reqId + cd = createContractDetails(msg) + self.wrapper.bondContractDetails(reqId, cd) + + ##################### legacy methods ########################################## def deltaNeutralValidation(self, fields): _, _, reqId, conId, delta, price = fields @@ -778,7 +708,6 @@ def deltaNeutralValidation(self, fields): DeltaNeutralContract(int(conId), float(delta or 0), float(price or 0)), ) - def mktDepthExchanges(self, fields): _, n, *fields = fields get = iter(fields).__next__ diff --git a/ib_async/ib.py b/ib_async/ib.py index 41f58934..dd5fb442 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -269,6 +269,7 @@ class IB: "commissionReportEvent", "updatePortfolioEvent", "positionEvent", + "positionMultiEvent", "accountValueEvent", "accountSummaryEvent", "pnlEvent", @@ -311,6 +312,7 @@ def _createEvents(self): self.commissionReportEvent = Event("commissionReportEvent") self.updatePortfolioEvent = Event("updatePortfolioEvent") self.positionEvent = Event("positionEvent") + self.positionMultiEvent = Event("positionMultiEvent") self.accountValueEvent = Event("accountValueEvent") self.accountSummaryEvent = Event("accountSummaryEvent") self.pnlEvent = Event("pnlEvent") diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py index 2d18f918..cbe411cb 100644 --- a/ib_async/protobuf_converters/contract_converters.py +++ b/ib_async/protobuf_converters/contract_converters.py @@ -181,6 +181,7 @@ def createComboLegs(contractProto: ContractProto) -> list[ComboLeg]: comboLeg.designatedLocation = comboLegProto.designatedLocation if comboLegProto.HasField("exemptCode"): comboLeg.exemptCode = comboLegProto.exemptCode + comboLegs.append(comboLeg) return comboLegs @@ -189,20 +190,21 @@ def createComboLegs(contractProto: ContractProto) -> list[ComboLeg]: def createDeltaNeutralContract( contractProto: ContractProto, ) -> DeltaNeutralContract | None: - deltaNeutralContract = None + dn = None if contractProto.HasField("deltaNeutralContract"): deltaNeutralContractProto = DeltaNeutralContractProto() deltaNeutralContractProto.CopyFrom(contractProto.deltaNeutralContract) if deltaNeutralContractProto is not None: - deltaNeutralContract = DeltaNeutralContract() if deltaNeutralContractProto.HasField("conId"): - deltaNeutralContract.conId = deltaNeutralContractProto.conId + conId = deltaNeutralContractProto.conId if deltaNeutralContractProto.HasField("delta"): - deltaNeutralContract.delta = deltaNeutralContractProto.delta + delta = deltaNeutralContractProto.delta if deltaNeutralContractProto.HasField("price"): - deltaNeutralContract.price = deltaNeutralContractProto.price + price = deltaNeutralContractProto.price + if conId is not None and delta is not None and price is not None: + dn = DeltaNeutralContract(conId, delta, price) - return deltaNeutralContract + return dn def createIneligibilityReasonList( @@ -212,12 +214,11 @@ def createIneligibilityReasonList( ineligibilityReasonProtoList = contractDetailsProto.ineligibilityReasonList if ineligibilityReasonProtoList: for ineligibilityReasonProto in ineligibilityReasonProtoList: - ineligibilityReason = IneligibilityReason() if ineligibilityReasonProto.HasField("id"): - ineligibilityReason.id_ = ineligibilityReasonProto.id + id_ = ineligibilityReasonProto.id if ineligibilityReasonProto.HasField("description"): - ineligibilityReason.description = ineligibilityReasonProto.description - ineligibilityReasonList.append(ineligibilityReason) + description = ineligibilityReasonProto.description + ineligibilityReasonList.append(IneligibilityReason(id_, description)) return ineligibilityReasonList diff --git a/ib_async/protobuf_converters/market_data_converters.py b/ib_async/protobuf_converters/market_data_converters.py index 25414cf7..253e469e 100644 --- a/ib_async/protobuf_converters/market_data_converters.py +++ b/ib_async/protobuf_converters/market_data_converters.py @@ -2,7 +2,9 @@ Market data protobuf converters """ -from typing import Any, Callable, TypeAlias +from collections.abc import Callable +from math import nan +from typing import Any, TypeAlias from ..objects import ( Contract, @@ -128,16 +130,18 @@ def createTickParams(msg: TickReqParamsProto) -> TickParams: def createTickPriceData(msg: TickPriceProto) -> TickPriceData: """Create a TickPriceData object from a TickPriceProto message.""" - if msg.HasField("reqId"): - reqId = msg.reqId - if msg.HasField("tickType") and msg.tickType in TickType: - tickType = TickType(msg.tickType) - if msg.HasField("price"): - price = float( - msg.price, - ) - if msg.HasField("size"): - size = float(msg.size) + reqId = msg.reqId if msg.HasField("reqId") else NO_VALID_ID + + tickType = ( + TickType(msg.tickType) + if msg.HasField("tickType") and msg.tickType in TickType + else TickType.NOT_SET + ) + + price = float(msg.price) if msg.HasField("price") else nan + size = float(msg.size) if msg.HasField("size") else nan + + attribs = TickAttrib() if msg.HasField("attrMask"): canAutoExecute = msg.attrMask & 1 != 0 pastLimit = msg.attrMask & 2 != 0 @@ -155,6 +159,9 @@ def createTickPriceData(msg: TickPriceProto) -> TickPriceData: def createTickSizeData(msg: TickSizeProto) -> TickSizeData: """Create a TickSizeData object from a TickSizeProto message.""" + reqId = NO_VALID_ID + tickType = TickType.NOT_SET + size = nan if msg.HasField("reqId"): reqId = msg.reqId if msg.HasField("tickType") and msg.tickType in TickType: @@ -167,6 +174,10 @@ def createTickSizeData(msg: TickSizeProto) -> TickSizeData: def createTickStringData(msg: TickStringProto) -> TickStringData: """Create a TickStringData object from a TickStringProto message.""" + reqId = NO_VALID_ID + tickType = TickType.NOT_SET + value = "" + if msg.HasField("reqId"): reqId = msg.reqId if msg.HasField("tickType") and msg.tickType in TickType: @@ -179,6 +190,9 @@ def createTickStringData(msg: TickStringProto) -> TickStringData: def createTickGenericData(msg: TickGenericProto) -> TickGenericData: """Create a TickGenericData object from a TickGenericProto message.""" + reqId = NO_VALID_ID + tickType = TickType.NOT_SET + value = nan if msg.HasField("reqId"): reqId = msg.reqId if msg.HasField("tickType") and msg.tickType in TickType: diff --git a/ib_async/util.py b/ib_async/util.py index 41b2879d..da0deff3 100644 --- a/ib_async/util.py +++ b/ib_async/util.py @@ -7,13 +7,12 @@ import signal import sys import time -from collections.abc import AsyncIterator, Awaitable, Iterator +from collections.abc import AsyncIterator, Awaitable, Callable, Iterator from dataclasses import fields, is_dataclass from decimal import ROUND_HALF_UP, Decimal from functools import wraps from typing import ( Any, - Callable, Final, TypeAlias, ) diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 7ebf5c9e..83cee220 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -605,7 +605,7 @@ def positionMulti( account_positionsMulti[postionMulti.contract.conId] = postionMulti if self._isReady: - self.ib.positionEvent.emit(postionMulti) + self.ib.positionMultiEvent.emit(postionMulti) self.response_bus.emit(reqId, postionMulti) def positionMultiEnd(self, reqId: int): @@ -681,6 +681,7 @@ def openOrder(self, trade: Trade, orderState: OrderState): final_trade = existing_trade or trade if self._isReady: self.ib.openOrderEvent.emit(final_trade) + if final_trade.order.orderId > 0: self.ib.client.updateReqId(final_trade.order.orderId + 1) self.response_bus.emit("openOrders", final_trade) From 04795ce3321f6394314834df9268b3fd26dc0b3c Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Wed, 31 Dec 2025 10:44:33 +0100 Subject: [PATCH 17/48] refactor: streamline logging on warnings. --- ib_async/wrapper.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 83cee220..4c5cba57 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -1184,6 +1184,7 @@ def error( # Record warnings into the trade object, but unlike the _error_ case, # DO NOT delete the trade object because the order is STILL LIVE at the # broker. + self._logger.warning(msg) if trade: status = trade.orderStatus.status = OrderStatus.ValidationError logEntry = TradeLogEntry(self.lastTime, status, msg, errorCode) @@ -1201,9 +1202,7 @@ def error( self.response_bus.emit(reqId, error) else: # a None will be interpreted as an empty result - self._logger.error("is request %s, %s", reqId, msg) self.response_bus.emit(reqId, None) - self._logger.info(msg) else: self._logger.error(msg) if isRequest: From 0d823ed64f79e7c5f5d88807421a862bd620265f Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:39:58 +0100 Subject: [PATCH 18/48] refactor: enhance protobuf converters with IBDefaults handling and safety checks. --- ib_async/ib.py | 2 + ib_async/objects.py | 11 ++- .../protobuf_converters/account_converters.py | 11 ++- .../protobuf_converters/base_converters.py | 16 +++- .../contract_converters.py | 54 ++++++++---- .../historical_data_converters.py | 87 ++++++++++++++----- .../market_data_converters.py | 61 ++++++------- .../protobuf_converters/news_converters.py | 1 + 8 files changed, 168 insertions(+), 75 deletions(-) diff --git a/ib_async/ib.py b/ib_async/ib.py index dd5fb442..6880db3a 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -58,6 +58,7 @@ StopOrder, Trade, ) +from .protobuf_converters.base_converters import set_ib_defaults from .ticker import Ticker from .wrapper import Wrapper @@ -2144,6 +2145,7 @@ async def connectAsync( self.wrapper._isReady = False clientId = int(clientId) self.wrapper.clientId = clientId + set_ib_defaults(self.wrapper.defaults) timeout = timeout or None try: # establish API connection diff --git a/ib_async/objects.py b/ib_async/objects.py index 91939615..345e15b2 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -636,8 +636,8 @@ class PositionMulti: position: float avgCost: float modelCode: str - - + + @dataclass(slots=True) class Fill: contract: Contract @@ -868,6 +868,13 @@ class IBDefaults: # on orders or data processing) timezone: tzinfo = timezone.utc + def __repr__(self): + clsName = self.__class__.__name__ + kwargs = ", ".join( + f"{k}={getattr(self, k)!r}" for k in self.__dir__() if not k.startswith("_") + ) + return f"{clsName}({kwargs})" + @dataclass(slots=True, frozen=True) class EfpData: diff --git a/ib_async/protobuf_converters/account_converters.py b/ib_async/protobuf_converters/account_converters.py index 6cb036a5..a448eb44 100644 --- a/ib_async/protobuf_converters/account_converters.py +++ b/ib_async/protobuf_converters/account_converters.py @@ -290,10 +290,13 @@ def createFamilyCodesRequestProto() -> FamilyCodesRequestProto: def createFamilyCode(familyCodeProto: FamilyCodeProto) -> FamilyCode: - if familyCodeProto and familyCodeProto.HasField("accountId"): - accountID = familyCodeProto.accountId - if familyCodeProto and familyCodeProto.HasField("familyCode"): - familyCodeStr = familyCodeProto.familyCode + accountID = "" + familyCodeStr = "" + if familyCodeProto: + if familyCodeProto.HasField("accountId"): + accountID = familyCodeProto.accountId + if familyCodeProto.HasField("familyCode"): + familyCodeStr = familyCodeProto.familyCode return FamilyCode(accountID, familyCodeStr) diff --git a/ib_async/protobuf_converters/base_converters.py b/ib_async/protobuf_converters/base_converters.py index acd3b018..495c601b 100644 --- a/ib_async/protobuf_converters/base_converters.py +++ b/ib_async/protobuf_converters/base_converters.py @@ -1,11 +1,23 @@ """Base converters for protobuf messages.""" from ..contract import TagValue +from ..objects import IBDefaults from ..protobuf.SetServerLogLevelRequest_pb2 import ( SetServerLogLevelRequest as SetServerLogLevelRequestProto, ) from ..util import isValidIntValue +# This module-level variable will be updated by the IB instance on connection. +ib_defaults = IBDefaults() + + +def set_ib_defaults(new_defaults: IBDefaults): + """ + Updates the shared defaults instance used by the protobuf converters. + """ + global ib_defaults + ib_defaults = new_defaults + class ClientException(Exception): def __init__(self, code, message, text): @@ -20,7 +32,9 @@ def fillTagValueList(tagValueList: list[TagValue], orderProtoMap: dict): for tagValue in tagValueList: orderProtoMap[tagValue.tag] = tagValue.value + def createSetServerLogLevelRequestProto(logLevel: int) -> SetServerLogLevelRequestProto: setServerLogLevelRequestProto = SetServerLogLevelRequestProto() - if isValidIntValue(logLevel): setServerLogLevelRequestProto.logLevel = logLevel + if isValidIntValue(logLevel): + setServerLogLevelRequestProto.logLevel = logLevel return setServerLogLevelRequestProto diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py index cbe411cb..df1a7221 100644 --- a/ib_async/protobuf_converters/contract_converters.py +++ b/ib_async/protobuf_converters/contract_converters.py @@ -87,18 +87,34 @@ def createOptionChain(secDefOptParameterProto: SecDefOptParameterProto) -> Optio Returns: OptionChain: The created OptionChain object. """ - if secDefOptParameterProto.exchange is not None: - exchange = secDefOptParameterProto.exchange - if secDefOptParameterProto.underlyingConId is not None: - underlyingConId = secDefOptParameterProto.underlyingConId - if secDefOptParameterProto.tradingClass is not None: - tradingClass = secDefOptParameterProto.tradingClass - if secDefOptParameterProto.multiplier is not None: - multiplier = secDefOptParameterProto.multiplier - if secDefOptParameterProto.expirations is not None: - expirations = list(secDefOptParameterProto.expirations) - if secDefOptParameterProto.strikes is not None: - strikes = list(secDefOptParameterProto.strikes) + exchange = ( + secDefOptParameterProto.exchange + if secDefOptParameterProto.HasField("exchange") + else "" + ) + underlyingConId = ( + secDefOptParameterProto.underlyingConId + if secDefOptParameterProto.HasField("underlyingConId") + else 0 + ) + tradingClass = ( + secDefOptParameterProto.tradingClass + if secDefOptParameterProto.HasField("tradingClass") + else "" + ) + multiplier = ( + secDefOptParameterProto.multiplier + if secDefOptParameterProto.HasField("multiplier") + else "" + ) + expirations = ( + list(secDefOptParameterProto.expirations) + if secDefOptParameterProto.expirations + else [] + ) + strikes = ( + list(secDefOptParameterProto.strikes) if secDefOptParameterProto.strikes else [] + ) optionChain = OptionChain( exchange=exchange, underlyingConId=underlyingConId, @@ -214,10 +230,16 @@ def createIneligibilityReasonList( ineligibilityReasonProtoList = contractDetailsProto.ineligibilityReasonList if ineligibilityReasonProtoList: for ineligibilityReasonProto in ineligibilityReasonProtoList: - if ineligibilityReasonProto.HasField("id"): - id_ = ineligibilityReasonProto.id - if ineligibilityReasonProto.HasField("description"): - description = ineligibilityReasonProto.description + id_ = ( + ineligibilityReasonProto.id + if ineligibilityReasonProto.HasField("id") + else "" + ) + description = ( + ineligibilityReasonProto.description + if ineligibilityReasonProto.HasField("description") + else "" + ) ineligibilityReasonList.append(IneligibilityReason(id_, description)) return ineligibilityReasonList diff --git a/ib_async/protobuf_converters/historical_data_converters.py b/ib_async/protobuf_converters/historical_data_converters.py index f6bef8b4..f53f9655 100644 --- a/ib_async/protobuf_converters/historical_data_converters.py +++ b/ib_async/protobuf_converters/historical_data_converters.py @@ -71,7 +71,7 @@ parseIBDatetime, parseIBTimeStamp, ) -from .base_converters import ClientException, fillTagValueList +from .base_converters import ClientException, fillTagValueList, ib_defaults from .contract_converters import createContractProto @@ -173,24 +173,55 @@ def createBarDataList( def createBarData(historicalDataBarProto: HistoricalDataBarProto) -> BarData: - if historicalDataBarProto.HasField("date"): - date = parseIBDatetime(historicalDataBarProto.date) - if historicalDataBarProto.HasField("open"): - open_ = historicalDataBarProto.open - if historicalDataBarProto.HasField("high"): - high = historicalDataBarProto.high - if historicalDataBarProto.HasField("low"): - low = historicalDataBarProto.low - if historicalDataBarProto.HasField("close"): - close = historicalDataBarProto.close - if historicalDataBarProto.HasField("volume"): - volume = float(historicalDataBarProto.volume) - if historicalDataBarProto.HasField("WAP"): - average = float(historicalDataBarProto.WAP) - if historicalDataBarProto.HasField("barCount"): - barCount = historicalDataBarProto.barCount - bar = BarData(date, open_, high, low, close, volume, average, barCount) - return bar + """ + Safely creates a frozen BarData object from a protobuf message, + using configurable defaults for missing fields. + """ + # For date, EPOCH is the established sentinel value. + date = ( + parseIBDatetime(historicalDataBarProto.date) + if historicalDataBarProto.HasField("date") + else EPOCH + ) + # Use the configurable 'unset' value (NaN) for optional float price fields. + open_ = ( + historicalDataBarProto.open + if historicalDataBarProto.HasField("open") + else ib_defaults.unset + ) + high = ( + historicalDataBarProto.high + if historicalDataBarProto.HasField("high") + else ib_defaults.unset + ) + low = ( + historicalDataBarProto.low + if historicalDataBarProto.HasField("low") + else ib_defaults.unset + ) + close = ( + historicalDataBarProto.close + if historicalDataBarProto.HasField("close") + else ib_defaults.unset + ) + # For volume and count, use the type-safe defaults from the dataclass definition (0). + volume = ( + float(historicalDataBarProto.volume) + if historicalDataBarProto.HasField("volume") + else 0.0 # Corresponds to BarData(volume=0.0) + ) + average = ( + float(historicalDataBarProto.WAP) + if historicalDataBarProto.HasField("WAP") + else ib_defaults.unset + ) + barCount = ( + historicalDataBarProto.barCount + if historicalDataBarProto.HasField("barCount") + else 0 # Corresponds to BarData(barCount=0) + ) + + return BarData(date, open_, high, low, close, volume, average, barCount) def createHistoricalTicksRequestProto( @@ -252,8 +283,16 @@ def createHistoricalTickBidAsk( priceBid = historicalTickBidAskProto.priceBid priceAsk = historicalTickBidAskProto.priceAsk - sizeBid = float(historicalTickBidAskProto.sizeBid) - sizeAsk = float(historicalTickBidAskProto.sizeAsk) + sizeBid = ( + float(historicalTickBidAskProto.sizeBid) + if historicalTickBidAskProto.sizeBid + else IBDefaults.emptySize + ) + sizeAsk = ( + float(historicalTickBidAskProto.sizeAsk) + if historicalTickBidAskProto.sizeAsk + else IBDefaults.emptySize + ) historicalTickBidAsk = HistoricalTickBidAsk( time, tickAttribBidAsk, priceBid, priceAsk, sizeBid, sizeAsk @@ -272,7 +311,11 @@ def createHistoricalTickLast( tickAttribLast = TickAttribLast(pastLimit, unreported) price = historicalTickLastProto.price - size = float(historicalTickLastProto.size) + size = ( + float(historicalTickLastProto.size) + if historicalTickLastProto.size + else IBDefaults.emptySize + ) exchange = historicalTickLastProto.exchange specialConditions = historicalTickLastProto.specialConditions diff --git a/ib_async/protobuf_converters/market_data_converters.py b/ib_async/protobuf_converters/market_data_converters.py index 253e469e..76708cce 100644 --- a/ib_async/protobuf_converters/market_data_converters.py +++ b/ib_async/protobuf_converters/market_data_converters.py @@ -54,6 +54,7 @@ UNSET_INTEGER, isValidIntValue, ) +from .base_converters import ib_defaults from .contract_converters import createContractProto from .historical_data_converters import fillTagValueList @@ -138,8 +139,8 @@ def createTickPriceData(msg: TickPriceProto) -> TickPriceData: else TickType.NOT_SET ) - price = float(msg.price) if msg.HasField("price") else nan - size = float(msg.size) if msg.HasField("size") else nan + price = float(msg.price) if msg.HasField("price") else ib_defaults.emptyPrice + size = float(msg.size) if msg.HasField("size") else ib_defaults.emptySize attribs = TickAttrib() if msg.HasField("attrMask"): @@ -161,7 +162,7 @@ def createTickSizeData(msg: TickSizeProto) -> TickSizeData: """Create a TickSizeData object from a TickSizeProto message.""" reqId = NO_VALID_ID tickType = TickType.NOT_SET - size = nan + size = ib_defaults.emptySize if msg.HasField("reqId"): reqId = msg.reqId if msg.HasField("tickType") and msg.tickType in TickType: @@ -176,8 +177,8 @@ def createTickStringData(msg: TickStringProto) -> TickStringData: """Create a TickStringData object from a TickStringProto message.""" reqId = NO_VALID_ID tickType = TickType.NOT_SET - value = "" - + value = ib_defaults.unset + if msg.HasField("reqId"): reqId = msg.reqId if msg.HasField("tickType") and msg.tickType in TickType: @@ -192,7 +193,7 @@ def createTickGenericData(msg: TickGenericProto) -> TickGenericData: """Create a TickGenericData object from a TickGenericProto message.""" reqId = NO_VALID_ID tickType = TickType.NOT_SET - value = nan + value = ib_defaults.unset if msg.HasField("reqId"): reqId = msg.reqId if msg.HasField("tickType") and msg.tickType in TickType: @@ -232,41 +233,41 @@ def createTickOptionComputation(msg: TickOptionComputationProto) -> TickComputat tickType = TickType(msg.tickType) if msg.HasField("tickType") else TickType.NOT_SET tickAttrib = msg.tickAttrib if msg.HasField("tickAttrib") else UNSET_INTEGER - impliedVol = msg.impliedVol if msg.HasField("impliedVol") else None + impliedVol = msg.impliedVol if msg.HasField("impliedVol") else ib_defaults.unset if impliedVol and impliedVol < 0: # -1 is the "not computed" indicator - impliedVol = None - delta = msg.delta if msg.HasField("delta") else None + impliedVol = ib_defaults.unset + delta = msg.delta if msg.HasField("delta") else ib_defaults.unset if delta == -2: # -2 is the "not computed" indicator - delta = None - optPrice = msg.optPrice if msg.HasField("optPrice") else None + delta = ib_defaults.unset + optPrice = msg.optPrice if msg.HasField("optPrice") else ib_defaults.unset if optPrice == -1: # -1 is the "not computed" indicator - optPrice = None - pvDividend = msg.pvDividend if msg.HasField("pvDividend") else None + optPrice = ib_defaults.unset + pvDividend = msg.pvDividend if msg.HasField("pvDividend") else ib_defaults.unset if pvDividend == -1: # -1 is the "not computed" indicator - pvDividend = None - gamma = msg.gamma if msg.HasField("gamma") else None + pvDividend = ib_defaults.unset + gamma = msg.gamma if msg.HasField("gamma") else ib_defaults.unset if gamma == -2: # -2 is the "not yet computed" indicator - gamma = None - vega = msg.vega if msg.HasField("vega") else None + gamma = ib_defaults.unset + vega = msg.vega if msg.HasField("vega") else ib_defaults.unset if vega == -2: # -2 is the "not yet computed" indicator - vega = None - theta = msg.theta if msg.HasField("theta") else None + vega = ib_defaults.unset + theta = msg.theta if msg.HasField("theta") else ib_defaults.unset if theta == -2: # -2 is the "not yet computed" indicator - theta = None - undPrice = msg.undPrice if msg.HasField("undPrice") else None + theta = ib_defaults.unset + undPrice = msg.undPrice if msg.HasField("undPrice") else ib_defaults.unset if undPrice == -1: # -1 is the "not computed" indicator - undPrice = None + undPrice = ib_defaults.unset comp = OptionComputation( tickAttrib, - impliedVol if impliedVol != -1 else None, - delta if delta != -2 else None, - optPrice if optPrice != -1 else None, - pvDividend if pvDividend != -1 else None, - gamma if gamma != -2 else None, - vega if vega != -2 else vega, - theta if theta != -2 else theta, - undPrice if undPrice != -1 else None, + impliedVol if impliedVol != -1 else ib_defaults.unset, + delta if delta != -2 else ib_defaults.unset, + optPrice if optPrice != -1 else ib_defaults.unset, + pvDividend if pvDividend != -1 else ib_defaults.unset, + gamma if gamma != -2 else ib_defaults.unset, + vega if vega != -2 else ib_defaults.unset, + theta if theta != -2 else ib_defaults.unset, + undPrice if undPrice != -1 else ib_defaults.unset, ) tick_comp = TickComputationData( diff --git a/ib_async/protobuf_converters/news_converters.py b/ib_async/protobuf_converters/news_converters.py index 75602ed3..aee83aab 100644 --- a/ib_async/protobuf_converters/news_converters.py +++ b/ib_async/protobuf_converters/news_converters.py @@ -2,6 +2,7 @@ from datetime import datetime from typing import cast + from ..contract import TagValue from ..objects import HistoricalNews, NewsArticle, NewsBulletin, NewsProvider, NewsTick from ..protobuf.CancelNewsBulletins_pb2 import ( From 269ef355d2c172e6da83c812e51436fa9c318b36 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:43:35 +0100 Subject: [PATCH 19/48] Refactor tests and add new test cases for various converters - Updated `conftest.py` to ignore type errors for mocked IB client methods. - Enhanced `test_account_converters.py` with additional test cases for new protobuf converters. - Created `test_base_converters.py` to cover base converter functionalities. - Modified `test_contract_converters.py` to include new contract-related tests and improved type handling. - Updated `test_contract_requests.py` to ensure proper mocking and type handling in contract requests. - Expanded `test_historical_data_converters.py` with new tests for historical tick data and related converters. - Enhanced `test_market_data_converters.py` with new tests for tick data handling. - Introduced `test_news_converters.py` to validate news-related protobuf converters. - Updated `test_subscription_converters.py` to improve test coverage for subscription-related converters. - Refactored `test_trade_converter.py` to streamline imports and enhance test coverage for trade-related functionalities. --- tests/conftest.py | 9 +- tests/test_account_converters.py | 181 +++++++++++++++++++++-- tests/test_base_converters.py | 101 +++++++++++++ tests/test_contract_converters.py | 38 ++--- tests/test_contract_requests.py | 75 ++++------ tests/test_historical_data_converters.py | 87 ++++++++++- tests/test_market_data_converters.py | 87 +++++++---- tests/test_news_converters.py | 167 +++++++++++++++++++++ tests/test_subscription_converters.py | 46 +++--- tests/test_trade_converter.py | 140 +++++++++--------- 10 files changed, 718 insertions(+), 213 deletions(-) create mode 100644 tests/test_base_converters.py create mode 100644 tests/test_news_converters.py diff --git a/tests/conftest.py b/tests/conftest.py index 676ff872..0c0cf275 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,4 @@ -import asyncio -from unittest.mock import Mock, patch +from unittest.mock import Mock import pytest @@ -26,7 +25,7 @@ async def ib(): def mock_ib(): """Fixture for a mocked IB instance.""" ib_instance = IB() - ib_instance.client.isConnected = Mock(return_value=True) - ib_instance.client.isReady = Mock(return_value=True) - ib_instance.client.serverVersion = Mock(return_value=201) + ib_instance.client.isConnected = Mock(return_value=True) # type: ignore + ib_instance.client.isReady = Mock(return_value=True) # type: ignore + ib_instance.client.serverVersion = Mock(return_value=201) # type: ignore return ib_instance diff --git a/tests/test_account_converters.py b/tests/test_account_converters.py index 650e0dd8..2cb7e0ae 100644 --- a/tests/test_account_converters.py +++ b/tests/test_account_converters.py @@ -1,36 +1,73 @@ -import pytest -from unittest.mock import Mock +"""Test for acount protobuf converters.""" -from ib_async.objects import AccountValue, Position, PortfolioItem -from ib_async.contract import Contract +from ib_async.objects import ( + AccountValue, + FamilyCode, + PortfolioItem, + Position, + PositionMulti, + SoftDollarTier, +) from ib_async.protobuf.AccountDataRequest_pb2 import ( AccountDataRequest as AccountDataRequestProto, ) -from ib_async.protobuf.AccountUpdatesMultiRequest_pb2 import ( - AccountUpdatesMultiRequest as AccountUpdatesMultiRequestProto, -) +from ib_async.protobuf.AccountSummary_pb2 import AccountSummary as AccountSummaryProto from ib_async.protobuf.AccountUpdateMulti_pb2 import ( AccountUpdateMulti as AccountUpdateMultiProto, ) -from ib_async.protobuf.AccountSummary_pb2 import AccountSummary as AccountSummaryProto +from ib_async.protobuf.AccountUpdatesMultiRequest_pb2 import ( + AccountUpdatesMultiRequest as AccountUpdatesMultiRequestProto, +) from ib_async.protobuf.AccountValue_pb2 import AccountValue as AccountValueProto from ib_async.protobuf.CancelAccountUpdatesMulti_pb2 import ( CancelAccountUpdatesMulti as CancelAccountUpdatesMultiProto, ) -from ib_async.protobuf.Position_pb2 import Position as PositionProto -from ib_async.protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto +from ib_async.protobuf.CancelPositionsMulti_pb2 import ( + CancelPositionsMulti as CancelPositionsMultiProto, +) from ib_async.protobuf.Contract_pb2 import Contract as ContractProto +from ib_async.protobuf.FamilyCode_pb2 import FamilyCode as FamilyCodeProto +from ib_async.protobuf.FamilyCodes_pb2 import FamilyCodes as FamilyCodesProto +from ib_async.protobuf.FamilyCodesRequest_pb2 import ( + FamilyCodesRequest as FamilyCodesRequestProto, +) +from ib_async.protobuf.FAReplace_pb2 import FAReplace as FAReplaceProto +from ib_async.protobuf.FARequest_pb2 import FARequest as FARequestProto from ib_async.protobuf.IdsRequest_pb2 import IdsRequest as IdsRequestProto - +from ib_async.protobuf.PortfolioValue_pb2 import PortfolioValue as PortfolioValueProto +from ib_async.protobuf.Position_pb2 import Position as PositionProto +from ib_async.protobuf.PositionMulti_pb2 import PositionMulti as PositionMultiProto +from ib_async.protobuf.PositionsMultiRequest_pb2 import ( + PositionsMultiRequest as PositionsMultiRequestProto, +) +from ib_async.protobuf.ReceiveFA_pb2 import ReceiveFA as ReceiveFAProto +from ib_async.protobuf.ReplaceFAEnd_pb2 import ReplaceFAEnd as ReplaceFAEndProto +from ib_async.protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto +from ib_async.protobuf.SoftDollarTiersRequest_pb2 import ( + SoftDollarTiersRequest as SoftDollarTiersRequestProto, +) from ib_async.protobuf_converters.account_converters import ( - createPosition, createAccountDataRequestProto, createAccountMultiRequestProto, - createCancelAccMultiRequestProto, - createAccountValueFromUpdateMulti, - createAccountValue, createAccountSummary, + createAccountValue, + createAccountValueFromUpdateMulti, + createCancelAccMultiRequestProto, + createCancelPositionsMultiRequestProto, + createFamilyCode, + createFamilyCodes, + createFamilyCodesRequestProto, + createFAmsg, + createFAReplaceProto, + createFARequestProto, createPortfolioItem, + createPosition, + createPositionMulti, + createPositionsMultiRequestProto, + createReplaceFAEnd, + createSoftDollarTier, + createSoftDollarTiers, + createSoftDollarTiersRequestProto, createUserInfoRequestProto, ) @@ -186,3 +223,117 @@ def test_createUserInfoRequestProto(self): proto = createUserInfoRequestProto(10) assert isinstance(proto, IdsRequestProto) assert proto.numIds == 10 + + def test_createFARequestProto(self): + proto = createFARequestProto(1) + assert isinstance(proto, FARequestProto) + assert proto.faDataType == 1 + + def test_createFAReplaceProto(self): + proto = createFAReplaceProto(2, 1, "data") + assert isinstance(proto, FAReplaceProto) + assert proto.reqId == 2 + assert proto.faDataType == 1 + assert proto.xml == "data" + + def test_createFAmsg(self): + msg = ReceiveFAProto(faDataType=1, xml="fa data") + fa_data_type, xml = createFAmsg(msg) + assert fa_data_type == 1 + assert xml == "fa data" + + def test_createReplaceFAEnd(self): + msg = ReplaceFAEndProto(text="FA data replaced") + text = createReplaceFAEnd(msg) + assert text == "FA data replaced" + + def test_createPositionsMultiRequestProto(self): + proto = createPositionsMultiRequestProto(3, "U123", "MyModel") + assert isinstance(proto, PositionsMultiRequestProto) + assert proto.reqId == 3 + assert proto.account == "U123" + assert proto.modelCode == "MyModel" + + def test_createCancelPositionsMultiRequestProto(self): + proto = createCancelPositionsMultiRequestProto(4) + assert isinstance(proto, CancelPositionsMultiProto) + assert proto.reqId == 4 + + def test_createPositionMulti(self): + contract_proto = ContractProto(symbol="TSLA", secType="STK") + pos_multi_proto = PositionMultiProto( + account="U456", + modelCode="MyModel", + contract=contract_proto, + position="200.0", + avgCost=300.0, + ) + pos_multi = createPositionMulti(pos_multi_proto) + assert isinstance(pos_multi, PositionMulti) + assert pos_multi.account == "U456" + assert pos_multi.modelCode == "MyModel" + assert pos_multi.contract.symbol == "TSLA" + assert pos_multi.position == 200.0 + assert pos_multi.avgCost == 300.0 + + def test_createFamilyCodesRequestProto(self): + proto = createFamilyCodesRequestProto() + assert isinstance(proto, FamilyCodesRequestProto) + + def test_createFamilyCode(self): + family_code_proto = FamilyCodeProto(accountId="F123", familyCode="FAM1") + family_code = createFamilyCode(family_code_proto) + assert isinstance(family_code, FamilyCode) + assert family_code.accountID == "F123" + assert family_code.familyCodeStr == "FAM1" + + def test_createFamilyCodes(self): + family_codes_proto = FamilyCodesProto() + fc1 = family_codes_proto.familyCodes.add() + fc1.accountId = "F1" + fc1.familyCode = "FC1" + fc2 = family_codes_proto.familyCodes.add() + fc2.accountId = "F2" + fc2.familyCode = "FC2" + + family_codes = createFamilyCodes(family_codes_proto) + assert len(family_codes) == 2 + assert family_codes[0].accountID == "F1" + assert family_codes[1].familyCodeStr == "FC2" + + def test_createSoftDollarTiersRequestProto(self): + proto = createSoftDollarTiersRequestProto(5) + assert isinstance(proto, SoftDollarTiersRequestProto) + assert proto.reqId == 5 + + def test_createSoftDollarTier(self): + tier_proto = SoftDollarTierProto( + name="TierA", value="ValA", displayName="Display A" + ) + tier = createSoftDollarTier(tier_proto) + assert isinstance(tier, SoftDollarTier) + assert tier.name == "TierA" + assert tier.val == "ValA" + assert tier.displayName == "Display A" + + def test_createSoftDollarTiers(self): + # The function createSoftDollarTiers seems to have a wrong type hint. + # It expects a proto message with a 'softDollarTiers' field. + # SoftDollarTiersRequestProto does not have this field. + # We use a mock object to simulate the correct response proto. + class MockSoftDollarTiersResponseProto: + def __init__(self): + self.softDollarTiers = [] + + tiers_proto = MockSoftDollarTiersResponseProto() + tiers_proto.softDollarTiers.append(SoftDollarTierProto(name="T1", value="V1")) + tiers_proto.softDollarTiers.append( + SoftDollarTierProto(name="T2", displayName="D2") + ) + + tiers = createSoftDollarTiers(tiers_proto) # type: ignore + assert len(tiers) == 2 + assert tiers[0].name == "T1" + assert tiers[0].val == "V1" + assert tiers[1].name == "T2" + assert tiers[1].displayName == "D2" diff --git a/tests/test_base_converters.py b/tests/test_base_converters.py new file mode 100644 index 00000000..108bcebf --- /dev/null +++ b/tests/test_base_converters.py @@ -0,0 +1,101 @@ +""" +Tests for base_converters +""" + +from ib_async.contract import TagValue +from ib_async.objects import IBDefaults +from ib_async.protobuf.SetServerLogLevelRequest_pb2 import ( + SetServerLogLevelRequest as SetServerLogLevelRequestProto, +) +from ib_async.protobuf_converters import base_converters +from ib_async.protobuf_converters.base_converters import ( + ClientException, + createSetServerLogLevelRequestProto, + fillTagValueList, +) +from ib_async.util import UNSET_INTEGER + + +class TestBaseConverters: + def test_set_ib_defaults(self): + """ + Tests that the shared defaults instance is updated correctly. + """ + original_unset_value = base_converters.ib_defaults.unset + original_defaults = base_converters.ib_defaults + + new_defaults = IBDefaults() + new_defaults.unset = float("inf") + base_converters.set_ib_defaults(new_defaults) + assert base_converters.ib_defaults.unset == float("inf") + + # Reset to original defaults to avoid side effects in other tests + base_converters.set_ib_defaults(original_defaults) + assert base_converters.ib_defaults.unset is original_unset_value + + def test_client_exception(self): + """ + Tests the ClientException custom exception. + """ + code = 123 + message = "Test error message" + text = "Additional text" + try: + raise ClientException(code, message, text) + except ClientException as e: + assert e.code == code + assert e.message == message + assert e.text == text + assert str(e) == f"Client request error: {code}: {message}, {text}" + + def test_fillTagValueList(self): + """ + Tests that fillTagValueList correctly populates a dictionary from a list of + TagValue objects. + """ + tag_value_list = [ + TagValue(tag="tag1", value="value1"), + TagValue(tag="tag2", value="value2"), + TagValue(tag="tag3", value=""), + ] + order_proto_map: dict = {} + fillTagValueList(tag_value_list, order_proto_map) + expected_map = {"tag1": "value1", "tag2": "value2", "tag3": ""} + assert order_proto_map == expected_map + + def test_fillTagValueList_empty(self): + """ + Tests that fillTagValueList handles an empty list correctly. + """ + tag_value_list: list = [] + order_proto_map: dict = {} + fillTagValueList(tag_value_list, order_proto_map) + assert order_proto_map == {} + + def test_fillTagValueList_none(self): + """ + Tests that fillTagValueList handles a None input correctly. + """ + tag_value_list = None + order_proto_map = {"existing": "value"} + fillTagValueList(tag_value_list, order_proto_map) # type: ignore + assert order_proto_map == {"existing": "value"} + + def test_createSetServerLogLevelRequestProto(self): + """ + Tests creating a SetServerLogLevelRequest protobuf message. + """ + log_level = 4 # DETAIL + proto = createSetServerLogLevelRequestProto(log_level) + assert isinstance(proto, SetServerLogLevelRequestProto) + assert proto.logLevel == log_level + + def test_createSetServerLogLevelRequestProto_invalid_value(self): + """ + Tests creating a SetServerLogLevelRequest with an invalid log level. + The underlying `isValidIntValue` check will cause the field to not be set. + """ + + proto = createSetServerLogLevelRequestProto(UNSET_INTEGER) + assert isinstance(proto, SetServerLogLevelRequestProto) + assert not proto.HasField("logLevel") diff --git a/tests/test_contract_converters.py b/tests/test_contract_converters.py index b64680d1..3f93cbf0 100644 --- a/tests/test_contract_converters.py +++ b/tests/test_contract_converters.py @@ -1,5 +1,5 @@ -import pytest from unittest.mock import Mock + from ib_async.contract import ( ComboLeg, Contract, @@ -34,30 +34,30 @@ from ib_async.protobuf.SecDefOptParamsRequest_pb2 import ( SecDefOptParamsRequest as SecDefOptParamsRequestProto, ) -from ib_async.protobuf.SmartComponentsRequest_pb2 import ( - SmartComponentsRequest as SmartComponentsRequestProto, -) from ib_async.protobuf.SmartComponents_pb2 import ( SmartComponents as SmartComponentsProto, ) +from ib_async.protobuf.SmartComponentsRequest_pb2 import ( + SmartComponentsRequest as SmartComponentsRequestProto, +) from ib_async.protobuf_converters.contract_converters import ( - createSecDefOptParamsRequestProto, - createOptionChain, - createContract, + createComboLegProto, + createComboLegProtoList, createComboLegs, - createDeltaNeutralContract, - createIneligibilityReasonList, - setLastTradeDate, - createContractDetails, + createContract, createContractDescription, - createMatchingSymbolsRequestProto, - createMarketRuleRequestProto, + createContractDetails, createContractProto, + createDeltaNeutralContract, createDeltaNeutralContractProto, - createComboLegProtoList, - createComboLegProto, - createSmartComponentsRequestProto, + createIneligibilityReasonList, + createMarketRuleRequestProto, + createMatchingSymbolsRequestProto, + createOptionChain, + createSecDefOptParamsRequestProto, createSmartComponents, + createSmartComponentsRequestProto, + setLastTradeDate, ) @@ -147,7 +147,7 @@ def test_createIneligibilityReasonList(self): def test_setLastTradeDate(self): cd = ContractDetails(contract=Contract()) setLastTradeDate("20251219", cd, isBond=False) - assert cd.contract.lastTradeDateOrContractMonth == "20251219" + assert cd.contract.lastTradeDateOrContractMonth == "20251219" # type: ignore cd_bond = ContractDetails(contract=Contract()) setLastTradeDate("20300101", cd_bond, isBond=True) @@ -160,7 +160,7 @@ def test_createContractDetails(self): cd = createContractDetails(msg) assert isinstance(cd, ContractDetails) - assert cd.contract.symbol == "TSLA" + assert cd.contract.symbol == "TSLA" # type: ignore assert cd.marketName == "Tesla" assert cd.longName == "Tesla Inc." @@ -171,7 +171,7 @@ def test_createContractDescription(self): desc = createContractDescription(desc_proto) assert isinstance(desc, ContractDescription) - assert desc.contract.symbol == "GOOG" + assert desc.contract.symbol == "GOOG" # type: ignore assert desc.derivativeSecTypes == ["OPT", "FUT"] def test_createMatchingSymbolsRequestProto(self): diff --git a/tests/test_contract_requests.py b/tests/test_contract_requests.py index 883a0215..ae92ec29 100644 --- a/tests/test_contract_requests.py +++ b/tests/test_contract_requests.py @@ -1,21 +1,21 @@ import asyncio +from unittest.mock import Mock + import pytest -from unittest.mock import Mock, patch -from ib_async import IB, Contract, ContractDetails, ContractDescription, OptionChain +from ib_async import IB, Contract, ContractDetails from ib_async.objects import ConnectionStats from ib_async.protobuf.Contract_pb2 import Contract as ContractProto from ib_async.protobuf.ContractDescription_pb2 import ( ContractDescription as ContractDescriptionProto, ) -from ib_async.protobuf.SymbolSamples_pb2 import SymbolSamples as SymbolSamplesProto +from ib_async.protobuf.SecDefOptParameter_pb2 import ( + SecDefOptParameter as SecDefOptParameterProto, +) from ib_async.protobuf_converters.contract_converters import ( createContractDescription, createOptionChain, ) -from ib_async.protobuf.SecDefOptParameter_pb2 import ( - SecDefOptParameter as SecDefOptParameterProto, -) @pytest.mark.asyncio @@ -24,11 +24,11 @@ async def test_reqContractDetailsAsync(): Test the end-to-end flow of reqContractDetailsAsync, including the eventkit stream handling. """ ib = IB() - ib.client.isConnected = Mock(return_value=True) - ib.client.serverVersion = Mock(return_value=201) - ib.client.getReqId = Mock(return_value=1) - ib.client.reqContractDetails = Mock() - ib.client.connectionStats = Mock(return_value=ConnectionStats(0, 0, 0, 0, 0, 0)) + ib.client.isConnected = Mock(return_value=True) # type: ignore + ib.client.serverVersion = Mock(return_value=201) # type: ignore + ib.client.getReqId = Mock(return_value=1) # type: ignore + ib.client.reqContractDetails = Mock() # type: ignore + ib.client.connectionStats = Mock(return_value=ConnectionStats(0, 0, 0, 0, 0, 0)) # type: ignore contract = Contract(symbol="AAPL", secType="STK", exchange="SMART", currency="USD") @@ -47,47 +47,24 @@ async def emitter(): results, _ = await asyncio.gather(event, emitter()) assert len(results) == 2 - assert results[0].contract.conId == 1 - assert results[1].contract.conId == 2 + assert results[0].contract.conId == 1 # type: ignore + assert results[1].contract.conId == 2 # type: ignore # Check that the underlying client method was called ib.client.reqContractDetails.assert_called_once_with(1, contract) -@pytest.mark.asyncio -async def test_reqContractDetails_protobuf_not_supported(): - """ - Test that reqContractDetails raises ConnectionError if protobuf is not supported by the server. - """ - ib = IB() - ib.client.isConnected = Mock(return_value=True) - ib.client.serverVersion = Mock(return_value=100) # Simulate old server version - ib.client.getReqId = Mock(return_value=1) - ib.client.reqContractDetails = Mock() - ib.client.connectionStats = Mock(return_value=ConnectionStats(0, 0, 0, 0, 0, 0)) - ib.client.connectAsync = Mock(return_value=asyncio.Future()) - ib.client.connectAsync.return_value.set_result(None) - - contract = Contract(symbol="AAPL", secType="STK", exchange="SMART", currency="USD") - - with pytest.raises(ConnectionError) as exc_info: - await ib.connectAsync("127.0.0.1", 7497, 1) - - assert "Protobuf not supported by server." in str(exc_info.value) - ib.client.reqContractDetails.assert_not_called() - - @pytest.mark.asyncio async def test_reqMatchingSymbolsAsync(): """ Test the end-to-end flow of reqMatchingSymbolsAsync, including eventkit stream handling. """ ib = IB() - ib.client.isConnected = Mock(return_value=True) - ib.client.serverVersion = Mock(return_value=201) - ib.client.getReqId = Mock(return_value=1) - ib.client.reqMatchingSymbols = Mock() - ib.client.connectionStats = Mock(return_value=ConnectionStats(0, 0, 0, 0, 0, 0)) + ib.client.isConnected = Mock(return_value=True) # type: ignore + ib.client.serverVersion = Mock(return_value=201) # type: ignore + ib.client.getReqId = Mock(return_value=1) # type: ignore + ib.client.reqMatchingSymbols = Mock() # type: ignore + ib.client.connectionStats = Mock(return_value=ConnectionStats(0, 0, 0, 0, 0, 0)) # type: ignore pattern = "AAPL" @@ -115,8 +92,8 @@ async def emitter(): results, _ = await asyncio.gather(event, emitter()) assert len(results) == 2 - assert results[0].contract.conId == 10 - assert results[1].contract.conId == 20 + assert results[0].contract.conId == 10 # type: ignore + assert results[1].contract.conId == 20 # type: ignore # Check that the underlying client method was called ib.client.reqMatchingSymbols.assert_called_once_with(1, pattern) @@ -128,11 +105,13 @@ async def test_reqSecDefOptParamsAsync(): Test the end-to-end flow of reqSecDefOptParamsAsync, including eventkit stream handling. """ ib = IB() - ib.client.isConnected = Mock(return_value=True) - ib.client.serverVersion = Mock(return_value=201) - ib.client.getReqId = Mock(return_value=1) - ib.client.reqSecDefOptParams = Mock() - ib.client.connectionStats = Mock(return_value=ConnectionStats(0, 0, 0, 0, 0, 0)) + ib.client.isConnected = Mock(return_value=True) # type: ignore + ib.client.serverVersion = Mock(return_value=201) # type: ignore + ib.client.getReqId = Mock(return_value=1) # type: ignore + ib.client.reqSecDefOptParams = Mock() # type: ignore + ib.client.connectionStats = Mock( # type: ignore + return_value=ConnectionStats(0, 0, 0, 0, 0, 0) + ) underlyingSymbol = "SPX" futFopExchange = "SMART" diff --git a/tests/test_historical_data_converters.py b/tests/test_historical_data_converters.py index bc0bf264..c7a2591e 100644 --- a/tests/test_historical_data_converters.py +++ b/tests/test_historical_data_converters.py @@ -12,6 +12,9 @@ HistoricalTickLast, RealTimeBar, ) +from ib_async.protobuf.CancelHistoricalData_pb2 import ( + CancelHistoricalData as CancelHistoricalDataProto, +) from ib_async.protobuf.FundamentalsDataRequest_pb2 import ( FundamentalsDataRequest as FundamentalsDataRequestProto, ) @@ -43,6 +46,15 @@ from ib_async.protobuf.HistoricalTickLast_pb2 import ( HistoricalTickLast as HistoricalTickLastProto, ) +from ib_async.protobuf.HistoricalTicks_pb2 import ( + HistoricalTicks as HistoricalTicksProto, +) +from ib_async.protobuf.HistoricalTicksBidAsk_pb2 import ( + HistoricalTicksBidAsk as HistoricalTicksBidAskProto, +) +from ib_async.protobuf.HistoricalTicksLast_pb2 import ( + HistoricalTicksLast as HistoricalTicksLastProto, +) from ib_async.protobuf.HistoricalTicksRequest_pb2 import ( HistoricalTicksRequest as HistoricalTicksRequestProto, ) @@ -58,12 +70,11 @@ from ib_async.protobuf.TickAttribLast_pb2 import ( TickAttribLast as TickAttribLastProto, ) -from ib_async.protobuf.CancelHistoricalData_pb2 import ( - CancelHistoricalData as CancelHistoricalDataProto, -) +from ib_async.protobuf.TickByTickData_pb2 import TickByTickData as TickByTickDataProto from ib_async.protobuf_converters.historical_data_converters import ( createBarData, createBarDataList, + createCancelHistoricalDataProto, createFundamentalsDataRequestProto, createHeadTimestampRequestProto, createHistogramDataEntry, @@ -73,11 +84,12 @@ createHistoricalTick, createHistoricalTickBidAsk, createHistoricalTickLast, + createHistoricalTickShim, createHistoricalTicksRequestProto, createRealTimeBarsRequestProto, createRealTimeBarTick, + createTickByTick, fillTagValueList, - createCancelHistoricalDataProto, ) @@ -319,3 +331,70 @@ def test_createCancelHistoricalDataProto(self): proto = createCancelHistoricalDataProto(1) assert isinstance(proto, CancelHistoricalDataProto) assert proto.reqId == 1 + + def test_createHistoricalTickShim_ticks(self): + proto = HistoricalTicksProto() + tick1 = proto.historicalTicks.add() + tick1.time = 1672531200 + tick1.price = 100.0 + tick1.size = "10" + ticks = createHistoricalTickShim(proto, timezone.utc) + assert len(ticks) == 1 + assert isinstance(ticks[0], HistoricalTick) + assert ticks[0].price == 100.0 + + def test_createHistoricalTickShim_bidask(self): + proto = HistoricalTicksBidAskProto() + tick1 = proto.historicalTicksBidAsk.add() + tick1.time = 1672531200 + tick1.priceBid = 99.9 + tick1.priceAsk = 100.1 + ticks = createHistoricalTickShim(proto, timezone.utc) + assert len(ticks) == 1 + assert isinstance(ticks[0], HistoricalTickBidAsk) + assert ticks[0].priceBid == 99.9 + + def test_createHistoricalTickShim_last(self): + proto = HistoricalTicksLastProto() + tick1 = proto.historicalTicksLast.add() + tick1.time = 1672531200 + tick1.price = 100.0 + ticks = createHistoricalTickShim(proto, timezone.utc) + assert len(ticks) == 1 + assert isinstance(ticks[0], HistoricalTickLast) + assert ticks[0].price == 100.0 + + def test_createTickByTick_last(self): + tick_last_proto = HistoricalTickLastProto(time=1672531200, price=150.0) + proto = TickByTickDataProto(tickType=1, historicalTickLast=tick_last_proto) + tick = createTickByTick(proto, timezone.utc) + assert isinstance(tick, HistoricalTickLast) + assert tick.price == 150.0 + + def test_createTickByTick_bidask(self): + tick_bidask_proto = HistoricalTickBidAskProto( + time=1672531200, priceBid=149.9, priceAsk=150.1 + ) + proto = TickByTickDataProto(tickType=3, historicalTickBidAsk=tick_bidask_proto) + tick = createTickByTick(proto, timezone.utc) + assert isinstance(tick, HistoricalTickBidAsk) + assert tick.priceBid == 149.9 + + def test_createTickByTick_midpoint(self): + tick_midpoint_proto = HistoricalTickProto(time=1672531200, price=149.95) + proto = TickByTickDataProto( + tickType=4, historicalTickMidPoint=tick_midpoint_proto + ) + tick = createTickByTick(proto, timezone.utc) + assert isinstance(tick, HistoricalTick) + assert tick.price == 149.95 + + def test_createTickByTick_invalid_type(self): + proto = TickByTickDataProto(tickType=0) + with pytest.raises(ValueError): + createTickByTick(proto, timezone.utc) + + def test_createTickByTick_none(self): + proto = TickByTickDataProto(tickType=5) # Unknown type + tick = createTickByTick(proto, timezone.utc) + assert tick is None diff --git a/tests/test_market_data_converters.py b/tests/test_market_data_converters.py index 1983e8f2..f2bbfc27 100644 --- a/tests/test_market_data_converters.py +++ b/tests/test_market_data_converters.py @@ -1,4 +1,5 @@ import pytest + from ib_async.contract import Contract from ib_async.objects import ( OptionComputation, @@ -10,26 +11,6 @@ TickStringData, TickType, ) -from ib_async.protobuf.MarketDataTypeRequest_pb2 import ( - MarketDataTypeRequest as MarketDataTypeRequestProto, -) -from ib_async.protobuf.MarketDataRequest_pb2 import ( - MarketDataRequest as MarketDataRequestProto, -) -from ib_async.protobuf.CancelMarketData_pb2 import ( - CancelMarketData as CancelMarketDataProto, -) -from ib_async.protobuf.TickByTickRequest_pb2 import ( - TickByTickRequest as TickByTickRequestProto, -) -from ib_async.protobuf.TickReqParams_pb2 import TickReqParams as TickReqParamsProto -from ib_async.protobuf.TickPrice_pb2 import TickPrice as TickPriceProto -from ib_async.protobuf.TickSize_pb2 import TickSize as TickSizeProto -from ib_async.protobuf.TickString_pb2 import TickString as TickStringProto -from ib_async.protobuf.TickGeneric_pb2 import TickGeneric as TickGenericProto -from ib_async.protobuf.TickOptionComputation_pb2 import ( - TickOptionComputation as TickOptionComputationProto, -) from ib_async.protobuf.CalculateImpliedVolatilityRequest_pb2 import ( CalculateImpliedVolatilityRequest as CalculateImpliedVolatilityRequestProto, ) @@ -42,21 +23,42 @@ from ib_async.protobuf.CancelCalculateOptionPrice_pb2 import ( CancelCalculateOptionPrice as CancelCalculateOptionPriceProto, ) +from ib_async.protobuf.CancelMarketData_pb2 import ( + CancelMarketData as CancelMarketDataProto, +) +from ib_async.protobuf.MarketDataRequest_pb2 import ( + MarketDataRequest as MarketDataRequestProto, +) +from ib_async.protobuf.MarketDataTypeRequest_pb2 import ( + MarketDataTypeRequest as MarketDataTypeRequestProto, +) +from ib_async.protobuf.TickByTickRequest_pb2 import ( + TickByTickRequest as TickByTickRequestProto, +) +from ib_async.protobuf.TickGeneric_pb2 import TickGeneric as TickGenericProto +from ib_async.protobuf.TickOptionComputation_pb2 import ( + TickOptionComputation as TickOptionComputationProto, +) +from ib_async.protobuf.TickPrice_pb2 import TickPrice as TickPriceProto +from ib_async.protobuf.TickReqParams_pb2 import TickReqParams as TickReqParamsProto +from ib_async.protobuf.TickSize_pb2 import TickSize as TickSizeProto +from ib_async.protobuf.TickString_pb2 import TickString as TickStringProto from ib_async.protobuf_converters.market_data_converters import ( - createMarketDataTypeRequestProto, - createMarketDataRequestProto, cancelMarketDataProto, + createCalculateImpliedVolatilityRequestProto, + createCalculateOptionPriceRequestProto, + createCancelCalculateImpliedVolatilityProto, + createCancelCalculateOptionPriceProto, + createMarketDataRequestProto, + createMarketDataTypeRequestProto, createTickByTickRequestProto, + createTickData, + createTickGenericData, + createTickOptionComputation, createTickParams, createTickPriceData, createTickSizeData, createTickStringData, - createTickGenericData, - createTickOptionComputation, - createCalculateImpliedVolatilityRequestProto, - createCalculateOptionPriceRequestProto, - createCancelCalculateImpliedVolatilityProto, - createCancelCalculateOptionPriceProto, ) @@ -144,6 +146,35 @@ def test_createTickGenericData(self): assert generic_data.tickType == TickType.OPTION_IMPLIED_VOL assert generic_data.value == 0.5 + def test_createTickData(self): + # Test with TickPriceProto + price_proto = TickPriceProto(reqId=1, tickType=TickType.BID.value, price=1.2, size="100") + price_data = createTickData(price_proto) + assert isinstance(price_data, TickPriceData) + assert price_data.price == 1.2 + + # Test with TickSizeProto + size_proto = TickSizeProto(reqId=1, tickType=TickType.ASK_SIZE.value, size="200") + size_data = createTickData(size_proto) + assert isinstance(size_data, TickSizeData) + assert size_data.size == 200 + + # Test with TickStringProto + string_proto = TickStringProto(reqId=1, tickType=TickType.LAST_TIMESTAMP.value, value="123") + string_data = createTickData(string_proto) + assert isinstance(string_data, TickStringData) + assert string_data.value == "123" + + # Test with TickGenericProto + generic_proto = TickGenericProto(reqId=1, tickType=TickType.HIGH.value, value=1.23) + generic_data = createTickData(generic_proto) + assert isinstance(generic_data, TickGenericData) + assert generic_data.value == 1.23 + + # Test with an invalid type + with pytest.raises(ValueError): + createTickData(123) # type: ignore + def test_createTickOptionComputation(self): proto = TickOptionComputationProto( reqId=1, diff --git a/tests/test_news_converters.py b/tests/test_news_converters.py new file mode 100644 index 00000000..941ce791 --- /dev/null +++ b/tests/test_news_converters.py @@ -0,0 +1,167 @@ +""" +Tests for news_converters. +""" + +from ib_async.contract import TagValue +from ib_async.objects import ( + HistoricalNews, + NewsArticle, + NewsBulletin, + NewsProvider, + NewsTick, +) +from ib_async.protobuf.CancelNewsBulletins_pb2 import ( + CancelNewsBulletins as CancelNewsBulletinsProto, +) +from ib_async.protobuf.HistoricalNews_pb2 import HistoricalNews as HistoricalNewsProto +from ib_async.protobuf.HistoricalNewsRequest_pb2 import ( + HistoricalNewsRequest as HistoricalNewsRequestProto, +) +from ib_async.protobuf.NewsArticle_pb2 import NewsArticle as NewsArticleProto +from ib_async.protobuf.NewsArticleRequest_pb2 import ( + NewsArticleRequest as NewsArticleRequestProto, +) +from ib_async.protobuf.NewsBulletin_pb2 import ( + NewsBulletin as NewsBulletinProto, +) +from ib_async.protobuf.NewsBulletinsRequest_pb2 import ( + NewsBulletinsRequest as NewsBulletinsRequestProto, +) +from ib_async.protobuf.NewsProviders_pb2 import ( + NewsProviders as NewsProvidersProto, +) +from ib_async.protobuf.NewsProvidersRequest_pb2 import ( + NewsProvidersRequest as NewsProvidersRequestProto, +) +from ib_async.protobuf.TickNews_pb2 import TickNews as TickNewsProto +from ib_async.protobuf_converters.news_converters import ( + createCancelNewsBulletinsProto, + createHistoricalNews, + createHistoricalNewsRequestProto, + createNewProviders, + createNewsArticle, + createNewsArticleRequestProto, + createNewsBulletin, + createNewsBulletinsRequestProto, + createNewsProvidersRequestProto, + createTickNews, +) + + +class TestNewsConverters: + def test_createNewsBulletinsRequestProto(self): + proto = createNewsBulletinsRequestProto(True) + assert isinstance(proto, NewsBulletinsRequestProto) + assert proto.allMessages is True + + proto_false = createNewsBulletinsRequestProto(False) + assert not proto_false.allMessages + + def test_createCancelNewsBulletinsProto(self): + proto = createCancelNewsBulletinsProto() + assert isinstance(proto, CancelNewsBulletinsProto) + + def test_createNewsBulletin(self): + proto = NewsBulletinProto( + newsMsgId=1, + newsMsgType=2, + newsMessage="Test message", + originatingExch="NYSE", + ) + bulletin = createNewsBulletin(proto) + assert isinstance(bulletin, NewsBulletin) + assert bulletin.msgId == 1 + assert bulletin.msgType == 2 + assert bulletin.message == "Test message" + assert bulletin.origExchange == "NYSE" + + def test_createNewsProvidersRequestProto(self): + proto = createNewsProvidersRequestProto() + assert isinstance(proto, NewsProvidersRequestProto) + + def test_createNewsArticleRequestProto(self): + options = [TagValue("opt1", "val1")] + proto = createNewsArticleRequestProto(1, "BZ", "article123", options) + assert isinstance(proto, NewsArticleRequestProto) + assert proto.reqId == 1 + assert proto.providerCode == "BZ" + assert proto.articleId == "article123" + assert "opt1" in proto.newsArticleOptions + assert proto.newsArticleOptions["opt1"] == "val1" + + def test_createHistoricalNewsRequestProto(self): + options = [TagValue("opt2", "val2")] + proto = createHistoricalNewsRequestProto( + 2, + 12345, + "BZ,FLY", + "20230101-00:00:00", + "20230102-00:00:00", + 100, + options, + ) + assert isinstance(proto, HistoricalNewsRequestProto) + assert proto.reqId == 2 + assert proto.conId == 12345 + assert proto.providerCodes == "BZ,FLY" + assert proto.startDateTime == "20230101-00:00:00" + assert proto.endDateTime == "20230102-00:00:00" + assert proto.totalResults == 100 + assert "opt2" in proto.historicalNewsOptions + assert proto.historicalNewsOptions["opt2"] == "val2" + + def test_createNewProviders(self): + providers_proto = NewsProvidersProto() + p1 = providers_proto.newsProviders.add() + p1.providerCode = "BZ" + p1.providerName = "Benzinga" + p2 = providers_proto.newsProviders.add() + p2.providerCode = "FLY" + p2.providerName = "Fly on the Wall" + + providers = createNewProviders(providers_proto) + assert isinstance(providers, list) + assert len(providers) == 2 + assert isinstance(providers[0], NewsProvider) + assert providers[0].code == "BZ" + assert providers[0].name == "Benzinga" + assert providers[1].code == "FLY" + assert providers[1].name == "Fly on the Wall" + + def test_createNewsArticle(self): + article_proto = NewsArticleProto( + articleType=0, articleText="This is the article body." + ) + article = createNewsArticle(article_proto) + assert isinstance(article, NewsArticle) + assert article.articleType == 0 + assert article.articleText == "This is the article body." + + def test_createHistoricalNews(self): + news_proto = HistoricalNewsProto( + time="2023-01-01 12:00:00.0", + providerCode="BZ", + articleId="news1", + headline="Breaking News", + ) + news = createHistoricalNews(news_proto) + assert isinstance(news, HistoricalNews) + assert news.providerCode == "BZ" + assert news.articleId == "news1" + assert news.headline == "Breaking News" + + def test_createTickNews(self): + tick_proto = TickNewsProto( + timestamp=1672531200, + providerCode="RTRS", + articleId="tick1", + headline="Market Update", + extraData="Extra info", + ) + tick = createTickNews(tick_proto) + assert isinstance(tick, NewsTick) + assert tick.timeStamp == 1672531200 + assert tick.providerCode == "RTRS" + assert tick.articleId == "tick1" + assert tick.headline == "Market Update" + assert tick.extraData == "Extra info" diff --git a/tests/test_subscription_converters.py b/tests/test_subscription_converters.py index 28c13d25..4291ed4c 100644 --- a/tests/test_subscription_converters.py +++ b/tests/test_subscription_converters.py @@ -1,38 +1,36 @@ -import pytest -from ib_async.objects import ScannerSubscription, TagValue, ScanData from ib_async.contract import ContractDetails -from ib_async.protobuf.ScannerParametersRequest_pb2 import ( - ScannerParametersRequest as ScannerParametersRequestProto, -) -from ib_async.protobuf.ScannerSubscriptionRequest_pb2 import ( - ScannerSubscriptionRequest as ScannerSubscriptionRequestProto, -) -from ib_async.protobuf.ScannerSubscription_pb2 import ( - ScannerSubscription as ScannerSubscriptionProto, +from ib_async.objects import ScanData, ScannerSubscription, TagValue +from ib_async.protobuf.CancelPnL_pb2 import CancelPnL as CancelPnLProto +from ib_async.protobuf.CancelPnLSingle_pb2 import ( + CancelPnLSingle as CancelPnLSingleProto, ) from ib_async.protobuf.CancelScannerSubscription_pb2 import ( CancelScannerSubscription as CancelScannerSubscriptionProto, ) from ib_async.protobuf.PnLRequest_pb2 import PnLRequest as PnLRequestProto -from ib_async.protobuf.CancelPnL_pb2 import CancelPnL as CancelPnLProto from ib_async.protobuf.PnLSingleRequest_pb2 import ( PnLSingleRequest as PnLSingleRequestProto, ) -from ib_async.protobuf.CancelPnLSingle_pb2 import ( - CancelPnLSingle as CancelPnLSingleProto, -) from ib_async.protobuf.ScannerData_pb2 import ScannerData as ScannerDataProto - +from ib_async.protobuf.ScannerParametersRequest_pb2 import ( + ScannerParametersRequest as ScannerParametersRequestProto, +) +from ib_async.protobuf.ScannerSubscription_pb2 import ( + ScannerSubscription as ScannerSubscriptionProto, +) +from ib_async.protobuf.ScannerSubscriptionRequest_pb2 import ( + ScannerSubscriptionRequest as ScannerSubscriptionRequestProto, +) from ib_async.protobuf_converters.subscription_converters import ( - createScannerParametersRequestProto, - createScannerSubscriptionRequestProto, - createScannerSubscriptionProto, + createCancelPnLProto, + createCancelPnLSingleProto, createCancelScannerSubscriptionProto, createPnLRequestProto, - createCancelPnLProto, createPnLSingleRequestProto, - createCancelPnLSingleProto, createScannerDataList, + createScannerParametersRequestProto, + createScannerSubscriptionProto, + createScannerSubscriptionRequestProto, ) @@ -67,7 +65,7 @@ def test_createScannerSubscriptionProto(self): stockTypeFilter="ALL", ) options = [TagValue("opt1", "val1")] - filters = [] + filters: list = [] proto = createScannerSubscriptionProto(sub, options, filters) assert isinstance(proto, ScannerSubscriptionProto) @@ -84,7 +82,7 @@ def test_createScannerSubscriptionProto(self): assert len(proto.scannerSubscriptionFilterOptions) == 0 def test_createScannerSubscriptionProto_none(self): - proto = createScannerSubscriptionProto(None, [], []) + proto = createScannerSubscriptionProto(None, [], []) # type: ignore assert proto is None def test_createCancelScannerSubscriptionProto(self): @@ -148,7 +146,7 @@ def test_createScannerDataList(self): assert isinstance(item1, ScanData) assert item1.rank == 1 assert isinstance(item1.contractDetails, ContractDetails) - assert item1.contractDetails.contract.symbol == "AAPL" + assert item1.contractDetails.contract.symbol == "AAPL" # type: ignore assert item1.contractDetails.marketName == "NASDAQ" assert item1.distance == "dist1" assert item1.benchmark == "bench1" @@ -159,7 +157,7 @@ def test_createScannerDataList(self): item2 = data_list[1] assert isinstance(item2, ScanData) assert item2.rank == 2 - assert item2.contractDetails.contract.symbol == "GOOG" + assert item2.contractDetails.contract.symbol == "GOOG" # type: ignore assert item2.distance == "" def test_createScannerDataList_empty(self): diff --git a/tests/test_trade_converter.py b/tests/test_trade_converter.py index 280927ce..15d612dd 100644 --- a/tests/test_trade_converter.py +++ b/tests/test_trade_converter.py @@ -1,115 +1,104 @@ -import pytest from datetime import datetime from decimal import Decimal -from unittest.mock import Mock -from ib_async.contract import Contract, ComboLeg +import pytest + +from ib_async.contract import ComboLeg, Contract from ib_async.objects import ( - OptionExerciseType, - SoftDollarTier, - Execution, - Fill, CommissionReport, + Execution, ExecutionFilter, + Fill, + OptionExerciseType, + SoftDollarTier, ) from ib_async.order import ( + ExecutionCondition, + MarginCondition, Order, + OrderCancel, OrderComboLeg, OrderState, OrderStatus, - Trade, + PercentChangeCondition, PriceCondition, TimeCondition, - MarginCondition, - ExecutionCondition, + Trade, VolumeCondition, - PercentChangeCondition, - OrderCancel, ) -from ib_async.protobuf.Order_pb2 import Order as OrderProto -from ib_async.protobuf.PlaceOrderRequest_pb2 import ( - PlaceOrderRequest as PlaceOrderRequestProto, -) -from ib_async.protobuf.Contract_pb2 import Contract as ContractProto -from ib_async.protobuf.DeltaNeutralContract_pb2 import ( - DeltaNeutralContract as DeltaNeutralContractProto, +from ib_async.protobuf.CancelOrderRequest_pb2 import ( + CancelOrderRequest as CancelOrderRequestProto, ) -from ib_async.protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto from ib_async.protobuf.ComboLeg_pb2 import ComboLeg as ComboLegProto -from ib_async.protobuf.OrderCondition_pb2 import OrderCondition as OrderConditionProto -from ib_async.protobuf.OrderState_pb2 import OrderState as OrderStateProto -from ib_async.protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto -from ib_async.protobuf.Execution_pb2 import Execution as ExecutionProto -from ib_async.protobuf.ExecutionDetails_pb2 import ( - ExecutionDetails as ExecutionDetailsProto, -) from ib_async.protobuf.CommissionAndFeesReport_pb2 import ( CommissionAndFeesReport as CommissionReportProto, ) -from ib_async.protobuf.ExecutionFilter_pb2 import ( - ExecutionFilter as ExecutionFilterProto, +from ib_async.protobuf.Contract_pb2 import Contract as ContractProto +from ib_async.protobuf.Execution_pb2 import Execution as ExecutionProto +from ib_async.protobuf.ExecutionDetails_pb2 import ( + ExecutionDetails as ExecutionDetailsProto, ) from ib_async.protobuf.ExecutionRequest_pb2 import ( ExecutionRequest as ExecutionRequestProto, ) -from ib_async.protobuf.OrderCancel_pb2 import OrderCancel as OrderCancelProto -from ib_async.protobuf.GlobalCancelRequest_pb2 import ( - GlobalCancelRequest as GlobalCancelRequestProto, -) -from ib_async.protobuf.CancelOrderRequest_pb2 import ( - CancelOrderRequest as CancelOrderRequestProto, -) from ib_async.protobuf.ExerciseOptionsRequest_pb2 import ( ExerciseOptionsRequest as ExerciseOptionsRequestProto, ) +from ib_async.protobuf.GlobalCancelRequest_pb2 import ( + GlobalCancelRequest as GlobalCancelRequestProto, +) from ib_async.protobuf.OpenOrder_pb2 import OpenOrder as OpenOrderProto -from ib_async.protobuf.OrderAllocation_pb2 import ( - OrderAllocation as OrderAllocationProtoProto, +from ib_async.protobuf.Order_pb2 import Order as OrderProto +from ib_async.protobuf.OrderCancel_pb2 import OrderCancel as OrderCancelProto +from ib_async.protobuf.OrderCondition_pb2 import OrderCondition as OrderConditionProto +from ib_async.protobuf.OrderState_pb2 import OrderState as OrderStateProto +from ib_async.protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto +from ib_async.protobuf.PlaceOrderRequest_pb2 import ( + PlaceOrderRequest as PlaceOrderRequestProto, ) - - +from ib_async.protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto from ib_async.protobuf_converters.trade_converters import ( - createPlaceOrderRequestProto, - createOrderProto, - createOrder, - createSoftDollarTierProto, - createTagValueList, - createOrderState, - createOrderAllocations, + createCancelOrderRequestProto, + createCommissionReport, + createConditionsProto, + createContractConditionProto, createContractFromExecutionDetails, - createOrderStatus, createExecution, - createFill, - createTradeFromOpenOrder, - createCommissionReport, + createExecutionCondition, + createExecutionConditionProto, createExecutionRequestProto, - createOrderCancelProto, - createGlobalCancelRequestProto, - createCancelOrderRequestProto, createExerciseOptionsRequestProto, - createConditionsProto, + createFill, + createGlobalCancelRequestProto, + createMarginCondition, + createMarginConditionProto, + createOperatorConditionProto, + createOrder, + createOrderAllocations, + createOrderCancelProto, createOrderComboLegs, createOrderConditionProto, - createOperatorConditionProto, - createContractConditionProto, + createOrderConditions, + createOrderProto, + createOrderState, + createOrderStatus, + createPercentChangeCondition, + createPercentChangeConditionProto, + createPlaceOrderRequestProto, + createPriceCondition, createPriceConditionProto, + createSoftDollarTier, + createSoftDollarTierFromOrder, + createSoftDollarTierProto, + createTagValueList, + createTimeCondition, createTimeConditionProto, - createMarginConditionProto, - createExecutionConditionProto, + createTradeFromOpenOrder, + createVolumeCondition, createVolumeConditionProto, - createPercentChangeConditionProto, - createOrderConditions, setConditionFields, - setOperatorConditionFields, setContractConditionFields, - createPriceCondition, - createTimeCondition, - createMarginCondition, - createExecutionCondition, - createVolumeCondition, - createPercentChangeCondition, - createSoftDollarTierFromOrder, - createSoftDollarTier, + setOperatorConditionFields, ) @@ -907,3 +896,14 @@ def test_createExerciseOptionsRequestProto(self): assert proto.exerciseQuantity == 100 assert proto.account == "U123" assert proto.override is True + + def test_createSoftDollarTierProto(self): + order = Order() + order.softDollarTier = SoftDollarTier( + name="TestTier", val="TestVal", displayName="Test Display" + ) + proto = createSoftDollarTierProto(order) + assert isinstance(proto, SoftDollarTierProto) + assert proto.name == "TestTier" + assert proto.value == "TestVal" + assert proto.displayName == "Test Display" From 5c9b15df3c9b30ebb569a3a2066fc44cd01f8291 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:44:35 +0100 Subject: [PATCH 20/48] refactor: clean up whitespace and formatting across multiple files --- ib_async/client.py | 2 -- ib_async/contract.py | 2 +- ib_async/decoder.py | 6 +----- ib_async/order.py | 2 +- ib_async/wrapper.py | 33 ++++++++++++--------------------- 5 files changed, 15 insertions(+), 30 deletions(-) diff --git a/ib_async/client.py b/ib_async/client.py index c9d9e840..f3ab0faf 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -877,8 +877,6 @@ def verifyAndAuthRequest(self, apiName, apiVersion, opaqueIsvKey): def verifyAndAuthMessage(self, apiData, xyzResponse): self.send(73, 1, apiData, xyzResponse) - - def reqAccountUpdatesMulti( self, reqId: int, account: str, modelCode: str, ledgerAndNLV: bool ): diff --git a/ib_async/contract.py b/ib_async/contract.py index 5895dc57..263ade33 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -31,7 +31,7 @@ class IneligibilityReason: description: str = field(default_factory=str) -@dataclass(slots=True,frozen=True) +@dataclass(slots=True, frozen=True) class DeltaNeutralContract: conId: int = 0 delta: float = 0.0 diff --git a/ib_async/decoder.py b/ib_async/decoder.py index a2ffc7e9..607296d7 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -3,9 +3,7 @@ import logging from .contract import ( - Contract, ContractDescription, - ContractDetails, DeltaNeutralContract, ) from .message import MessageId @@ -15,7 +13,6 @@ HistoricalTickType, NewsProvider, PriceIncrement, - TagValue, ) from .order import OrderStatus from .protobuf.AccountDataEnd_pb2 import AccountDataEnd as AccountDataEndProto @@ -692,8 +689,7 @@ def softDollarTiersProto(self, msg: SoftDollarTiersProto): def familyCodesProto(self, msg: FamilyCodesProto): self.wrapper.familyCodes(msg) - - def bondContractDetailsProto(self, msg:ContractDataProto): + def bondContractDetailsProto(self, msg: ContractDataProto): reqId = msg.reqId cd = createContractDetails(msg) self.wrapper.bondContractDetails(reqId, cd) diff --git a/ib_async/order.py b/ib_async/order.py index 070d9fb6..3f9b8c1d 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -283,7 +283,7 @@ class OrderStatus: @property def total(self) -> float | Decimal: """Helper property to return the total size of this requested order.""" - return self.filled + self.remaining # type: ignore + return self.filled + self.remaining # type: ignore PendingSubmit: ClassVar[str] = "PendingSubmit" PendingCancel: ClassVar[str] = "PendingCancel" diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 4c5cba57..2d7aa07e 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -305,7 +305,7 @@ def __init__(self, reqId: int, code: int, message: str): class Wrapper: """Wrapper implementation for use with the IB class. - Wrapper keeps track of `state`, accounts, accoutn values, positions, etc. and + Wrapper keeps track of `state`, accounts, accoutn values, positions, etc. and respond to requests, subscriptions and streming data. """ @@ -323,7 +323,7 @@ class Wrapper: positions: dict[str, dict[int, Position]] = field(init=False) """ account -> conId -> Position """ - + positionsMulti: dict[str, dict[int, PositionMulti]] = field(init=False) """ account -> conId -> PositionMulti """ @@ -578,7 +578,7 @@ def updatePortfolio(self, portfolioItem: PortfolioItem): def position(self, position: Position): # get/create dict for account - account_positions = self.positions[position.account] + account_positions = self.positions[position.account] if position.position == 0: # remove position account_positions.pop(position.contract.conId, None) @@ -593,11 +593,7 @@ def position(self, position: Position): def positionEnd(self): self._endReq("position") - def positionMulti( - self, - reqId: int, - postionMulti: PositionMulti - ): + def positionMulti(self, reqId: int, postionMulti: PositionMulti): account_positionsMulti = self.positionsMulti[postionMulti.account] if postionMulti.position == 0: account_positionsMulti.pop(postionMulti.contract.conId, None) @@ -605,13 +601,12 @@ def positionMulti( account_positionsMulti[postionMulti.contract.conId] = postionMulti if self._isReady: - self.ib.positionMultiEvent.emit(postionMulti) + self.ib.positionMultiEvent.emit(postionMulti) self.response_bus.emit(reqId, postionMulti) def positionMultiEnd(self, reqId: int): self._endReq(reqId) - def pnl( self, reqId: int, dailyPnL: float, unrealizedPnL: float, realizedPnL: float ): @@ -1039,20 +1034,16 @@ def tickNews( self.newsTicks.append(newsTick) self.ib.tickNewsEvent.emit(newsTick) - def newsArticle(self, reqId: int, newsArticle:NewsArticle): - self.response_bus.emit(reqId,newsArticle) + def newsArticle(self, reqId: int, newsArticle: NewsArticle): + self.response_bus.emit(reqId, newsArticle) - def historicalNews( - self, reqId: int, historicalNews: HistoricalNews - ): + def historicalNews(self, reqId: int, historicalNews: HistoricalNews): self.response_bus.emit(reqId, historicalNews) def historicalNewsEnd(self, reqId, _hasMore: bool): self._endReq(reqId) - def updateNewsBulletin( - self, msgId: int, newsBulletin:NewsBulletin - ): + def updateNewsBulletin(self, msgId: int, newsBulletin: NewsBulletin): self.newsBulletins[msgId] = newsBulletin self.ib.newsBulletinEvent.emit(newsBulletin) @@ -1060,9 +1051,9 @@ def receiveFA(self, _faDataType: int, faXmlData: str): self.response_bus.emit("requestFA", faXmlData) self._endReq("requestFA") - def replaceFAEnd(self, reqId:int,text:str): + def replaceFAEnd(self, reqId: int, text: str): self._logger.info("Replace FA Response: %s, %s", reqId, text) - + def currentTime(self, time: int): dt = datetime.fromtimestamp(time, self.defaultTimezone) self.response_bus.emit("currentTime", dt) @@ -1104,7 +1095,7 @@ def userInfo(self, reqId: int, whiteBrandingId: str): self.response_bus.emit(reqId, whiteBrandingId) def softDollarTiers(self, reqId: int, tiers: list[SoftDollarTier]): - self._logger.info("reqId: %s, softDollarTiers: %s", reqId,tiers) + self._logger.info("reqId: %s, softDollarTiers: %s", reqId, tiers) def familyCodes(self, familyCodes: list[FamilyCode]): self._logger.info("familyCodes: %s", familyCodes) From 3b9d9edb2739839af0763c2e26dbce5b42b3f07d Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Thu, 15 Jan 2026 21:31:17 +0100 Subject: [PATCH 21/48] fix: cancel market data request for unknown ticker delivery --- ib_async/wrapper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 2d7aa07e..27ea8004 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -868,6 +868,7 @@ def tickerDelivery(self, reqId: int, tickData: TickDeliveryType): ticker = self.tickers.get_by_request_id(reqId) if not ticker: self._logger.error("tickerDelivery: Unknown reqId: %s, %r", reqId, tickData) + self.ib.client.cancelMktData(reqId) return ticker.ticker_bus.emit(tickData, self.lastTime) self.pendingTickers.add(ticker) From 833862cd102ff4fe71e276044ee4b45656a95046 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sat, 17 Jan 2026 22:03:46 +0100 Subject: [PATCH 22/48] refactor: update PortfolioItem to use Decimal for position and enhance protobuf converters --- ib_async/objects.py | 3 +- .../protobuf_converters/account_converters.py | 62 +++++++++++++++---- ib_async/wrapper.py | 3 +- tests/test_account_converters.py | 3 +- 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/ib_async/objects.py b/ib_async/objects.py index 345e15b2..f11631f0 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -5,6 +5,7 @@ from dataclasses import dataclass, field from datetime import date as date_ from datetime import datetime, timezone, tzinfo +from decimal import Decimal from enum import Enum from typing import TYPE_CHECKING, Any, NamedTuple, TypeAlias @@ -612,7 +613,7 @@ class PriceIncrement: @dataclass(slots=True, frozen=True) class PortfolioItem: contract: Contract - position: float + position: Decimal marketPrice: float marketValue: float averageCost: float diff --git a/ib_async/protobuf_converters/account_converters.py b/ib_async/protobuf_converters/account_converters.py index a448eb44..cdb9f36c 100644 --- a/ib_async/protobuf_converters/account_converters.py +++ b/ib_async/protobuf_converters/account_converters.py @@ -2,7 +2,7 @@ Account data protobuf converters. """ -from ib_async.util import UNSET_DOUBLE, isValidIntValue +from decimal import Decimal from ..objects import ( AccountValue, @@ -49,6 +49,7 @@ from ..protobuf.SoftDollarTiersRequest_pb2 import ( SoftDollarTiersRequest as SoftDollarTiersRequestProto, ) +from ..util import UNSET_DECIMAL, UNSET_DOUBLE, isValidIntValue from .base_converters import ClientException from .contract_converters import createContract @@ -188,18 +189,55 @@ def createAccountSummary(accountSummaryProto: AccountSummaryProto) -> AccountVal def createPortfolioItem(portfolioValueProto: PortfolioValueProto) -> PortfolioItem: - return PortfolioItem( - contract=createContract(portfolioValueProto.contract), - position=float(portfolioValueProto.position) - if portfolioValueProto.position - else 0.0, - marketPrice=portfolioValueProto.marketPrice, - marketValue=portfolioValueProto.marketValue, - averageCost=portfolioValueProto.averageCost, - unrealizedPNL=portfolioValueProto.unrealizedPNL, - realizedPNL=portfolioValueProto.realizedPNL, - account=portfolioValueProto.accountName, + contract = createContract(portfolioValueProto.contract) + + position = ( + Decimal(portfolioValueProto.position) + if portfolioValueProto.HasField("position") + else UNSET_DECIMAL + ) + marketPrice = ( + portfolioValueProto.marketPrice + if portfolioValueProto.HasField("marketPrice") + else 0 + ) + marketValue = ( + portfolioValueProto.marketValue + if portfolioValueProto.HasField("marketValue") + else 0 + ) + averageCost = ( + portfolioValueProto.averageCost + if portfolioValueProto.HasField("averageCost") + else 0 + ) + unrealizedPNL = ( + portfolioValueProto.unrealizedPNL + if portfolioValueProto.HasField("unrealizedPNL") + else 0 + ) + realizedPNL = ( + portfolioValueProto.realizedPNL + if portfolioValueProto.HasField("realizedPNL") + else 0 + ) + accountName = ( + portfolioValueProto.accountName + if portfolioValueProto.HasField("accountName") + else "" + ) + + portfolioItem = PortfolioItem( + contract=contract, + position=position, + marketPrice=marketPrice, + marketValue=marketValue, + averageCost=averageCost, + unrealizedPNL=unrealizedPNL, + realizedPNL=realizedPNL, + account=accountName, ) + return portfolioItem def createUserInfoRequestProto(reqId: int) -> IdsRequestProto: diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 27ea8004..cd217560 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio +from decimal import Decimal import logging import time from collections import defaultdict @@ -568,7 +569,7 @@ def accountSummaryEnd(self, reqId: int): def updatePortfolio(self, portfolioItem: PortfolioItem): account_portfolio = self.portfolio[portfolioItem.account] - if portfolioItem.position == 0: + if portfolioItem.position == Decimal(0): account_portfolio.pop(portfolioItem.contract.conId, None) else: account_portfolio[portfolioItem.contract.conId] = portfolioItem diff --git a/tests/test_account_converters.py b/tests/test_account_converters.py index 2cb7e0ae..10d3fe41 100644 --- a/tests/test_account_converters.py +++ b/tests/test_account_converters.py @@ -1,5 +1,6 @@ """Test for acount protobuf converters.""" +from decimal import Decimal from ib_async.objects import ( AccountValue, FamilyCode, @@ -197,7 +198,7 @@ def test_createPortfolioItem(self): assert isinstance(portfolio_item, PortfolioItem) assert portfolio_item.contract.conId == 456 assert portfolio_item.contract.symbol == "AAPL" - assert portfolio_item.position == 50.0 + assert portfolio_item.position == Decimal("50.0") assert portfolio_item.marketPrice == 170.0 assert portfolio_item.marketValue == 8500.0 assert portfolio_item.averageCost == 160.0 From 3cd7a55bd20e21be5bc4e468fae112e644f2a8db Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sat, 17 Jan 2026 22:28:12 +0100 Subject: [PATCH 23/48] refactor: update Position and PortfolioItem to use Decimal for position and enhance related converters --- ib_async/objects.py | 7 ++++--- ib_async/protobuf_converters/account_converters.py | 14 ++++++++------ ib_async/util.py | 3 +++ ib_async/wrapper.py | 5 +++-- tests/test_account_converters.py | 5 +++-- 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/ib_async/objects.py b/ib_async/objects.py index f11631f0..cf066d49 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -16,7 +16,7 @@ from eventkit import Event from .contract import Contract, ScanData, TagValue -from .util import EPOCH, UNSET_DOUBLE, UNSET_INTEGER +from .util import EPOCH, UNSET_DOUBLE, UNSET_INTEGER, DECIMAL_NAN nan = float("nan") @@ -626,7 +626,7 @@ class PortfolioItem: class Position: account: str contract: Contract - position: float + position: Decimal avgCost: float @@ -634,7 +634,7 @@ class Position: class PositionMulti: account: str contract: Contract - position: float + position: Decimal avgCost: float modelCode: str @@ -864,6 +864,7 @@ class IBDefaults: # optionally replace ib_async default for all instance variable values before # popualted from API updates unset: Any = nan + unset_decimal: Any = DECIMAL_NAN # optionally change the timezone used for log history events in objects (no impact # on orders or data processing) diff --git a/ib_async/protobuf_converters/account_converters.py b/ib_async/protobuf_converters/account_converters.py index cdb9f36c..4b0ce7e2 100644 --- a/ib_async/protobuf_converters/account_converters.py +++ b/ib_async/protobuf_converters/account_converters.py @@ -49,8 +49,8 @@ from ..protobuf.SoftDollarTiersRequest_pb2 import ( SoftDollarTiersRequest as SoftDollarTiersRequestProto, ) -from ..util import UNSET_DECIMAL, UNSET_DOUBLE, isValidIntValue -from .base_converters import ClientException +from ..util import UNSET_DOUBLE, isValidIntValue +from .base_converters import ClientException, ib_defaults from .contract_converters import createContract @@ -62,7 +62,9 @@ def createPosition(positionProto: PositionProto) -> Position: position = Position( account=positionProto.account, contract=contract, - position=float(positionProto.position) if positionProto.position else 0.0, + position=Decimal(positionProto.position) + if positionProto.position + else ib_defaults.unset_decimal, avgCost=positionProto.avgCost, ) return position @@ -194,7 +196,7 @@ def createPortfolioItem(portfolioValueProto: PortfolioValueProto) -> PortfolioIt position = ( Decimal(portfolioValueProto.position) if portfolioValueProto.HasField("position") - else UNSET_DECIMAL + else ib_defaults.unset_decimal ) marketPrice = ( portfolioValueProto.marketPrice @@ -311,9 +313,9 @@ def createPositionMulti(positionMultiProto: PositionMultiProto) -> PositionMulti contract = createContract(positionMultiProto.contract) position = ( - float(positionMultiProto.position) + Decimal(positionMultiProto.position) if positionMultiProto.HasField("position") - else UNSET_DOUBLE + else ib_defaults.unset_decimal ) avgCost = ( positionMultiProto.avgCost if positionMultiProto.HasField("avgCost") else 0 diff --git a/ib_async/util.py b/ib_async/util.py index da0deff3..88393bce 100644 --- a/ib_async/util.py +++ b/ib_async/util.py @@ -30,6 +30,9 @@ UNSET_DOUBLE: Final = sys.float_info.max UNSET_DECIMAL: Final = Decimal(2**127 - 1) NO_VALID_ID: Final = -1 +DECIMAL_NAN: Final = Decimal("NaN") +DECIMAL_ZERO = Decimal("0") + Time_t: TypeAlias = dt.time | dt.datetime diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index cd217560..bd47c273 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -66,6 +66,7 @@ from .order import Order, OrderState, OrderStatus, Trade from .ticker import Ticker from .util import ( + DECIMAL_ZERO, EPOCH, dataclassUpdate, getLoop, @@ -569,7 +570,7 @@ def accountSummaryEnd(self, reqId: int): def updatePortfolio(self, portfolioItem: PortfolioItem): account_portfolio = self.portfolio[portfolioItem.account] - if portfolioItem.position == Decimal(0): + if portfolioItem.position == DECIMAL_ZERO: account_portfolio.pop(portfolioItem.contract.conId, None) else: account_portfolio[portfolioItem.contract.conId] = portfolioItem @@ -580,7 +581,7 @@ def updatePortfolio(self, portfolioItem: PortfolioItem): def position(self, position: Position): # get/create dict for account account_positions = self.positions[position.account] - if position.position == 0: + if position.position == DECIMAL_ZERO: # remove position account_positions.pop(position.contract.conId, None) else: diff --git a/tests/test_account_converters.py b/tests/test_account_converters.py index 10d3fe41..911d306b 100644 --- a/tests/test_account_converters.py +++ b/tests/test_account_converters.py @@ -1,6 +1,7 @@ """Test for acount protobuf converters.""" from decimal import Decimal + from ib_async.objects import ( AccountValue, FamilyCode, @@ -88,7 +89,7 @@ def test_createPosition(self): assert position.account == "DU12345" assert position.contract.conId == 123 assert position.contract.symbol == "SPY" - assert position.position == 100.0 + assert position.position == Decimal("100.0") assert position.avgCost == 150.0 def test_createAccountDataRequestProto(self): @@ -274,7 +275,7 @@ def test_createPositionMulti(self): assert pos_multi.account == "U456" assert pos_multi.modelCode == "MyModel" assert pos_multi.contract.symbol == "TSLA" - assert pos_multi.position == 200.0 + assert pos_multi.position == Decimal("200.0") assert pos_multi.avgCost == 300.0 def test_createFamilyCodesRequestProto(self): From 325ef86d3bfd9054bacb3e6d9d8764a83ed1f0fd Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 10:02:04 +0100 Subject: [PATCH 24/48] refactor: update OrderAllocation to use DECIMAL_NAN for default values and enhance tests for desiredAllocQty --- ib_async/order.py | 17 +++++++++++------ tests/test_trade_converter.py | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ib_async/order.py b/ib_async/order.py index 3f9b8c1d..9ad6916b 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -12,7 +12,12 @@ from .contract import Contract, TagValue from .objects import Fill, SoftDollarTier, TradeLogEntry -from .util import UNSET_DECIMAL, UNSET_DOUBLE, UNSET_INTEGER, dataclassNonDefaults +from .util import ( + DECIMAL_NAN, + UNSET_DOUBLE, + UNSET_INTEGER, + dataclassNonDefaults, +) class OrderTIF(StrEnum): @@ -561,11 +566,11 @@ class OrderAllocation: """ account: str = field(default="") - position: Decimal = field(default=UNSET_DECIMAL) - positionDesired: Decimal = field(default=UNSET_DECIMAL) - positionAfter: Decimal = field(default=UNSET_DECIMAL) - desiredAllocQty: Decimal = field(default=UNSET_DECIMAL) - allowedAllocQty: Decimal = field(default=UNSET_DECIMAL) + position: Decimal = field(default=DECIMAL_NAN) + positionDesired: Decimal = field(default=DECIMAL_NAN) + positionAfter: Decimal = field(default=DECIMAL_NAN) + desiredAllocQty: Decimal = field(default=DECIMAL_NAN) + allowedAllocQty: Decimal = field(default=DECIMAL_NAN) isMonetary: bool = field(default=False) diff --git a/tests/test_trade_converter.py b/tests/test_trade_converter.py index 15d612dd..87bb735e 100644 --- a/tests/test_trade_converter.py +++ b/tests/test_trade_converter.py @@ -741,6 +741,8 @@ def test_createOrderAllocations(self): assert len(allocations) == 1 assert allocations[0].account == "U1" assert allocations[0].position == Decimal("10.0") + assert allocations[0].desiredAllocQty == Decimal("5") + def test_createContractFromExecutionDetails(self): exec_details_proto = ExecutionDetailsProto() From d3003a6257c7ce63c6ce5c04b737386d302792d3 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 10:13:21 +0100 Subject: [PATCH 25/48] refactor: update OrderState to use DECIMAL_NAN for suggestedSize and simplify Decimal conversions in createOrderState --- ib_async/order.py | 2 +- .../protobuf_converters/trade_converters.py | 53 +++++++++++-------- tests/test_trade_converter.py | 2 +- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/ib_async/order.py b/ib_async/order.py index 9ad6916b..eaabf570 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -371,7 +371,7 @@ class OrderState: initMarginAfterOutsideRTH: float | Decimal = nan maintMarginAfterOutsideRTH: float | Decimal = nan equityWithLoanAfterOutsideRTH: float | Decimal = nan - suggestedSize: float | Decimal = nan + suggestedSize: Decimal = DECIMAL_NAN rejectReason: str = "" orderAllocations: list[OrderAllocation] | None = None warningText: str = "" diff --git a/ib_async/protobuf_converters/trade_converters.py b/ib_async/protobuf_converters/trade_converters.py index 3c68357a..a48cc2a2 100644 --- a/ib_async/protobuf_converters/trade_converters.py +++ b/ib_async/protobuf_converters/trade_converters.py @@ -994,29 +994,29 @@ def createOrderState(orderStateProto: OrderStateProto) -> OrderState: if orderStateProto.HasField("status"): orderState.status = orderStateProto.status if orderStateProto.HasField("initMarginBefore"): - orderState.initMarginBefore = Decimal(orderStateProto.initMarginBefore) + orderState.initMarginBefore = orderStateProto.initMarginBefore if orderStateProto.HasField("maintMarginBefore"): - orderState.maintMarginBefore = Decimal(orderStateProto.maintMarginBefore) + orderState.maintMarginBefore = orderStateProto.maintMarginBefore if orderStateProto.HasField("equityWithLoanBefore"): - orderState.equityWithLoanBefore = Decimal(orderStateProto.equityWithLoanBefore) + orderState.equityWithLoanBefore = orderStateProto.equityWithLoanBefore if orderStateProto.HasField("initMarginChange"): - orderState.initMarginChange = Decimal(orderStateProto.initMarginChange) + orderState.initMarginChange = orderStateProto.initMarginChange if orderStateProto.HasField("maintMarginChange"): - orderState.maintMarginChange = Decimal(orderStateProto.maintMarginChange) + orderState.maintMarginChange = orderStateProto.maintMarginChange if orderStateProto.HasField("equityWithLoanChange"): - orderState.equityWithLoanChange = Decimal(orderStateProto.equityWithLoanChange) + orderState.equityWithLoanChange = orderStateProto.equityWithLoanChange if orderStateProto.HasField("initMarginAfter"): - orderState.initMarginAfter = Decimal(orderStateProto.initMarginAfter) + orderState.initMarginAfter = orderStateProto.initMarginAfter if orderStateProto.HasField("maintMarginAfter"): - orderState.maintMarginAfter = Decimal(orderStateProto.maintMarginAfter) + orderState.maintMarginAfter = orderStateProto.maintMarginAfter if orderStateProto.HasField("equityWithLoanAfter"): - orderState.equityWithLoanAfter = Decimal(orderStateProto.equityWithLoanAfter) + orderState.equityWithLoanAfter = orderStateProto.equityWithLoanAfter if orderStateProto.HasField("commissionAndFees"): - orderState.commission = Decimal(orderStateProto.commissionAndFees) + orderState.commission = orderStateProto.commissionAndFees if orderStateProto.HasField("minCommissionAndFees"): - orderState.minCommission = Decimal(orderStateProto.minCommissionAndFees) + orderState.minCommission = orderStateProto.minCommissionAndFees if orderStateProto.HasField("maxCommissionAndFees"): - orderState.maxCommission = Decimal(orderStateProto.maxCommissionAndFees) + orderState.maxCommission = orderStateProto.maxCommissionAndFees if orderStateProto.HasField("commissionAndFeesCurrency"): orderState.commissionCurrency = orderStateProto.commissionAndFeesCurrency if orderStateProto.HasField("warningText"): @@ -1024,41 +1024,48 @@ def createOrderState(orderStateProto: OrderStateProto) -> OrderState: if orderStateProto.HasField("marginCurrency"): orderState.marginCurrency = orderStateProto.marginCurrency if orderStateProto.HasField("initMarginBeforeOutsideRTH"): - orderState.initMarginBeforeOutsideRTH = Decimal( + orderState.initMarginBeforeOutsideRTH = ( orderStateProto.initMarginBeforeOutsideRTH ) + if orderStateProto.HasField("maintMarginBeforeOutsideRTH"): - orderState.maintMarginBeforeOutsideRTH = Decimal( + orderState.maintMarginBeforeOutsideRTH = ( orderStateProto.maintMarginBeforeOutsideRTH ) + if orderStateProto.HasField("equityWithLoanBeforeOutsideRTH"): - orderState.equityWithLoanBeforeOutsideRTH = Decimal( + orderState.equityWithLoanBeforeOutsideRTH = ( orderStateProto.equityWithLoanBeforeOutsideRTH ) + if orderStateProto.HasField("initMarginChangeOutsideRTH"): - orderState.initMarginChangeOutsideRTH = Decimal( + orderState.initMarginChangeOutsideRTH = ( orderStateProto.initMarginChangeOutsideRTH ) + if orderStateProto.HasField("maintMarginChangeOutsideRTH"): - orderState.maintMarginChangeOutsideRTH = Decimal( + orderState.maintMarginChangeOutsideRTH = ( orderStateProto.maintMarginChangeOutsideRTH ) + if orderStateProto.HasField("equityWithLoanChangeOutsideRTH"): - orderState.equityWithLoanChangeOutsideRTH = Decimal( + orderState.equityWithLoanChangeOutsideRTH = ( orderStateProto.equityWithLoanChangeOutsideRTH ) + if orderStateProto.HasField("initMarginAfterOutsideRTH"): - orderState.initMarginAfterOutsideRTH = Decimal( - orderStateProto.initMarginAfterOutsideRTH - ) + orderState.initMarginAfterOutsideRTH = orderStateProto.initMarginAfterOutsideRTH + if orderStateProto.HasField("maintMarginAfterOutsideRTH"): - orderState.maintMarginAfterOutsideRTH = Decimal( + orderState.maintMarginAfterOutsideRTH = ( orderStateProto.maintMarginAfterOutsideRTH ) + if orderStateProto.HasField("equityWithLoanAfterOutsideRTH"): - orderState.equityWithLoanAfterOutsideRTH = Decimal( + orderState.equityWithLoanAfterOutsideRTH = ( orderStateProto.equityWithLoanAfterOutsideRTH ) + if orderStateProto.HasField("suggestedSize"): orderState.suggestedSize = Decimal(orderStateProto.suggestedSize) if orderStateProto.HasField("rejectReason"): diff --git a/tests/test_trade_converter.py b/tests/test_trade_converter.py index 87bb735e..4f7adce4 100644 --- a/tests/test_trade_converter.py +++ b/tests/test_trade_converter.py @@ -726,7 +726,7 @@ def test_createOrderState(self): order_state = createOrderState(order_state_proto) assert isinstance(order_state, OrderState) assert order_state.status == "Filled" - assert order_state.initMarginBefore == Decimal("1000.00") + assert order_state.initMarginBefore == 1000.00 assert order_state.commission == 10.00 assert order_state.completedTime == "20250101 10:00:00" From 95f5f92009a503762f27fc670caf4825b3ec22f2 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 12:27:14 +0100 Subject: [PATCH 26/48] refactor: update Order class to use DECIMAL_ZERO for totalQuantity and filledQuantity, and adjust trade converter tests for Decimal handling --- ib_async/order.py | 5 +++-- ib_async/protobuf_converters/trade_converters.py | 2 +- tests/test_trade_converter.py | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ib_async/order.py b/ib_async/order.py index eaabf570..51bf5531 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -14,6 +14,7 @@ from .objects import Fill, SoftDollarTier, TradeLogEntry from .util import ( DECIMAL_NAN, + DECIMAL_ZERO, UNSET_DOUBLE, UNSET_INTEGER, dataclassNonDefaults, @@ -52,7 +53,7 @@ class Order: clientId: int = 0 permId: int = 0 action: str = "" - totalQuantity: float | Decimal = 0.0 + totalQuantity: float | Decimal = DECIMAL_ZERO orderType: str = "" lmtPrice: float | Decimal | None = UNSET_DOUBLE auxPrice: float | Decimal | None = UNSET_DOUBLE @@ -170,7 +171,7 @@ class Order: isOmsContainer: bool = False discretionaryUpToLimitPrice: bool = False autoCancelDate: str = "" - filledQuantity: float | Decimal = UNSET_DOUBLE + filledQuantity: float | Decimal = DECIMAL_ZERO refFuturesConId: int = 0 autoCancelParent: bool = False shareholder: str = "" diff --git a/ib_async/protobuf_converters/trade_converters.py b/ib_async/protobuf_converters/trade_converters.py index a48cc2a2..811b78d7 100644 --- a/ib_async/protobuf_converters/trade_converters.py +++ b/ib_async/protobuf_converters/trade_converters.py @@ -544,7 +544,7 @@ def createOrder( if orderProto.HasField("action"): order.action = orderProto.action if orderProto.HasField("totalQuantity"): - order.totalQuantity = float(orderProto.totalQuantity) + order.totalQuantity = Decimal(orderProto.totalQuantity) if orderProto.HasField("orderType"): order.orderType = orderProto.orderType if orderProto.HasField("lmtPrice"): diff --git a/tests/test_trade_converter.py b/tests/test_trade_converter.py index 4f7adce4..2f7c337b 100644 --- a/tests/test_trade_converter.py +++ b/tests/test_trade_converter.py @@ -115,7 +115,7 @@ def test_createPlaceOrderRequestProto(self): assert proto.orderId == 1 assert proto.contract.conId == 123 assert proto.order.action == "BUY" - assert float(proto.order.totalQuantity) == 100 + assert proto.order.totalQuantity == "100" assert proto.order.orderType == "LMT" assert proto.order.lmtPrice == 400.0 @@ -504,7 +504,7 @@ def test_createOrder(self): assert isinstance(order, Order) assert order.orderId == 1 assert order.action == "BUY" - assert order.totalQuantity == 100.0 + assert order.totalQuantity == Decimal("100.0") assert order.orderType == "LMT" assert order.lmtPrice == 400.0 assert order.tif == "DAY" From 7be5f0eed8628ec18dbd5aa91208466ce7e87655 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 12:33:43 +0100 Subject: [PATCH 27/48] refactor: use relative imports consistenly in trade_converters.py --- .../protobuf_converters/trade_converters.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ib_async/protobuf_converters/trade_converters.py b/ib_async/protobuf_converters/trade_converters.py index 811b78d7..d3075434 100644 --- a/ib_async/protobuf_converters/trade_converters.py +++ b/ib_async/protobuf_converters/trade_converters.py @@ -5,8 +5,8 @@ import datetime as dt from decimal import Decimal -from ib_async.contract import Contract -from ib_async.objects import ( +from ..contract import Contract +from ..objects import ( CommissionReport, Execution, ExecutionFilter, @@ -15,7 +15,7 @@ SoftDollarTier, TagValue, ) -from ib_async.order import ( +from ..order import ( ExecutionCondition, MarginCondition, Order, @@ -32,14 +32,6 @@ Trade, VolumeCondition, ) -from ib_async.util import ( - UNSET_DOUBLE, - UNSET_INTEGER, - getEnumTypeFromString, - isValidIntValue, - parseIBDatetime, -) - from ..protobuf.CancelOrderRequest_pb2 import ( CancelOrderRequest as CancelOrderRequestProto, ) @@ -67,6 +59,14 @@ from ..protobuf.OrderStatus_pb2 import OrderStatus as OrderStatusProto from ..protobuf.PlaceOrderRequest_pb2 import PlaceOrderRequest as PlaceOrderRequestProto from ..protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto +from ..util import ( + DECIMAL_ZERO, + UNSET_DOUBLE, + UNSET_INTEGER, + getEnumTypeFromString, + isValidIntValue, + parseIBDatetime, +) from .base_converters import ClientException, fillTagValueList from .contract_converters import ( createContract, @@ -99,7 +99,7 @@ def createOrderProto(order: Order) -> OrderProto: orderProto.parentId = order.parentId if order.action: orderProto.action = order.action - if order.totalQuantity != UNSET_DOUBLE: + if order.totalQuantity != DECIMAL_ZERO: orderProto.totalQuantity = str(order.totalQuantity) if isValidIntValue(order.displaySize): orderProto.displaySize = order.displaySize From 0bd094e925ab524cdac3158af79740e2c2da9b11 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 13:46:37 +0100 Subject: [PATCH 28/48] class exec --- ib_async/objects.py | 6 +++--- ib_async/protobuf_converters/trade_converters.py | 4 ++-- tests/test_trade_converter.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ib_async/objects.py b/ib_async/objects.py index cf066d49..607205b3 100644 --- a/ib_async/objects.py +++ b/ib_async/objects.py @@ -16,7 +16,7 @@ from eventkit import Event from .contract import Contract, ScanData, TagValue -from .util import EPOCH, UNSET_DOUBLE, UNSET_INTEGER, DECIMAL_NAN +from .util import DECIMAL_NAN, DECIMAL_ZERO, EPOCH, UNSET_DOUBLE, UNSET_INTEGER nan = float("nan") @@ -75,13 +75,13 @@ class Execution: acctNumber: str = "" exchange: str = "" side: str = "" - shares: float = 0.0 + shares: Decimal = DECIMAL_ZERO price: float = 0.0 permId: int = 0 clientId: int = 0 orderId: int = 0 liquidation: int = 0 - cumQty: float = 0.0 + cumQty: Decimal = DECIMAL_ZERO avgPrice: float = 0.0 orderRef: str = "" evRule: str = "" diff --git a/ib_async/protobuf_converters/trade_converters.py b/ib_async/protobuf_converters/trade_converters.py index d3075434..569a9758 100644 --- a/ib_async/protobuf_converters/trade_converters.py +++ b/ib_async/protobuf_converters/trade_converters.py @@ -1172,7 +1172,7 @@ def createExecution(executionProto: ExecutionProto) -> Execution: if executionProto.HasField("side"): execution.side = executionProto.side if executionProto.HasField("shares"): - execution.shares = float(executionProto.shares) + execution.shares = Decimal(executionProto.shares) if executionProto.HasField("price"): execution.price = executionProto.price if executionProto.HasField("permId"): @@ -1184,7 +1184,7 @@ def createExecution(executionProto: ExecutionProto) -> Execution: if executionProto.HasField("isLiquidation"): execution.liquidation = 1 if executionProto.isLiquidation else 0 if executionProto.HasField("cumQty"): - execution.cumQty = float(executionProto.cumQty) + execution.cumQty = Decimal(executionProto.cumQty) if executionProto.HasField("avgPrice"): execution.avgPrice = executionProto.avgPrice if executionProto.HasField("orderRef"): diff --git a/tests/test_trade_converter.py b/tests/test_trade_converter.py index 2f7c337b..6f24d70d 100644 --- a/tests/test_trade_converter.py +++ b/tests/test_trade_converter.py @@ -801,7 +801,7 @@ def test_createExecution(self): assert isinstance(execution, Execution) assert execution.execId == "0001" assert execution.time == datetime(2025, 1, 1, 10, 0, 0) - assert execution.shares == 100.0 + assert execution.shares == Decimal("100.0") assert execution.optExerciseOrLapseType == OptionExerciseType.NoneItem def test_createFill(self): From c89dd966fd2b9031cecb697fcb2a01a8d984cffd Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 14:35:47 +0100 Subject: [PATCH 29/48] refactor: update contract and converters to use Decimal for size fields and remove unused imports --- ib_async/contract.py | 10 ++++++---- ib_async/protobuf_converters/account_converters.py | 2 +- ib_async/protobuf_converters/contract_converters.py | 13 ++++++++++--- ib_async/wrapper.py | 1 - 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/ib_async/contract.py b/ib_async/contract.py index 263ade33..2bc8bb6c 100644 --- a/ib_async/contract.py +++ b/ib_async/contract.py @@ -2,9 +2,11 @@ import datetime as dt from dataclasses import dataclass, field +from decimal import Decimal from enum import Enum -import ib_async.util as util +from . import util +from .util import DECIMAL_ZERO class FundAssetType(Enum): @@ -624,9 +626,9 @@ class ContractDetails: realExpirationDate: str = "" lastTradeTime: str = "" stockType: str = "" - minSize: float = 0.0 - sizeIncrement: float = 0.0 - suggestedSizeIncrement: float = 0.0 + minSize: Decimal = DECIMAL_ZERO + sizeIncrement: Decimal = DECIMAL_ZERO + suggestedSizeIncrement: Decimal = DECIMAL_ZERO # minCashQtySize: float = 0.0 cusip: str = "" ratings: str = "" diff --git a/ib_async/protobuf_converters/account_converters.py b/ib_async/protobuf_converters/account_converters.py index 4b0ce7e2..9705f1ff 100644 --- a/ib_async/protobuf_converters/account_converters.py +++ b/ib_async/protobuf_converters/account_converters.py @@ -49,7 +49,7 @@ from ..protobuf.SoftDollarTiersRequest_pb2 import ( SoftDollarTiersRequest as SoftDollarTiersRequestProto, ) -from ..util import UNSET_DOUBLE, isValidIntValue +from ..util import isValidIntValue from .base_converters import ClientException, ib_defaults from .contract_converters import createContract diff --git a/ib_async/protobuf_converters/contract_converters.py b/ib_async/protobuf_converters/contract_converters.py index df1a7221..39984f16 100644 --- a/ib_async/protobuf_converters/contract_converters.py +++ b/ib_async/protobuf_converters/contract_converters.py @@ -1,5 +1,7 @@ """Contract converters""" +from decimal import Decimal + from ..contract import ( ComboLeg, Contract, @@ -37,6 +39,7 @@ SmartComponentsRequest as SmartComponentsRequestProto, ) from ..util import ( + DECIMAL_ZERO, UNSET_DOUBLE, floatMaxString, getEnumTypeFromString, @@ -304,12 +307,16 @@ def createContractDetails( contractDetails.marketRuleIds = details.marketRuleIds contractDetails.realExpirationDate = details.realExpirationDate contractDetails.stockType = details.stockType - contractDetails.minSize = float(details.minSize) if details.minSize else 0.0 + contractDetails.minSize = ( + Decimal(details.minSize) if details.minSize else DECIMAL_ZERO + ) contractDetails.sizeIncrement = ( - float(details.sizeIncrement) if details.sizeIncrement else 0.0 + Decimal(details.sizeIncrement) if details.sizeIncrement else DECIMAL_ZERO ) contractDetails.suggestedSizeIncrement = ( - float(details.suggestedSizeIncrement) if details.suggestedSizeIncrement else 0.0 + Decimal(details.suggestedSizeIncrement) + if details.suggestedSizeIncrement + else DECIMAL_ZERO ) contractDetails.cusip = details.cusip contractDetails.ratings = details.ratings diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index bd47c273..05665855 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -3,7 +3,6 @@ from __future__ import annotations import asyncio -from decimal import Decimal import logging import time from collections import defaultdict From c23599e8c9d51fed13b381d0fca86dc916373dc4 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 16:42:15 +0100 Subject: [PATCH 30/48] refactor: add pre-commit configuration and update pyproject.toml for improved linting and formatting --- .pre-commit-config.yaml | 22 ++++++++++++++++++++++ pyproject.toml | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..1485423a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - id: check-toml +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.13 + hooks: + - id: ruff + - id: ruff-format +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.19.1 + hooks: + - id: mypy + args: [ --config-file=pyproject.toml ] + exclude: '^ib_async/protobuf/' diff --git a/pyproject.toml b/pyproject.toml index e6464fd0..35f94bd1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,19 +7,29 @@ maintainers = ["Matt Stancliff "] license = "BSD" readme = "README.md" repository = "https://github.com/ib-api-reloaded/ib_async" -packages = [{include = "ib_async"}] +packages = [{ include = "ib_async" }] include = ["ib_async/py.typed"] classifiers = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Topic :: Office/Business :: Financial :: Investment", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3 :: Only", + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Topic :: Office/Business :: Financial :: Investment", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", +] +keywords = [ + "ibapi", + "tws", + "asyncio", + "jupyter", + "interactive", + "brokers", + "async", + "ib_async", + "ib_insync", ] -keywords = ["ibapi", "tws", "asyncio", "jupyter", "interactive", "brokers", "async", "ib_async", "ib_insync"] [tool.poetry.dependencies] python = ">=3.10" @@ -45,6 +55,7 @@ ruff = "^0.11.13" grpcio-tools = "^1.75.1" pytest-timeout = "*" pytest-cov = "^6.2.1" +pre-commit = "^3.5.0" [tool.poetry.group.docs] optional = true @@ -58,6 +69,8 @@ myst-parser = "^2.0.0" [tool.mypy] ignore_missing_imports = true check_untyped_defs = true +exclude = ["^ib_async/protobuf/"] +follow_imports = "silent" [tool.pytest.ini_options] @@ -73,6 +86,8 @@ timeout = 10 [tool.ruff] line-length = 88 +fix = true +exclude = ["ib_async/protobuf/*", "examples/", "notebooks/"] [tool.ruff.lint] extend-select = [ @@ -80,8 +95,10 @@ extend-select = [ "UP", # isort imports "I", + "F", + "E", ] -ignore = ["E402"] # Ignore Module level import not at top of file +ignore = ["E402", "E501"] # Ignore Module level import not at top of file [build-system] requires = ["poetry-core"] From 26948f3ff91feaef264941345924aa4239670783 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 16:47:31 +0100 Subject: [PATCH 31/48] refactor: clean up imports and improve code formatting across multiple files --- .../market_data_converters.py | 1 - scripts/fix_proto_imports.py | 13 +++++++------ tests/test_base_converters.py | 2 +- tests/test_market_data_converters.py | 18 +++++++++++++----- tests/test_trade_converter.py | 1 - tests/test_util.py | 7 ++----- 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/ib_async/protobuf_converters/market_data_converters.py b/ib_async/protobuf_converters/market_data_converters.py index 76708cce..0f0a03fe 100644 --- a/ib_async/protobuf_converters/market_data_converters.py +++ b/ib_async/protobuf_converters/market_data_converters.py @@ -3,7 +3,6 @@ """ from collections.abc import Callable -from math import nan from typing import Any, TypeAlias from ..objects import ( diff --git a/scripts/fix_proto_imports.py b/scripts/fix_proto_imports.py index c687af7c..4d31889b 100644 --- a/scripts/fix_proto_imports.py +++ b/scripts/fix_proto_imports.py @@ -16,8 +16,9 @@ This ensures that the generated modules can correctly import their dependencies within the same package. """ -import os + import glob +import os def fix_imports(directory: str): @@ -30,19 +31,19 @@ def fix_imports(directory: str): `*_pb2.py` files. """ for filepath in glob.glob(os.path.join(directory, "*_pb2.py")): - with open(filepath, 'r+') as f: + with open(filepath, "r+") as f: lines = f.readlines() f.seek(0) new_lines = [] for line in lines: - if line.startswith('import') and '_pb2' in line: - new_lines.append('from . ' + line) + if line.startswith("import") and "_pb2" in line: + new_lines.append("from . " + line) else: new_lines.append(line) f.writelines(new_lines) f.truncate() -if __name__ == '__main__': +if __name__ == "__main__": # The target directory where the generated protobuf files are located. - fix_imports('ib_async/protobuf') \ No newline at end of file + fix_imports("ib_async/protobuf") diff --git a/tests/test_base_converters.py b/tests/test_base_converters.py index 108bcebf..25e2b793 100644 --- a/tests/test_base_converters.py +++ b/tests/test_base_converters.py @@ -50,7 +50,7 @@ def test_client_exception(self): def test_fillTagValueList(self): """ - Tests that fillTagValueList correctly populates a dictionary from a list of + Tests that fillTagValueList correctly populates a dictionary from a list of TagValue objects. """ tag_value_list = [ diff --git a/tests/test_market_data_converters.py b/tests/test_market_data_converters.py index f2bbfc27..58530355 100644 --- a/tests/test_market_data_converters.py +++ b/tests/test_market_data_converters.py @@ -148,32 +148,40 @@ def test_createTickGenericData(self): def test_createTickData(self): # Test with TickPriceProto - price_proto = TickPriceProto(reqId=1, tickType=TickType.BID.value, price=1.2, size="100") + price_proto = TickPriceProto( + reqId=1, tickType=TickType.BID.value, price=1.2, size="100" + ) price_data = createTickData(price_proto) assert isinstance(price_data, TickPriceData) assert price_data.price == 1.2 # Test with TickSizeProto - size_proto = TickSizeProto(reqId=1, tickType=TickType.ASK_SIZE.value, size="200") + size_proto = TickSizeProto( + reqId=1, tickType=TickType.ASK_SIZE.value, size="200" + ) size_data = createTickData(size_proto) assert isinstance(size_data, TickSizeData) assert size_data.size == 200 # Test with TickStringProto - string_proto = TickStringProto(reqId=1, tickType=TickType.LAST_TIMESTAMP.value, value="123") + string_proto = TickStringProto( + reqId=1, tickType=TickType.LAST_TIMESTAMP.value, value="123" + ) string_data = createTickData(string_proto) assert isinstance(string_data, TickStringData) assert string_data.value == "123" # Test with TickGenericProto - generic_proto = TickGenericProto(reqId=1, tickType=TickType.HIGH.value, value=1.23) + generic_proto = TickGenericProto( + reqId=1, tickType=TickType.HIGH.value, value=1.23 + ) generic_data = createTickData(generic_proto) assert isinstance(generic_data, TickGenericData) assert generic_data.value == 1.23 # Test with an invalid type with pytest.raises(ValueError): - createTickData(123) # type: ignore + createTickData(123) # type: ignore def test_createTickOptionComputation(self): proto = TickOptionComputationProto( diff --git a/tests/test_trade_converter.py b/tests/test_trade_converter.py index 6f24d70d..f1da8615 100644 --- a/tests/test_trade_converter.py +++ b/tests/test_trade_converter.py @@ -743,7 +743,6 @@ def test_createOrderAllocations(self): assert allocations[0].position == Decimal("10.0") assert allocations[0].desiredAllocQty == Decimal("5") - def test_createContractFromExecutionDetails(self): exec_details_proto = ExecutionDetailsProto() exec_details_proto.contract.symbol = "AAPL" diff --git a/tests/test_util.py b/tests/test_util.py index c04f4979..832abdc4 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,4 +1,3 @@ -import asyncio import datetime as dt import enum from dataclasses import dataclass, field @@ -33,9 +32,7 @@ parseIBTimeStamp, patchAsyncio, quantize_decimals, - run, schedule, - sleep, timeit, timeRange, timeRangeAsync, @@ -114,7 +111,7 @@ def test_dataclassAsDict(): def test_dataclassAsTuple(): obj = TestDataClass(a=1, b="test") - expected = (1, "test", 1.0, Decimal("1.23"), [], {}) + expected = (1, "test", 1.0, Decimal("1.23"), [], {}) # type: ignore assert dataclassAsTuple(obj) == expected with pytest.raises(TypeError): @@ -373,7 +370,7 @@ def test_floatMaxString(): assert floatMaxString(123.000000) == "123" assert floatMaxString(UNSET_DOUBLE) == "" assert floatMaxString(0.0) == "0" - assert floatMaxString(None) == "" + assert floatMaxString(None) == "" # type: ignore def test_getEnumTypeFromString(): From 2f2d7b329741d3e4cfa22a212114582ed796bbeb Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 16:54:43 +0100 Subject: [PATCH 32/48] refactor: change order filled and remaining methods to use Decimal for improved precision --- ib_async/order.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ib_async/order.py b/ib_async/order.py index 51bf5531..2bcbb615 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -453,18 +453,18 @@ def isDone(self) -> bool: """True if completely filled or cancelled, false otherwise.""" return self.orderStatus.status in OrderStatus.DoneStates - def filled(self) -> float: + def filled(self) -> Decimal: """Number of shares filled.""" fills = self.fills if self.contract.secType == "BAG": # don't count fills for the leg contracts fills = [f for f in fills if f.contract.secType == "BAG"] - return sum([f.execution.shares for f in fills]) + return sum((f.execution.shares for f in fills), DECIMAL_ZERO) - def remaining(self) -> float: + def remaining(self) -> Decimal: """Number of shares remaining to be filled.""" - return float(self.order.totalQuantity) - self.filled() + return Decimal(self.order.totalQuantity) - self.filled() @dataclass(slots=True) From 76aacb484bd204627e76664944b0e1b09012e4ef Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 16:56:12 +0100 Subject: [PATCH 33/48] refactor: remove unnecessary blank lines in documentation files --- docs/api.rst | 1 - docs/index.rst | 2 -- docs/notebooks.rst | 2 -- docs/readme.rst | 2 -- 4 files changed, 7 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 97742815..6977f86d 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -63,4 +63,3 @@ IBC Watchdog -------- .. autoclass:: ib_async.ibcontroller.Watchdog - diff --git a/docs/index.rst b/docs/index.rst index e433e422..6f4e1b2c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,5 +14,3 @@ Contents .. include:: ../README.md :parser: myst_parser.sphinx_ - - diff --git a/docs/notebooks.rst b/docs/notebooks.rst index 5785f0ed..c21eb930 100644 --- a/docs/notebooks.rst +++ b/docs/notebooks.rst @@ -18,5 +18,3 @@ Here are some recipe notebooks: Market depth Ordering Scanners - - diff --git a/docs/readme.rst b/docs/readme.rst index be92df85..339dc8f4 100644 --- a/docs/readme.rst +++ b/docs/readme.rst @@ -1,4 +1,2 @@ .. include:: ../README.md :parser: myst_parser.sphinx_ - - From e671404c1fe8b1a7cc00e55d7772d073298dcc96 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 18 Jan 2026 16:58:21 +0100 Subject: [PATCH 34/48] refactor: clean up README and pyproject.toml for improved readability and linting --- README.md | 2 +- pyproject.toml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 21cf50c6..f75449ce 100644 --- a/README.md +++ b/README.md @@ -504,7 +504,7 @@ Copy `*.proto` files from `twsapi` into `ib_async/proto`, then run: ```bash poetry run python -m grpc_tools.protoc -I=proto --python_out=ib_async/protobuf --pyi_out=ib_async/protobuf proto/*.proto -# +# poetry run python scripts/fix_proto_imports.py ``` diff --git a/pyproject.toml b/pyproject.toml index 35f94bd1..e17fe73d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,7 +98,10 @@ extend-select = [ "F", "E", ] -ignore = ["E402", "E501"] # Ignore Module level import not at top of file +ignore = [ + "E402", # Ignore Module level import not at top of file + "E501", # Ingnore long lines, specially in comments and docstrings +] [build-system] requires = ["poetry-core"] From d2d20e043c12e72684e82f37b9ef05e07470cf15 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:19:57 +0100 Subject: [PATCH 35/48] refactor: change margin and commission fields in OrderState to use float for consistency --- ib_async/order.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/ib_async/order.py b/ib_async/order.py index 2bcbb615..107014ca 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -349,29 +349,29 @@ def total(self) -> float | Decimal: @dataclass(slots=True) class OrderState: status: str = "" - initMarginBefore: float | Decimal = nan - maintMarginBefore: float | Decimal = nan - equityWithLoanBefore: float | Decimal = nan - initMarginChange: float | Decimal = nan - maintMarginChange: float | Decimal = nan - equityWithLoanChange: float | Decimal = nan - initMarginAfter: float | Decimal = nan - maintMarginAfter: float | Decimal = nan - equityWithLoanAfter: float | Decimal = nan - commission: float | Decimal = nan - minCommission: float | Decimal = nan - maxCommission: float | Decimal = nan + initMarginBefore: float = nan + maintMarginBefore: float = nan + equityWithLoanBefore: float = nan + initMarginChange: float = nan + maintMarginChange: float = nan + equityWithLoanChange: float = nan + initMarginAfter: float = nan + maintMarginAfter: float = nan + equityWithLoanAfter: float = nan + commission: float = nan + minCommission: float = nan + maxCommission: float = nan commissionCurrency: str = "" marginCurrency: str = "" - initMarginBeforeOutsideRTH: float | Decimal = nan - maintMarginBeforeOutsideRTH: float | Decimal = nan - equityWithLoanBeforeOutsideRTH: float | Decimal = nan - initMarginChangeOutsideRTH: float | Decimal = nan - maintMarginChangeOutsideRTH: float | Decimal = nan - equityWithLoanChangeOutsideRTH: float | Decimal = nan - initMarginAfterOutsideRTH: float | Decimal = nan - maintMarginAfterOutsideRTH: float | Decimal = nan - equityWithLoanAfterOutsideRTH: float | Decimal = nan + initMarginBeforeOutsideRTH: float = nan + maintMarginBeforeOutsideRTH: float = nan + equityWithLoanBeforeOutsideRTH: float = nan + initMarginChangeOutsideRTH: float = nan + maintMarginChangeOutsideRTH: float = nan + equityWithLoanChangeOutsideRTH: float = nan + initMarginAfterOutsideRTH: float = nan + maintMarginAfterOutsideRTH: float = nan + equityWithLoanAfterOutsideRTH: float = nan suggestedSize: Decimal = DECIMAL_NAN rejectReason: str = "" orderAllocations: list[OrderAllocation] | None = None From af6ec9550231ac46f033f9c84f5975642c14a83e Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:08:19 +0100 Subject: [PATCH 36/48] refactor: removie Decimal type for ETF NAV fields. Set contract field as mandatory and only Contract type. --- ib_async/ticker.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ib_async/ticker.py b/ib_async/ticker.py index 66684d5d..fa00b75e 100644 --- a/ib_async/ticker.py +++ b/ib_async/ticker.py @@ -4,7 +4,6 @@ from contextlib import suppress from dataclasses import dataclass, field from datetime import datetime -from decimal import Decimal from typing import ClassVar, Final, TypeAlias from eventkit import Event, Op @@ -154,8 +153,7 @@ class Ticker: * ``updateEvent`` (ticker: :class:`.Ticker`) """ - events: ClassVar = ("updateEvent",) - contract: Contract | None = None + contract: Contract time: datetime | None = None timestamp: float | None = None marketDataType: int = 1 @@ -250,20 +248,21 @@ class Ticker: creditmanMarkPrice: float = nan creditmanSlowMarkPrice: float = nan reutersMutualFunds: str = "" - etfNavClose: float | Decimal = nan - etfNavPriorClose: float | Decimal = nan - etfNavBid: float | Decimal = nan - etfNavAsk: float | Decimal = nan - etfNavLast: float | Decimal = nan - etfFrozenNavLast: float | Decimal = nan - etfNavHigh: float | Decimal = nan - etfNavLow: float | Decimal = nan + etfNavClose: float = nan + etfNavPriorClose: float = nan + etfNavBid: float = nan + etfNavAsk: float = nan + etfNavLast: float = nan + etfFrozenNavLast: float = nan + etfNavHigh: float = nan + etfNavLow: float = nan socialMarketAnalytics: str = "" estimatedIpoMidpoint: float = nan finalIpoLast: float = nan defaults: IBDefaults = field(default_factory=IBDefaults, repr=False) created: bool = field(default=False, repr=False) + events: ClassVar = ("updateEvent",) updateEvent: Event = field(repr=False, init=False) ticker_bus: Event = field(repr=False, init=False) From 3f25ce2f22d0791b128e1520a79431d103f35a55 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:09:41 +0100 Subject: [PATCH 37/48] refactor: set default value for tif in Order class to DAY --- ib_async/order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ib_async/order.py b/ib_async/order.py index 107014ca..540c373a 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -57,7 +57,7 @@ class Order: orderType: str = "" lmtPrice: float | Decimal | None = UNSET_DOUBLE auxPrice: float | Decimal | None = UNSET_DOUBLE - tif: str = "" + tif: str = OrderTIF.DAY activeStartTime: str = "" activeStopTime: str = "" ocaGroup: str = "" From 6b97df2949649a210ea4b5f63a2fca4e2c6447cf Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:57:39 +0100 Subject: [PATCH 38/48] refactor: add coverage configuration to omit specific directories and files --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index e17fe73d..ecf9da24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,6 +103,10 @@ ignore = [ "E501", # Ingnore long lines, specially in comments and docstrings ] +[tool.coverage.run] +# Omit specific directories and files +omit = ["ib_async/protobuf/*", "examples/", "notebooks/"] + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" From 2d640468db770252cde77b796b98abede0ed679e Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:04:02 +0100 Subject: [PATCH 39/48] refactor: update OrderStatus fields to use Decimal for consistency and adjust related converters and tests --- ib_async/order.py | 10 +++++----- ib_async/protobuf_converters/trade_converters.py | 6 +++--- tests/test_trade_converter.py | 9 +++++++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/ib_async/order.py b/ib_async/order.py index 540c373a..488819b0 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -276,15 +276,15 @@ class OrderStatus: orderId: int = 0 status: str = "" - filled: float | Decimal = 0.0 - remaining: float | Decimal = 0.0 - avgFillPrice: float | Decimal = 0.0 + filled: Decimal = DECIMAL_ZERO + remaining: Decimal = DECIMAL_ZERO + avgFillPrice: float = 0.0 permId: int = 0 parentId: int = 0 - lastFillPrice: float | Decimal = 0.0 + lastFillPrice: float = 0.0 clientId: int = 0 whyHeld: str = "" - mktCapPrice: float | Decimal = 0.0 + mktCapPrice: float = 0.0 @property def total(self) -> float | Decimal: diff --git a/ib_async/protobuf_converters/trade_converters.py b/ib_async/protobuf_converters/trade_converters.py index 569a9758..5306c8c1 100644 --- a/ib_async/protobuf_converters/trade_converters.py +++ b/ib_async/protobuf_converters/trade_converters.py @@ -1137,19 +1137,19 @@ def createOrderStatus(orderStatusProto: OrderStatusProto) -> OrderStatus: if orderStatusProto.HasField("remaining"): orderStatus.remaining = Decimal(orderStatusProto.remaining) if orderStatusProto.HasField("avgFillPrice"): - orderStatus.avgFillPrice = Decimal(str(orderStatusProto.avgFillPrice)) + orderStatus.avgFillPrice = orderStatusProto.avgFillPrice if orderStatusProto.HasField("permId"): orderStatus.permId = orderStatusProto.permId if orderStatusProto.HasField("parentId"): orderStatus.parentId = orderStatusProto.parentId if orderStatusProto.HasField("lastFillPrice"): - orderStatus.lastFillPrice = Decimal(str(orderStatusProto.lastFillPrice)) + orderStatus.lastFillPrice = orderStatusProto.lastFillPrice if orderStatusProto.HasField("clientId"): orderStatus.clientId = orderStatusProto.clientId if orderStatusProto.HasField("whyHeld"): orderStatus.whyHeld = orderStatusProto.whyHeld if orderStatusProto.HasField("mktCapPrice"): - orderStatus.mktCapPrice = Decimal(str(orderStatusProto.mktCapPrice)) + orderStatus.mktCapPrice = orderStatusProto.mktCapPrice return orderStatus diff --git a/tests/test_trade_converter.py b/tests/test_trade_converter.py index f1da8615..606ad807 100644 --- a/tests/test_trade_converter.py +++ b/tests/test_trade_converter.py @@ -504,6 +504,7 @@ def test_createOrder(self): assert isinstance(order, Order) assert order.orderId == 1 assert order.action == "BUY" + assert order.filledQuantity == Decimal("0.0") assert order.totalQuantity == Decimal("100.0") assert order.orderType == "LMT" assert order.lmtPrice == 400.0 @@ -771,6 +772,14 @@ def test_createOrderStatus(self): assert order_status.orderId == 1 assert order_status.status == "Submitted" assert order_status.filled == Decimal("50.0") + assert order_status.remaining == Decimal("50.0") + assert order_status.avgFillPrice == 150.0 + assert order_status.permId == 1001 + assert order_status.parentId == 0 + assert order_status.lastFillPrice == 150.5 + assert order_status.clientId == 10 + assert order_status.whyHeld == "" + assert order_status.mktCapPrice == 0.0 def test_createExecution(self): exec_proto = ExecutionProto( From fefb1a78951829bb6e21718f18334a5f568c8fa4 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Thu, 22 Jan 2026 13:51:53 +0100 Subject: [PATCH 40/48] fix: remove snapshot tickers once returned. --- ib_async/ib.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ib_async/ib.py b/ib_async/ib.py index 6880db3a..8658e224 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -2306,6 +2306,9 @@ async def reqTickersAsync( tickers = [] reqIds = [] for contract in contracts: + if contract is None or contract.conId: + self._logger.warning("Invalid contract: %s", contract) + continue reqId = self.client.getReqId() reqIds.append(reqId) ticker = self.wrapper.startTicker(reqId, contract) @@ -2315,6 +2318,9 @@ async def reqTickersAsync( awaitables = [t.ticker_bus for t in tickers] await asyncio.gather(*awaitables) + for ticker in tickers: + self.wrapper.endTicker(ticker) + return tickers def whatIfOrderAsync( From 8f632b37bf3fc09ceae0d700c8fac0a5dbdf9e4d Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 25 Jan 2026 13:58:02 +0100 Subject: [PATCH 41/48] fix: avoid duplicate ticker creating. wrapper.startTicker should get by contract, and only create a new ticker if contract is not found. --- ib_async/wrapper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 05665855..3fa9ca29 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -455,8 +455,10 @@ def startTicker(self, reqId: int, contract: Contract): Start a tick request that has the reqId associated with the contract. Return the ticker. """ - ticker = self.tickers.get_by_request_id(reqId) + # get existing tickers first. + ticker = self.tickers.get_by_object_id(hash(contract)) if not ticker: + # create new ticker ticker = Ticker(contract=contract, defaults=self.defaults) self.tickers.add(reqId, hash(ticker.contract), ticker) ticker.ticker_bus.takewhile(lambda data, t: data is not None).connect( From f177aa4c470f4f53e6fd67edce7a7d7575abdc72 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Fri, 30 Jan 2026 11:10:36 +0100 Subject: [PATCH 42/48] fix: typing linter warnings. --- ib_async/decoder.py | 8 +++++--- ib_async/wrapper.py | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ib_async/decoder.py b/ib_async/decoder.py index 607296d7..8258690e 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -1,6 +1,8 @@ """Deserialize and dispatch protobuf messages.""" import logging +from collections.abc import Iterable +from typing import cast from .contract import ( ContractDescription, @@ -583,7 +585,7 @@ def tickByTickDataProto(self, msg: TickByTickDataProto): self.wrapper.tickByTick(reqId, tickByTickData) def fundamentatalDataProto(self, msg: FundamentalsDataProto): - self.wrapper.fundamentalData(msg.reqId, msg.fundamentalData) + self.wrapper.fundamentalData(msg.reqId, msg.data) def scannerParametersProto(self, msg: ScannerParametersProto): xml = msg.xml if msg.HasField("xml") else "" @@ -645,7 +647,7 @@ def smartComponentsProto(self, msg: SmartComponentsProto): self.wrapper.smartComponents(reqId, components) def newsBulletinProto(self, msg: NewsBulletinProto): - msgId = msg.reqId if msg.HasField("msgId") else NO_VALID_ID + msgId = msg.newsMsgId if msg.HasField("newsMsgId") else NO_VALID_ID newsBulletin = createNewsBulletin(msg) self.wrapper.updateNewsBulletin(msgId, newsBulletin) @@ -687,7 +689,7 @@ def softDollarTiersProto(self, msg: SoftDollarTiersProto): self.wrapper.softDollarTiers(reqId, tiers) def familyCodesProto(self, msg: FamilyCodesProto): - self.wrapper.familyCodes(msg) + self.wrapper.familyCodes(cast(Iterable, msg)) def bondContractDetailsProto(self, msg: ContractDataProto): reqId = msg.reqId diff --git a/ib_async/wrapper.py b/ib_async/wrapper.py index 3fa9ca29..37fb35bd 100644 --- a/ib_async/wrapper.py +++ b/ib_async/wrapper.py @@ -6,7 +6,7 @@ import logging import time from collections import defaultdict -from collections.abc import Hashable +from collections.abc import Hashable, Iterable from dataclasses import dataclass, field from datetime import datetime from typing import ( @@ -1101,7 +1101,7 @@ def userInfo(self, reqId: int, whiteBrandingId: str): def softDollarTiers(self, reqId: int, tiers: list[SoftDollarTier]): self._logger.info("reqId: %s, softDollarTiers: %s", reqId, tiers) - def familyCodes(self, familyCodes: list[FamilyCode]): + def familyCodes(self, familyCodes: Iterable[FamilyCode]): self._logger.info("familyCodes: %s", familyCodes) def error( From 4eab72075975669cf914f87caadac8922b460a0d Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Fri, 30 Jan 2026 11:15:30 +0100 Subject: [PATCH 43/48] fix: SoftDollarTiersProto and SoftDollarTierProto (singular) typing. --- ib_async/decoder.py | 2 +- ib_async/protobuf_converters/account_converters.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ib_async/decoder.py b/ib_async/decoder.py index 8258690e..966ba06a 100644 --- a/ib_async/decoder.py +++ b/ib_async/decoder.py @@ -523,7 +523,7 @@ def headTimestampProto(self, msg: HeadTimestampProto): self.wrapper.headTimestamp(msg.reqId, msg.headTimestamp) def historicalDataProto(self, msg: HistoricalDataProto): - bar_data = createBarDataList(msg.historicalDataBars) + bar_data = createBarDataList(cast(list, msg.historicalDataBars)) self.wrapper.historicalData(msg.reqId, bar_data) def historicalDataProtoEnd(self, msg: HistoricalDataEndProto): diff --git a/ib_async/protobuf_converters/account_converters.py b/ib_async/protobuf_converters/account_converters.py index 9705f1ff..411bb893 100644 --- a/ib_async/protobuf_converters/account_converters.py +++ b/ib_async/protobuf_converters/account_converters.py @@ -46,6 +46,7 @@ from ..protobuf.ReceiveFA_pb2 import ReceiveFA as ReceiveFAProto from ..protobuf.ReplaceFAEnd_pb2 import ReplaceFAEnd as ReplaceFAEndProto from ..protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto +from ..protobuf.SoftDollarTiers_pb2 import SoftDollarTiers as SoftDollarTiersProto from ..protobuf.SoftDollarTiersRequest_pb2 import ( SoftDollarTiersRequest as SoftDollarTiersRequestProto, ) @@ -357,7 +358,9 @@ def createSoftDollarTiersRequestProto(reqId: int) -> SoftDollarTiersRequestProto return softDollarTiersRequestProto -def createSoftDollarTier(softDollarTierProto: SoftDollarTierProto) -> SoftDollarTier: +def createSoftDollarTier( + softDollarTierProto: SoftDollarTierProto, +) -> SoftDollarTier | None: name = "" value = "" displayName = "" @@ -375,7 +378,7 @@ def createSoftDollarTier(softDollarTierProto: SoftDollarTierProto) -> SoftDollar def createSoftDollarTiers( - softDollarTiersProto: SoftDollarTiersRequestProto, + softDollarTiersProto: SoftDollarTiersProto, ) -> list[SoftDollarTier]: tiers = [] if softDollarTiersProto.softDollarTiers: From 55dd092cc1374ab03eb56f34e1a66f75e63efff1 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Fri, 30 Jan 2026 14:12:08 +0100 Subject: [PATCH 44/48] fix: typing linter warnings in sendMsg call to --- ib_async/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ib_async/client.py b/ib_async/client.py index f3ab0faf..7cf969b8 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -320,7 +320,7 @@ def sendMsg(self, msg: bytes): self._isThrottling = True self.throttleStart.emit() self._logger.info("Started to throttle requests") - loop.call_at(times[0] + self.RequestsInterval, self.sendMsg, None) + loop.call_at(times[0] + self.RequestsInterval, self.sendMsg) else: if self._isThrottling: self._isThrottling = False From 42c24143773a8b87be21df6193a87fb34e396d10 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Fri, 30 Jan 2026 14:16:10 +0100 Subject: [PATCH 45/48] fix: typing linter warnings in sendMsg call --- ib_async/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ib_async/client.py b/ib_async/client.py index 7cf969b8..d5a4eb3f 100644 --- a/ib_async/client.py +++ b/ib_async/client.py @@ -320,7 +320,7 @@ def sendMsg(self, msg: bytes): self._isThrottling = True self.throttleStart.emit() self._logger.info("Started to throttle requests") - loop.call_at(times[0] + self.RequestsInterval, self.sendMsg) + loop.call_at(times[0] + self.RequestsInterval, self.sendMsg, None) # type: ignore else: if self._isThrottling: self._isThrottling = False From b960601f41e36194f7925a5883a902a6afc11626 Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Sun, 1 Feb 2026 14:50:33 +0100 Subject: [PATCH 46/48] feat: add Trigger enum --- ib_async/order.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/ib_async/order.py b/ib_async/order.py index 488819b0..6b575185 100644 --- a/ib_async/order.py +++ b/ib_async/order.py @@ -4,7 +4,7 @@ from dataclasses import dataclass, field from decimal import Decimal -from enum import StrEnum +from enum import IntEnum, StrEnum from math import nan from typing import ClassVar, TypeAlias @@ -500,6 +500,32 @@ def Or(self): return self +class TriggerMethod(IntEnum): + """ + Trigger Method. + + see https://interactivebrokers.github.io/tws-api/trigger_method_limit.html + + + 0 - The default method for instrument + 1 - "Double bid/ask" function, where stop orders are triggered based on two + consecutive bid or ask prices. + 2 - "Last" function, where stop orders are triggered based on the last price + 3 - "Double last" function + 4 - Bid/ask function + 7 - Last or bid/ask function + 8 - Mid-point function + """ + + Default = 0 + DoubleBidAsk = 1 + Last = 2 + DoubleLast = 3 + BidAsk = 4 + LastBidAsk = 7 + MidPoint = 8 + + @dataclass(slots=True) class PriceCondition(OrderCondition): condType: ClassVar[int] = 1 @@ -507,7 +533,7 @@ class PriceCondition(OrderCondition): price: float = 0.0 conId: int = 0 exch: str = "" - triggerMethod: int = 0 + triggerMethod: int = TriggerMethod.Default @dataclass(slots=True) From 7a67b319b52fdc85896d123df80ed86fec2027be Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Tue, 10 Feb 2026 14:40:43 +0100 Subject: [PATCH 47/48] fix: wrapper.response_bus "pile-up" Add _response_single and _response_multi to fix the issue and keep code DRY. This commit solves the problem for all methods called during connection. Problem explanation: by using `response_bus.filter` we create a problem. response bus is a "long lived, common" event, it does not complete, so it's never set as done. that means that `filter` is not calling `on_sorce_done` and itself is not set to "done" state. however, `take(n)` and `takewhile(predicate)` work as expected and disconnect from the source when the condition is meet. Setting themselves to "done" and cascading the effect downstream. This causes the pipeline to be done, but leaves a "filter" left-over that is not complete. The consecuence is a "pile up" in `response_bus._slots` as filter listeners remain connected. every new request creates a new slots entry. Which is clearly not acceptable. Proposed solution is to create a "notifier" event, and start the pipeline with `takeuntil(notifier)` once we receive our results. we call `notifier.emit()` to stop the pipeline from the starting point. --- ib_async/ib.py | 90 ++++++++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 54 deletions(-) diff --git a/ib_async/ib.py b/ib_async/ib.py index 8658e224..3049531c 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -521,6 +521,34 @@ def setTimeout(self, timeout: float = 60): """ self.wrapper.setTimeout(timeout) + async def _response_single(self, bus: Event, reqId: int | str) -> Any: + """Build reactive pipeline for single value""" + notifier = Event(f"notifier_{reqId}") + pipeline = await ( + bus.takeuntil(notifier) + .filter(lambda rId, data: rId == reqId) + .take(1) + .pluck(1) + .map(self._raise_if_error) + ) + notifier.emit("stop") + + return pipeline + + async def _response_multi(self, bus: Event, reqId: int | str) -> Any: + """Build reactive pipeline for single value""" + notifier = Event(f"notifier_{reqId}") + pipeline = await ( + bus.takeuntil(notifier) + .filter(lambda rId, data: rId == reqId) + .takewhile(lambda rId, data: data is not None) + .pluck(1) + .map(self._raise_if_error) + .list() + ) + notifier.emit("stop") + return pipeline + def managedAccounts(self) -> list[str]: """List of account names.""" return list(self.wrapper.accounts) @@ -2369,25 +2397,14 @@ def reqAccountUpdatesAsync(self, account: str = "") -> Awaitable[None]: """ acctCode = account or self.wrapper.accounts[0] self.client.reqAccountUpdates(True, acctCode) - return ( - self.wrapper.response_bus.filter(lambda key, _: key == "accountValues") - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(self.wrapper.response_bus, "accountValues") def reqAccountUpdatesMultiAsync( self, account: str, modelCode: str = "" ) -> Awaitable[list[AccountValue]]: reqId = self.client.getReqId() self.client.reqAccountUpdatesMulti(reqId, account, modelCode, False) - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .takewhile(lambda rId, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - .list() - ) + return self._response_multi(self.wrapper.response_bus, reqId) async def accountSummaryAsync(self, account: str = "") -> list[AccountValue]: if not self.wrapper.acctSummary: @@ -2431,33 +2448,15 @@ def reqAccountSummaryAsync(self, group: str = "All", tags: str = ""): def reqOpenOrdersAsync(self) -> Awaitable[list[Trade]]: self.client.reqOpenOrders() - return ( - self.wrapper.response_bus.filter(lambda key, _: key == "openOrders") - .takewhile(lambda key, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - .list() - ) + return self._response_multi(self.wrapper.response_bus, "openOrders") def reqAllOpenOrdersAsync(self) -> Awaitable[list[Trade]]: self.client.reqAllOpenOrders() - return ( - self.wrapper.response_bus.filter(lambda key, _: key == "openOrders") - .takewhile(lambda key, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - .list() - ) + return self._response_multi(self.wrapper.response_bus, "openOrders") def reqCompletedOrdersAsync(self, apiOnly: bool) -> Awaitable[list[Trade]]: self.client.reqCompletedOrders(apiOnly) - return ( - self.wrapper.response_bus.filter(lambda key, _: key == "completedOrders") - .takewhile(lambda key, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - .list() - ) + return self._response_multi(self.wrapper.response_bus, "completedOrders") def reqExecutionsAsync( self, execFilter: ExecutionFilter | None = None @@ -2465,37 +2464,20 @@ def reqExecutionsAsync( """Request a list of fills.""" reqId = self.client.getReqId() self.client.reqExecutions(reqId, execFilter or ExecutionFilter()) - fills = ( - self.wrapper.response_bus.filter(lambda rId, e: rId == reqId) - .takewhile(lambda r, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - .list() - ) + fills = self._response_multi(self.wrapper.response_bus, reqId) return fills def reqPositionsAsync(self) -> Awaitable[list[Position]]: """Request a list of positions.""" self.client.reqPositions() - return ( - self.wrapper.response_bus.filter(lambda name, _: name == "position") - .takewhile(lambda name, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(self.wrapper.response_bus, "position") def reqPositionsMultiAsync( self, account: str, modelCode: str = "" ) -> Awaitable[list[Position]]: reqId = self.client.getReqId() self.client.reqPositionsMulti(reqId, account, modelCode) - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .takewhile(lambda rId, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - .list() - ) + return self._response_multi(self.wrapper.response_bus, reqId) def reqContractDetailsAsync( self, contract: Contract From 0b4b02647189cc7b70582b74c9b3f7b464068b1e Mon Sep 17 00:00:00 2001 From: gnzsnz <8376642+gnzsnz@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:31:48 +0100 Subject: [PATCH 48/48] feat: implement _response_single and _response_multi, DRY --- ib_async/ib.py | 202 ++++++++++--------------------------------------- 1 file changed, 38 insertions(+), 164 deletions(-) diff --git a/ib_async/ib.py b/ib_async/ib.py index 3049531c..0106ce97 100644 --- a/ib_async/ib.py +++ b/ib_async/ib.py @@ -521,8 +521,9 @@ def setTimeout(self, timeout: float = 60): """ self.wrapper.setTimeout(timeout) - async def _response_single(self, bus: Event, reqId: int | str) -> Any: - """Build reactive pipeline for single value""" + async def _response_single(self, reqId: int | str) -> Any: + """Build reactive pipeline for single value.""" + bus = self.wrapper.response_bus notifier = Event(f"notifier_{reqId}") pipeline = await ( bus.takeuntil(notifier) @@ -535,8 +536,9 @@ async def _response_single(self, bus: Event, reqId: int | str) -> Any: return pipeline - async def _response_multi(self, bus: Event, reqId: int | str) -> Any: - """Build reactive pipeline for single value""" + async def _response_multi(self, reqId: int | str) -> Any: + """Build reactive pipeline for list.""" + bus = self.wrapper.response_bus notifier = Event(f"notifier_{reqId}") pipeline = await ( bus.takeuntil(notifier) @@ -2359,30 +2361,15 @@ def whatIfOrderAsync( whatIfOrder.whatIf = True whatIfOrder.orderId = reqId self.client.placeOrder(reqId, contract, whatIfOrder) - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(reqId) def reqCurrentTimeAsync(self) -> Awaitable[datetime.datetime]: self.client.reqCurrentTime() - return ( - self.wrapper.response_bus.filter(lambda key, _: key == "currentTime") - .pluck(1) - .take(1) - .map(self._raise_if_error) - ) + return self._response_single("currentTime") def reqCurrentTimeMiliAsync(self) -> Awaitable[datetime.datetime]: self.client.reqCurrentTimeMili() - return ( - self.wrapper.response_bus.filter(lambda key, _: key == "currentTimeMili") - .pluck(1) - .take(1) - .map(self._raise_if_error) - ) + return self._response_single("currentTimeMili") def reqAccountUpdatesAsync(self, account: str = "") -> Awaitable[None]: """ @@ -2397,14 +2384,14 @@ def reqAccountUpdatesAsync(self, account: str = "") -> Awaitable[None]: """ acctCode = account or self.wrapper.accounts[0] self.client.reqAccountUpdates(True, acctCode) - return self._response_single(self.wrapper.response_bus, "accountValues") + return self._response_single("accountValues") def reqAccountUpdatesMultiAsync( self, account: str, modelCode: str = "" ) -> Awaitable[list[AccountValue]]: reqId = self.client.getReqId() self.client.reqAccountUpdatesMulti(reqId, account, modelCode, False) - return self._response_multi(self.wrapper.response_bus, reqId) + return self._response_multi(reqId) async def accountSummaryAsync(self, account: str = "") -> list[AccountValue]: if not self.wrapper.acctSummary: @@ -2439,24 +2426,19 @@ def reqAccountSummaryAsync(self, group: str = "All", tags: str = ""): ) reqId = self.client.getReqId() self.client.reqAccountSummary(reqId, group, tags) - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(reqId) def reqOpenOrdersAsync(self) -> Awaitable[list[Trade]]: self.client.reqOpenOrders() - return self._response_multi(self.wrapper.response_bus, "openOrders") + return self._response_multi("openOrders") def reqAllOpenOrdersAsync(self) -> Awaitable[list[Trade]]: self.client.reqAllOpenOrders() - return self._response_multi(self.wrapper.response_bus, "openOrders") + return self._response_multi("openOrders") def reqCompletedOrdersAsync(self, apiOnly: bool) -> Awaitable[list[Trade]]: self.client.reqCompletedOrders(apiOnly) - return self._response_multi(self.wrapper.response_bus, "completedOrders") + return self._response_multi("completedOrders") def reqExecutionsAsync( self, execFilter: ExecutionFilter | None = None @@ -2464,20 +2446,20 @@ def reqExecutionsAsync( """Request a list of fills.""" reqId = self.client.getReqId() self.client.reqExecutions(reqId, execFilter or ExecutionFilter()) - fills = self._response_multi(self.wrapper.response_bus, reqId) + fills = self._response_multi(reqId) return fills def reqPositionsAsync(self) -> Awaitable[list[Position]]: """Request a list of positions.""" self.client.reqPositions() - return self._response_single(self.wrapper.response_bus, "position") + return self._response_single("position") def reqPositionsMultiAsync( self, account: str, modelCode: str = "" ) -> Awaitable[list[Position]]: reqId = self.client.getReqId() self.client.reqPositionsMulti(reqId, account, modelCode) - return self._response_multi(self.wrapper.response_bus, reqId) + return self._response_multi(reqId) def reqContractDetailsAsync( self, contract: Contract @@ -2490,16 +2472,7 @@ def reqContractDetailsAsync( """ reqId = self.client.getReqId() self.client.reqContractDetails(reqId, contract) - # This is a streaming response; contract details are sent one by one, - # followed by a final "end" event. The .list() operator gathers all - # individual results into a single list. - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .takewhile(lambda rId, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - .list() - ) + return self._response_multi(reqId) def reqMatchingSymbolsAsync( self, pattern: str @@ -2518,15 +2491,7 @@ def reqMatchingSymbolsAsync( """ reqId = self.client.getReqId() self.client.reqMatchingSymbols(reqId, pattern) - # This is a single-shot response; the API sends the entire list - # of results in one event. .take(1).pluck(1) is used to grab the - # payload (which is the list) from that single event. - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(reqId) def reqMarketRuleAsync(self, marketRuleId: int) -> Awaitable[list[PriceIncrement]]: """ @@ -2542,14 +2507,7 @@ def reqMarketRuleAsync(self, marketRuleId: int) -> Awaitable[list[PriceIncrement which contains a comma separated string of market rule IDs. """ self.client.reqMarketRule(marketRuleId) - return ( - self.wrapper.response_bus.filter( - lambda rId, _: rId == f"marketRule-{marketRuleId}" - ) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(f"marketRule-{marketRuleId}") async def reqHistoricalDataAsync( self, @@ -2591,13 +2549,9 @@ async def reqHistoricalDataAsync( keepUpToDate, chartOptions, ) - awaitable = ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .takewhile(lambda rId, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - ) + awaitable = self._response_single(reqId) task = asyncio.wait_for(awaitable, timeout) if timeout else awaitable + result = [] try: result = await task except asyncio.TimeoutError: @@ -2632,12 +2586,7 @@ def reqHistoricalScheduleAsync( False, None, ) - awaitable = ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + awaitable = self._response_single(reqId) return awaitable def reqHistoricalTicksAsync( @@ -2665,12 +2614,7 @@ def reqHistoricalTicksAsync( ignoreSize, miscOptions, ) - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .takewhile(lambda rId, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(reqId) async def reqHeadTimeStampAsync( self, contract: Contract, whatToShow: str, useRTH: bool, formatDate: int = 1 @@ -2679,26 +2623,15 @@ async def reqHeadTimeStampAsync( self.client.reqHeadTimeStamp(reqId, contract, whatToShow, useRTH, formatDate) - result = ( - await self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + result = await self._response_single(reqId) self.client.cancelHeadTimeStamp(reqId) - return result def reqSmartComponentsAsync(self, bboExchange) -> Awaitable[list[SmartComponent]]: reqId = self.client.getReqId() self.client.reqSmartComponents(reqId, bboExchange) - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(reqId) def reqMktDepthExchangesAsync(self) -> Awaitable[list[DepthMktDataDescription]]: future = self.wrapper.startReq("mktDepthExchanges") @@ -2710,12 +2643,7 @@ def reqHistogramDataAsync( ) -> Awaitable[list[HistogramData]]: reqId = self.client.getReqId() self.client.reqHistogramData(reqId, contract, useRTH, period) - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .takewhile(lambda rId, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(reqId) def reqFundamentalDataAsync( self, @@ -2727,12 +2655,7 @@ def reqFundamentalDataAsync( self.client.reqFundamentalData( reqId, contract, reportType, fundamentalDataOptions ) - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(reqId) async def reqScannerDataAsync( self, @@ -2755,12 +2678,7 @@ async def reqScannerDataAsync( def reqScannerParametersAsync(self) -> Awaitable[str]: reqId = "scannerParams" self.client.reqScannerParameters() - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(reqId) async def calculateImpliedVolatilityAsync( self, @@ -2774,12 +2692,7 @@ async def calculateImpliedVolatilityAsync( reqId, contract, optionPrice, underPrice, implVolOptions ) try: - awaitable = ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + awaitable = self._response_single(reqId) result = await asyncio.wait_for(awaitable, 4) return result except asyncio.TimeoutError: @@ -2800,12 +2713,7 @@ async def calculateOptionPriceAsync( reqId, contract, volatility, underPrice, optPrcOptions ) try: - awaitable = ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + awaitable = self._response_single(reqId) result = await asyncio.wait_for(awaitable, 4) return result except asyncio.TimeoutError: @@ -2825,21 +2733,10 @@ def reqSecDefOptParamsAsync( self.client.reqSecDefOptParams( reqId, underlyingSymbol, futFopExchange, underlyingSecType, underlyingConId ) - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .takewhile(lambda rId, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - .list() - ) + return self._response_multi(reqId) def reqNewsProvidersAsync(self) -> Awaitable[list[NewsProvider]]: - future = ( - self.wrapper.response_bus.filter(lambda name, _: name == "newsProviders") - .takewhile(lambda name, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - ) + future = self._response_single("newsProviders") self.client.reqNewsProviders() return future @@ -2847,14 +2744,7 @@ def reqNewsArticleAsync( self, providerCode: str, articleId: str, newsArticleOptions: list[TagValue] = [] ) -> Awaitable[NewsArticle]: reqId = self.client.getReqId() - - future = ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) - + future = self._response_single(reqId) self.client.reqNewsArticle(reqId, providerCode, articleId, newsArticleOptions) return future @@ -2874,13 +2764,7 @@ async def reqHistoricalNewsAsync( self.client.reqHistoricalNews( reqId, conId, providerCodes, start, end, totalResults, historicalNewsOptions ) - future = ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .takewhile(lambda rId, data: data is not None) - .pluck(1) - .map(self._raise_if_error) - .list() - ) + future = self._response_multi(reqId) try: result = await asyncio.wait_for(future, 4) return result @@ -2889,12 +2773,7 @@ async def reqHistoricalNewsAsync( return None async def requestFAAsync(self, faDataType: int): - future = ( - self.wrapper.response_bus.filter(lambda name, _: name == "requestFA") - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + future = self._response_single("requestFA") self.client.requestFA(faDataType) try: result = await asyncio.wait_for(future, 4) @@ -2926,12 +2805,7 @@ async def getWshEventDataAsync(self, data: WshEventData) -> str: def reqUserInfoAsync(self) -> Awaitable[str]: reqId = self.client.getReqId() self.client.reqUserInfo(reqId) - return ( - self.wrapper.response_bus.filter(lambda rId, _: rId == reqId) - .take(1) - .pluck(1) - .map(self._raise_if_error) - ) + return self._response_single(reqId) if __name__ == "__main__":