ibapi-stable 10.37.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ibapi/__init__.py +16 -0
- ibapi/account_summary_tags.py +70 -0
- ibapi/client.py +4891 -0
- ibapi/client_utils.py +412 -0
- ibapi/comm.py +100 -0
- ibapi/commission_and_fees_report.py +31 -0
- ibapi/common.py +315 -0
- ibapi/connection.py +126 -0
- ibapi/const.py +12 -0
- ibapi/contract.py +265 -0
- ibapi/decoder.py +1711 -0
- ibapi/decoder_utils.py +420 -0
- ibapi/enum_implem.py +20 -0
- ibapi/errors.py +43 -0
- ibapi/execution.py +95 -0
- ibapi/ineligibility_reason.py +18 -0
- ibapi/message.py +186 -0
- ibapi/news.py +13 -0
- ibapi/object_implem.py +12 -0
- ibapi/order.py +263 -0
- ibapi/order_cancel.py +21 -0
- ibapi/order_condition.py +289 -0
- ibapi/order_state.py +117 -0
- ibapi/orderdecoder.py +539 -0
- ibapi/protobuf/CancelOrderRequest_pb2.py +38 -0
- ibapi/protobuf/ComboLeg_pb2.py +37 -0
- ibapi/protobuf/Contract_pb2.py +39 -0
- ibapi/protobuf/DeltaNeutralContract_pb2.py +37 -0
- ibapi/protobuf/ErrorMessage_pb2.py +37 -0
- ibapi/protobuf/ExecutionDetailsEnd_pb2.py +37 -0
- ibapi/protobuf/ExecutionDetails_pb2.py +39 -0
- ibapi/protobuf/ExecutionFilter_pb2.py +37 -0
- ibapi/protobuf/ExecutionRequest_pb2.py +38 -0
- ibapi/protobuf/Execution_pb2.py +37 -0
- ibapi/protobuf/GlobalCancelRequest_pb2.py +38 -0
- ibapi/protobuf/OpenOrder_pb2.py +40 -0
- ibapi/protobuf/OpenOrdersEnd_pb2.py +37 -0
- ibapi/protobuf/OrderAllocation_pb2.py +37 -0
- ibapi/protobuf/OrderCancel_pb2.py +37 -0
- ibapi/protobuf/OrderCondition_pb2.py +37 -0
- ibapi/protobuf/OrderState_pb2.py +38 -0
- ibapi/protobuf/OrderStatus_pb2.py +37 -0
- ibapi/protobuf/Order_pb2.py +51 -0
- ibapi/protobuf/PlaceOrderRequest_pb2.py +39 -0
- ibapi/protobuf/SoftDollarTier_pb2.py +37 -0
- ibapi/reader.py +49 -0
- ibapi/scanner.py +77 -0
- ibapi/server_versions.py +159 -0
- ibapi/softdollartier.py +16 -0
- ibapi/tag_value.py +22 -0
- ibapi/ticktype.py +117 -0
- ibapi/utils.py +233 -0
- ibapi/wrapper.py +885 -0
- ibapi_stable-10.37.2.dist-info/METADATA +136 -0
- ibapi_stable-10.37.2.dist-info/RECORD +57 -0
- ibapi_stable-10.37.2.dist-info/WHEEL +5 -0
- ibapi_stable-10.37.2.dist-info/top_level.txt +1 -0
ibapi/client_utils.py
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (C) 2025 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
|
3
|
+
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ibapi.const import UNSET_DOUBLE
|
|
7
|
+
from ibapi.execution import ExecutionFilter
|
|
8
|
+
|
|
9
|
+
from ibapi.contract import Contract, DeltaNeutralContract, ComboLeg
|
|
10
|
+
from ibapi.order import Order
|
|
11
|
+
from ibapi.order_condition import OrderCondition, OperatorCondition, ContractCondition, PriceCondition, TimeCondition, MarginCondition, ExecutionCondition, VolumeCondition, PercentChangeCondition
|
|
12
|
+
from ibapi.tag_value import TagValue
|
|
13
|
+
from ibapi.utils import isValidIntValue, isValidFloatValue, isValidLongValue, isValidDecimalValue, decimalMaxString
|
|
14
|
+
from ibapi.order_condition import Create
|
|
15
|
+
|
|
16
|
+
from ibapi.protobuf.ComboLeg_pb2 import ComboLeg as ComboLegProto
|
|
17
|
+
from ibapi.protobuf.Contract_pb2 import Contract as ContractProto
|
|
18
|
+
from ibapi.protobuf.DeltaNeutralContract_pb2 import DeltaNeutralContract as DeltaNeutralContractProto
|
|
19
|
+
from ibapi.protobuf.OrderCancel_pb2 import OrderCancel as OrderCancelProto
|
|
20
|
+
from ibapi.protobuf.OrderCondition_pb2 import OrderCondition as OrderConditionProto
|
|
21
|
+
from ibapi.protobuf.PlaceOrderRequest_pb2 import PlaceOrderRequest as PlaceOrderRequestProto
|
|
22
|
+
from ibapi.protobuf.CancelOrderRequest_pb2 import CancelOrderRequest as CancelOrderRequestProto
|
|
23
|
+
from ibapi.protobuf.GlobalCancelRequest_pb2 import GlobalCancelRequest as GlobalCancelRequestProto
|
|
24
|
+
from ibapi.protobuf.ExecutionFilter_pb2 import ExecutionFilter as ExecutionFilterProto
|
|
25
|
+
from ibapi.protobuf.ExecutionRequest_pb2 import ExecutionRequest as ExecutionRequestProto
|
|
26
|
+
from ibapi.protobuf.Order_pb2 import Order as OrderProto
|
|
27
|
+
from ibapi.protobuf.SoftDollarTier_pb2 import SoftDollarTier as SoftDollarTierProto
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def createExecutionRequestProto(reqId: int, execFilter: ExecutionFilter) -> ExecutionRequestProto:
|
|
31
|
+
executionFilterProto = ExecutionFilterProto()
|
|
32
|
+
if isValidIntValue(execFilter.clientId): executionFilterProto.clientId = execFilter.clientId
|
|
33
|
+
if execFilter.acctCode: executionFilterProto.acctCode = execFilter.acctCode
|
|
34
|
+
if execFilter.time: executionFilterProto.time = execFilter.time
|
|
35
|
+
if execFilter.symbol: executionFilterProto.symbol = execFilter.symbol
|
|
36
|
+
if execFilter.secType: executionFilterProto.secType = execFilter.secType
|
|
37
|
+
if execFilter.exchange: executionFilterProto.exchange = execFilter.exchange
|
|
38
|
+
if execFilter.side: executionFilterProto.side = execFilter.side
|
|
39
|
+
if isValidIntValue(execFilter.lastNDays): executionFilterProto.lastNDays = execFilter.lastNDays
|
|
40
|
+
if execFilter.specificDates is not None and execFilter.specificDates: executionFilterProto.specificDates.extend(execFilter.specificDates)
|
|
41
|
+
executionRequestProto = ExecutionRequestProto()
|
|
42
|
+
if isValidIntValue(reqId): executionRequestProto.reqId = reqId
|
|
43
|
+
executionRequestProto.executionFilter.CopyFrom(executionFilterProto)
|
|
44
|
+
return executionRequestProto
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def createPlaceOrderRequestProto(orderId: int, contract: Contract, order: Order) -> PlaceOrderRequestProto:
|
|
48
|
+
placeOrderRequestProto = PlaceOrderRequestProto()
|
|
49
|
+
if isValidIntValue(orderId): placeOrderRequestProto.orderId = orderId
|
|
50
|
+
contractProto = createContractProto(contract, order)
|
|
51
|
+
if contractProto is not None: placeOrderRequestProto.contract.CopyFrom(contractProto)
|
|
52
|
+
orderProto = createOrderProto(order)
|
|
53
|
+
if orderProto is not None: placeOrderRequestProto.order.CopyFrom(orderProto)
|
|
54
|
+
return placeOrderRequestProto
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def createContractProto(contract: Contract, order: Order) -> ContractProto:
|
|
58
|
+
contractProto = ContractProto()
|
|
59
|
+
if isValidIntValue(contract.conId): contractProto.conId = contract.conId
|
|
60
|
+
if contract.symbol: contractProto.symbol = contract.symbol
|
|
61
|
+
if contract.secType: contractProto.secType = contract.secType
|
|
62
|
+
if contract.lastTradeDateOrContractMonth: contractProto.lastTradeDateOrContractMonth = contract.lastTradeDateOrContractMonth
|
|
63
|
+
if isValidFloatValue(contract.strike): contractProto.strike = contract.strike
|
|
64
|
+
if contract.right: contractProto.right = contract.right
|
|
65
|
+
if contract.multiplier: contractProto.multiplier = float(contract.multiplier)
|
|
66
|
+
if contract.exchange: contractProto.exchange = contract.exchange
|
|
67
|
+
if contract.primaryExchange: contractProto.primaryExchange = contract.primaryExchange
|
|
68
|
+
if contract.currency: contractProto.currency = contract.currency
|
|
69
|
+
if contract.localSymbol: contractProto.localSymbol = contract.localSymbol
|
|
70
|
+
if contract.tradingClass: contractProto.tradingClass = contract.tradingClass
|
|
71
|
+
if contract.secIdType: contractProto.secIdType = contract.secIdType
|
|
72
|
+
if contract.secId: contractProto.secId = contract.secId
|
|
73
|
+
if contract.includeExpired: contractProto.includeExpired = contract.includeExpired
|
|
74
|
+
if contract.comboLegsDescrip: contractProto.comboLegsDescrip = contract.comboLegsDescrip
|
|
75
|
+
if contract.description: contractProto.description = contract.description
|
|
76
|
+
if contract.issuerId: contractProto.issuerId = contract.issuerId
|
|
77
|
+
|
|
78
|
+
comboLegProtoList = createComboLegProtoList(contract, order)
|
|
79
|
+
if comboLegProtoList is not None and comboLegProtoList: contractProto.comboLegs.extend(comboLegProtoList)
|
|
80
|
+
|
|
81
|
+
deltaNeutralContractProto = createDeltaNeutralContractProto(contract)
|
|
82
|
+
if deltaNeutralContractProto is not None: contractProto.deltaNeutralContract.CopyFrom(deltaNeutralContractProto)
|
|
83
|
+
|
|
84
|
+
return contractProto
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def createDeltaNeutralContractProto(contract: Contract) -> DeltaNeutralContractProto:
|
|
88
|
+
deltaNeutralContractProto = None
|
|
89
|
+
if contract.deltaNeutralContract is not None:
|
|
90
|
+
deltaNeutralContract = contract.deltaNeutralContract
|
|
91
|
+
deltaNeutralContractProto = DeltaNeutralContractProto()
|
|
92
|
+
if isValidIntValue(deltaNeutralContract.conId): deltaNeutralContractProto.conId = deltaNeutralContract.conId
|
|
93
|
+
if isValidFloatValue(deltaNeutralContract.delta): deltaNeutralContractProto.delta = deltaNeutralContract.delta
|
|
94
|
+
if isValidFloatValue(deltaNeutralContract.price): deltaNeutralContractProto.price = deltaNeutralContract.price
|
|
95
|
+
return deltaNeutralContractProto
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
def createComboLegProtoList(contract: Contract, order: Order) -> list[ComboLegProto]:
|
|
100
|
+
comboLegs = contract.comboLegs
|
|
101
|
+
orderComboLegs = order.orderComboLegs
|
|
102
|
+
comboLegProtoList = []
|
|
103
|
+
if comboLegs is not None and comboLegs:
|
|
104
|
+
for i, comboLeg in enumerate(comboLegs):
|
|
105
|
+
perLegPrice = UNSET_DOUBLE
|
|
106
|
+
if orderComboLegs is not None and i < len(orderComboLegs):
|
|
107
|
+
perLegPrice = orderComboLegs[i].price
|
|
108
|
+
comboLegProto = createComboLegProto(comboLeg, perLegPrice)
|
|
109
|
+
comboLegProtoList.append(comboLegProto)
|
|
110
|
+
return comboLegProtoList
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def createComboLegProto(comboLeg: ComboLeg, perLegPrice: float) -> ComboLegProto:
|
|
114
|
+
comboLegProto = ComboLegProto()
|
|
115
|
+
if isValidIntValue(comboLeg.conId): comboLegProto.conId = comboLeg.conId
|
|
116
|
+
if isValidIntValue(comboLeg.ratio): comboLegProto.ratio = comboLeg.ratio
|
|
117
|
+
if comboLeg.action: comboLegProto.action = comboLeg.action
|
|
118
|
+
if comboLeg.exchange: comboLegProto.exchange = comboLeg.exchange
|
|
119
|
+
if isValidIntValue(comboLeg.openClose): comboLegProto.openClose = comboLeg.openClose
|
|
120
|
+
if isValidIntValue(comboLeg.shortSaleSlot): comboLegProto.shortSalesSlot = comboLeg.shortSaleSlot
|
|
121
|
+
if comboLeg.designatedLocation: comboLegProto.designatedLocation = comboLeg.designatedLocation
|
|
122
|
+
if isValidIntValue(comboLeg.exemptCode): comboLegProto.exemptCode = comboLeg.exemptCode
|
|
123
|
+
if isValidFloatValue(perLegPrice): comboLegProto.perLegPrice = perLegPrice
|
|
124
|
+
return comboLegProto
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
def createOrderProto(order: Order) -> OrderProto:
|
|
128
|
+
orderProto = OrderProto()
|
|
129
|
+
if isValidIntValue(order.clientId): order.clientId = order.clientId
|
|
130
|
+
if isValidLongValue(order.permId): orderProto.permId = order.permId
|
|
131
|
+
if isValidIntValue(order.parentId): orderProto.parentId = order.parentId
|
|
132
|
+
if order.action: orderProto.action = order.action
|
|
133
|
+
if isValidDecimalValue(order.totalQuantity): orderProto.totalQuantity = decimalMaxString(order.totalQuantity)
|
|
134
|
+
if isValidIntValue(order.displaySize): orderProto.displaySize = order.displaySize
|
|
135
|
+
if order.orderType: orderProto.orderType = order.orderType
|
|
136
|
+
if isValidFloatValue(order.lmtPrice): orderProto.lmtPrice = order.lmtPrice
|
|
137
|
+
if isValidFloatValue(order.auxPrice): orderProto.auxPrice = order.auxPrice
|
|
138
|
+
if order.tif: orderProto.tif = order.tif
|
|
139
|
+
if order.account: orderProto.account = order.account
|
|
140
|
+
if order.settlingFirm: orderProto.settlingFirm = order.settlingFirm
|
|
141
|
+
if order.clearingAccount: orderProto.clearingAccount = order.clearingAccount
|
|
142
|
+
if order.clearingIntent: orderProto.clearingIntent = order.clearingIntent
|
|
143
|
+
if order.allOrNone: orderProto.allOrNone = order.allOrNone
|
|
144
|
+
if order.blockOrder: orderProto.blockOrder = order.blockOrder
|
|
145
|
+
if order.hidden: orderProto.hidden = order.hidden
|
|
146
|
+
if order.outsideRth: orderProto.outsideRth = order.outsideRth
|
|
147
|
+
if order.sweepToFill: orderProto.sweepToFill = order.sweepToFill
|
|
148
|
+
if isValidFloatValue(order.percentOffset): orderProto.percentOffset = order.percentOffset
|
|
149
|
+
if isValidFloatValue(order.trailingPercent): orderProto.trailingPercent = order.trailingPercent
|
|
150
|
+
if isValidFloatValue(order.trailStopPrice): orderProto.trailStopPrice = order.trailStopPrice
|
|
151
|
+
if isValidIntValue(order.minQty): orderProto.minQty = order.minQty
|
|
152
|
+
if order.goodAfterTime: orderProto.goodAfterTime = order.goodAfterTime
|
|
153
|
+
if order.goodTillDate: orderProto.goodTillDate = order.goodTillDate
|
|
154
|
+
if order.ocaGroup: orderProto.ocaGroup = order.ocaGroup
|
|
155
|
+
if order.orderRef: orderProto.orderRef = order.orderRef
|
|
156
|
+
if order.rule80A: orderProto.rule80A = order.rule80A
|
|
157
|
+
if isValidIntValue(order.ocaType): orderProto.ocaType = order.ocaType
|
|
158
|
+
if isValidIntValue(order.triggerMethod): orderProto.triggerMethod = order.triggerMethod
|
|
159
|
+
if order.activeStartTime: orderProto.activeStartTime = order.activeStartTime
|
|
160
|
+
if order.activeStopTime: orderProto.activeStopTime = order.activeStopTime
|
|
161
|
+
if order.faGroup: orderProto.faGroup = order.faGroup
|
|
162
|
+
if order.faMethod: orderProto.faMethod = order.faMethod
|
|
163
|
+
if order.faPercentage: orderProto.faPercentage = order.faPercentage
|
|
164
|
+
if isValidFloatValue(order.volatility): orderProto.volatility = order.volatility
|
|
165
|
+
if isValidIntValue(order.volatilityType): orderProto.volatilityType = order.volatilityType
|
|
166
|
+
if order.continuousUpdate: orderProto.continuousUpdate = order.continuousUpdate
|
|
167
|
+
if isValidIntValue(order.referencePriceType): orderProto.referencePriceType = order.referencePriceType
|
|
168
|
+
if order.deltaNeutralOrderType: orderProto.deltaNeutralOrderType = order.deltaNeutralOrderType
|
|
169
|
+
if isValidFloatValue(order.deltaNeutralAuxPrice): orderProto.deltaNeutralAuxPrice = order.deltaNeutralAuxPrice
|
|
170
|
+
if isValidIntValue(order.deltaNeutralConId): orderProto.deltaNeutralConId = order.deltaNeutralConId
|
|
171
|
+
if order.deltaNeutralOpenClose: orderProto.deltaNeutralOpenClose = order.deltaNeutralOpenClose
|
|
172
|
+
if order.deltaNeutralShortSale: orderProto.deltaNeutralShortSale = order.deltaNeutralShortSale
|
|
173
|
+
if isValidIntValue(order.deltaNeutralShortSaleSlot): orderProto.deltaNeutralShortSaleSlot = order.deltaNeutralShortSaleSlot
|
|
174
|
+
if order.deltaNeutralDesignatedLocation: orderProto.deltaNeutralDesignatedLocation = order.deltaNeutralDesignatedLocation
|
|
175
|
+
if isValidIntValue(order.scaleInitLevelSize): orderProto.scaleInitLevelSize = order.scaleInitLevelSize
|
|
176
|
+
if isValidIntValue(order.scaleSubsLevelSize): orderProto.scaleSubsLevelSize = order.scaleSubsLevelSize
|
|
177
|
+
if isValidFloatValue(order.scalePriceIncrement): orderProto.scalePriceIncrement = order.scalePriceIncrement
|
|
178
|
+
if isValidFloatValue(order.scalePriceAdjustValue): orderProto.scalePriceAdjustValue = order.scalePriceAdjustValue
|
|
179
|
+
if isValidIntValue(order.scalePriceAdjustInterval): orderProto.scalePriceAdjustInterval = order.scalePriceAdjustInterval
|
|
180
|
+
if isValidFloatValue(order.scaleProfitOffset): orderProto.scaleProfitOffset = order.scaleProfitOffset
|
|
181
|
+
if order.scaleAutoReset: orderProto.scaleAutoReset = order.scaleAutoReset
|
|
182
|
+
if isValidIntValue(order.scaleInitPosition): orderProto.scaleInitPosition = order.scaleInitPosition
|
|
183
|
+
if isValidIntValue(order.scaleInitFillQty): orderProto.scaleInitFillQty = order.scaleInitFillQty
|
|
184
|
+
if order.scaleRandomPercent: orderProto.scaleRandomPercent = order.scaleRandomPercent
|
|
185
|
+
if order.scaleTable: orderProto.scaleTable = order.scaleTable
|
|
186
|
+
if order.hedgeType: orderProto.hedgeType = order.hedgeType
|
|
187
|
+
if order.hedgeParam: orderProto.hedgeParam = order.hedgeParam
|
|
188
|
+
|
|
189
|
+
if order.algoStrategy: orderProto.algoStrategy = order.algoStrategy
|
|
190
|
+
fillTagValueList(order.algoParams, orderProto.algoParams)
|
|
191
|
+
if order.algoId: orderProto.algoId = order.algoId
|
|
192
|
+
|
|
193
|
+
fillTagValueList(order.smartComboRoutingParams, orderProto.smartComboRoutingParams)
|
|
194
|
+
|
|
195
|
+
if order.whatIf: orderProto.whatIf = order.whatIf
|
|
196
|
+
if order.transmit: orderProto.transmit = order.transmit
|
|
197
|
+
if order.overridePercentageConstraints: orderProto.overridePercentageConstraints = order.overridePercentageConstraints
|
|
198
|
+
if order.openClose: orderProto.openClose = order.openClose
|
|
199
|
+
if isValidIntValue(order.origin): orderProto.origin = order.origin
|
|
200
|
+
if isValidIntValue(order.shortSaleSlot): orderProto.shortSaleSlot = order.shortSaleSlot
|
|
201
|
+
if order.designatedLocation: orderProto.designatedLocation = order.designatedLocation
|
|
202
|
+
if isValidIntValue(order.exemptCode): orderProto.exemptCode = order.exemptCode
|
|
203
|
+
if order.deltaNeutralSettlingFirm: orderProto.deltaNeutralSettlingFirm = order.deltaNeutralSettlingFirm
|
|
204
|
+
if order.deltaNeutralClearingAccount: orderProto.deltaNeutralClearingAccount = order.deltaNeutralClearingAccount
|
|
205
|
+
if order.deltaNeutralClearingIntent: orderProto.deltaNeutralClearingIntent = order.deltaNeutralClearingIntent
|
|
206
|
+
if isValidIntValue(order.discretionaryAmt): orderProto.discretionaryAmt = order.discretionaryAmt
|
|
207
|
+
if order.optOutSmartRouting: orderProto.optOutSmartRouting = order.optOutSmartRouting
|
|
208
|
+
if isValidIntValue(order.exemptCode): orderProto.exemptCode = order.exemptCode
|
|
209
|
+
if isValidFloatValue(order.startingPrice): orderProto.startingPrice = order.startingPrice
|
|
210
|
+
if isValidFloatValue(order.stockRefPrice): orderProto.stockRefPrice = order.stockRefPrice
|
|
211
|
+
if isValidFloatValue(order.delta): orderProto.delta = order.delta
|
|
212
|
+
if isValidFloatValue(order.stockRangeLower): orderProto.stockRangeLower = order.stockRangeLower
|
|
213
|
+
if isValidFloatValue(order.stockRangeUpper): orderProto.stockRangeUpper = order.stockRangeUpper
|
|
214
|
+
if order.notHeld: orderProto.notHeld = order.notHeld
|
|
215
|
+
|
|
216
|
+
fillTagValueList(order.orderMiscOptions, orderProto.orderMiscOptions)
|
|
217
|
+
|
|
218
|
+
if order.solicited: orderProto.solicited = order.solicited
|
|
219
|
+
if order.randomizeSize: orderProto.randomizeSize = order.randomizeSize
|
|
220
|
+
if order.randomizePrice: orderProto.randomizePrice = order.randomizePrice
|
|
221
|
+
if isValidIntValue(order.referenceContractId): orderProto.referenceContractId = order.referenceContractId
|
|
222
|
+
if isValidFloatValue(order.peggedChangeAmount): orderProto.peggedChangeAmount = order.peggedChangeAmount
|
|
223
|
+
if order.isPeggedChangeAmountDecrease: orderProto.isPeggedChangeAmountDecrease = order.isPeggedChangeAmountDecrease
|
|
224
|
+
if isValidFloatValue(order.referenceChangeAmount): orderProto.referenceChangeAmount = order.referenceChangeAmount
|
|
225
|
+
if order.referenceExchangeId: orderProto.referenceExchangeId = order.referenceExchangeId
|
|
226
|
+
if order.adjustedOrderType: orderProto.adjustedOrderType = order.adjustedOrderType
|
|
227
|
+
if isValidFloatValue(order.triggerPrice): orderProto.triggerPrice = order.triggerPrice
|
|
228
|
+
if isValidFloatValue(order.adjustedStopPrice): orderProto.adjustedStopPrice = order.adjustedStopPrice
|
|
229
|
+
if isValidFloatValue(order.adjustedStopLimitPrice): orderProto.adjustedStopLimitPrice = order.adjustedStopLimitPrice
|
|
230
|
+
if isValidFloatValue(order.adjustedTrailingAmount): orderProto.adjustedTrailingAmount = order.adjustedTrailingAmount
|
|
231
|
+
if isValidIntValue(order.adjustableTrailingUnit): orderProto.adjustableTrailingUnit = order.adjustableTrailingUnit
|
|
232
|
+
if isValidFloatValue(order.lmtPriceOffset): orderProto.lmtPriceOffset = order.lmtPriceOffset
|
|
233
|
+
|
|
234
|
+
orderConditionList = createConditionsProto(order)
|
|
235
|
+
if orderConditionList is not None and orderConditionList: orderProto.conditions.extend(orderConditionList)
|
|
236
|
+
if order.conditionsCancelOrder: orderProto.conditionsCancelOrder = order.conditionsCancelOrder
|
|
237
|
+
if order.conditionsIgnoreRth: orderProto.conditionsIgnoreRth = order.conditionsIgnoreRth
|
|
238
|
+
|
|
239
|
+
if order.modelCode: orderProto.modelCode = order.modelCode
|
|
240
|
+
if order.extOperator: orderProto.extOperator = order.extOperator
|
|
241
|
+
|
|
242
|
+
softDollarTier = createSoftDollarTierProto(order)
|
|
243
|
+
if softDollarTier is not None: orderProto.softDollarTier.CopyFrom(softDollarTier)
|
|
244
|
+
|
|
245
|
+
if isValidFloatValue(order.cashQty): orderProto.cashQty = order.cashQty
|
|
246
|
+
if order.mifid2DecisionMaker: orderProto.mifid2DecisionMaker = order.mifid2DecisionMaker
|
|
247
|
+
if order.mifid2DecisionAlgo: orderProto.mifid2DecisionAlgo = order.mifid2DecisionAlgo
|
|
248
|
+
if order.mifid2ExecutionTrader: orderProto.mifid2ExecutionTrader = order.mifid2ExecutionTrader
|
|
249
|
+
if order.mifid2ExecutionAlgo: orderProto.mifid2ExecutionAlgo = order.mifid2ExecutionAlgo
|
|
250
|
+
if order.dontUseAutoPriceForHedge: orderProto.dontUseAutoPriceForHedge = order.dontUseAutoPriceForHedge
|
|
251
|
+
if order.isOmsContainer: orderProto.isOmsContainer = order.isOmsContainer
|
|
252
|
+
if order.discretionaryUpToLimitPrice: orderProto.discretionaryUpToLimitPrice = order.discretionaryUpToLimitPrice
|
|
253
|
+
if order.usePriceMgmtAlgo is not None: orderProto.usePriceMgmtAlgo = 1 if order.usePriceMgmtAlgo else 0
|
|
254
|
+
if isValidIntValue(order.duration): orderProto.duration = order.duration
|
|
255
|
+
if isValidIntValue(order.postToAts): orderProto.postToAts = order.postToAts
|
|
256
|
+
if order.advancedErrorOverride: orderProto.advancedErrorOverride = order.advancedErrorOverride
|
|
257
|
+
if order.manualOrderTime: orderProto.manualOrderTime = order.manualOrderTime
|
|
258
|
+
if isValidIntValue(order.minTradeQty): orderProto.minTradeQty = order.minTradeQty
|
|
259
|
+
if isValidIntValue(order.minCompeteSize): orderProto.minCompeteSize = order.minCompeteSize
|
|
260
|
+
if isValidFloatValue(order.competeAgainstBestOffset): orderProto.competeAgainstBestOffset = order.competeAgainstBestOffset
|
|
261
|
+
if isValidFloatValue(order.midOffsetAtWhole): orderProto.midOffsetAtWhole = order.midOffsetAtWhole
|
|
262
|
+
if isValidFloatValue(order.midOffsetAtHalf): orderProto.midOffsetAtHalf = order.midOffsetAtHalf
|
|
263
|
+
if order.customerAccount: orderProto.customerAccount = order.customerAccount
|
|
264
|
+
if order.professionalCustomer: orderProto.professionalCustomer = order.professionalCustomer
|
|
265
|
+
if order.bondAccruedInterest: orderProto.bondAccruedInterest = order.bondAccruedInterest
|
|
266
|
+
if order.includeOvernight: orderProto.includeOvernight = order.includeOvernight
|
|
267
|
+
if isValidIntValue(order.manualOrderIndicator): orderProto.manualOrderIndicator = order.manualOrderIndicator
|
|
268
|
+
if order.submitter: orderProto.submitter = order.submitter
|
|
269
|
+
if order.autoCancelParent: orderProto.autoCancelParent = order.autoCancelParent
|
|
270
|
+
if order.imbalanceOnly: orderProto.imbalanceOnly = order.imbalanceOnly
|
|
271
|
+
|
|
272
|
+
return orderProto
|
|
273
|
+
|
|
274
|
+
def createConditionsProto(order: Order) -> list[OrderConditionProto]:
|
|
275
|
+
orderConditionProtoList = []
|
|
276
|
+
if order.conditions is not None and order.conditions:
|
|
277
|
+
for orderCondition in order.conditions:
|
|
278
|
+
conditionType = orderCondition.condType
|
|
279
|
+
|
|
280
|
+
if OrderCondition.Price == conditionType:
|
|
281
|
+
orderConditionProto = createPriceConditionProto(orderCondition)
|
|
282
|
+
elif OrderCondition.Time == conditionType:
|
|
283
|
+
orderConditionProto = createTimeConditionProto(orderCondition)
|
|
284
|
+
elif OrderCondition.Margin == conditionType:
|
|
285
|
+
orderConditionProto = createMarginConditionProto(orderCondition)
|
|
286
|
+
elif OrderCondition.Execution == conditionType:
|
|
287
|
+
orderConditionProto = createExecutionConditionProto(orderCondition)
|
|
288
|
+
elif OrderCondition.Volume == conditionType:
|
|
289
|
+
orderConditionProto = createVolumeConditionProto(orderCondition)
|
|
290
|
+
elif OrderCondition.PercentChange == conditionType:
|
|
291
|
+
orderConditionProto = createPercentChangeConditionProto(orderCondition)
|
|
292
|
+
|
|
293
|
+
if orderConditionProto is not None: orderConditionProtoList.append(orderConditionProto)
|
|
294
|
+
|
|
295
|
+
return orderConditionProtoList
|
|
296
|
+
|
|
297
|
+
@staticmethod
|
|
298
|
+
def createOrderConditionProto(orderCondition: OrderCondition) -> OrderConditionProto:
|
|
299
|
+
conditionType = orderCondition.condType
|
|
300
|
+
isConjunctionConnection = orderCondition.isConjunctionConnection
|
|
301
|
+
orderConditionProto = OrderConditionProto()
|
|
302
|
+
if isValidIntValue(conditionType): orderConditionProto.type = conditionType
|
|
303
|
+
orderConditionProto.isConjunctionConnection = isConjunctionConnection
|
|
304
|
+
return orderConditionProto
|
|
305
|
+
|
|
306
|
+
@staticmethod
|
|
307
|
+
def createOperatorConditionProto(operatorCondition: OperatorCondition) -> OrderConditionProto:
|
|
308
|
+
orderConditionProto = createOrderConditionProto(operatorCondition)
|
|
309
|
+
operatorConditionProto = OrderConditionProto()
|
|
310
|
+
operatorConditionProto.MergeFrom(orderConditionProto)
|
|
311
|
+
operatorConditionProto.isMore = operatorCondition.isMore
|
|
312
|
+
return operatorConditionProto
|
|
313
|
+
|
|
314
|
+
@staticmethod
|
|
315
|
+
def createContractConditionProto(contractCondition: ContractCondition) -> OrderConditionProto:
|
|
316
|
+
operatorConditionProto = createOperatorConditionProto(contractCondition)
|
|
317
|
+
contractConditionProto = OrderConditionProto()
|
|
318
|
+
contractConditionProto.MergeFrom(operatorConditionProto)
|
|
319
|
+
if isValidIntValue(contractCondition.conId): contractConditionProto.conId = contractCondition.conId
|
|
320
|
+
if contractCondition.exchange: contractConditionProto.exchange = contractCondition.exchange
|
|
321
|
+
return contractConditionProto
|
|
322
|
+
|
|
323
|
+
@staticmethod
|
|
324
|
+
def createPriceConditionProto(priceCondition: PriceCondition) -> OrderConditionProto:
|
|
325
|
+
contractConditionProto = createContractConditionProto(priceCondition)
|
|
326
|
+
priceConditionProto = OrderConditionProto()
|
|
327
|
+
priceConditionProto.MergeFrom(contractConditionProto)
|
|
328
|
+
if isValidFloatValue(priceCondition.price): priceConditionProto.price = priceCondition.price
|
|
329
|
+
if isValidIntValue(priceCondition.triggerMethod): priceConditionProto.triggerMethod = priceCondition.triggerMethod
|
|
330
|
+
return priceConditionProto
|
|
331
|
+
|
|
332
|
+
@staticmethod
|
|
333
|
+
def createTimeConditionProto(timeCondition: TimeCondition) -> OrderConditionProto:
|
|
334
|
+
operatorConditionProto = createOperatorConditionProto(timeCondition)
|
|
335
|
+
timeConditionProto = OrderConditionProto()
|
|
336
|
+
timeConditionProto.MergeFrom(operatorConditionProto)
|
|
337
|
+
if timeCondition.time: timeConditionProto.time = timeCondition.time
|
|
338
|
+
return timeConditionProto
|
|
339
|
+
|
|
340
|
+
@staticmethod
|
|
341
|
+
def createMarginConditionProto(marginCondition: MarginCondition) -> OrderConditionProto:
|
|
342
|
+
operatorConditionProto = createOperatorConditionProto(marginCondition)
|
|
343
|
+
marginConditionProto = OrderConditionProto()
|
|
344
|
+
marginConditionProto.MergeFrom(operatorConditionProto)
|
|
345
|
+
if isValidFloatValue(marginCondition.percent): marginConditionProto.percent = marginCondition.percent
|
|
346
|
+
return marginConditionProto
|
|
347
|
+
|
|
348
|
+
@staticmethod
|
|
349
|
+
def createExecutionConditionProto(executionCondition: ExecutionCondition) -> OrderConditionProto:
|
|
350
|
+
orderConditionProto = createOrderConditionProto(executionCondition)
|
|
351
|
+
executionConditionProto = OrderConditionProto()
|
|
352
|
+
executionConditionProto.MergeFrom(orderConditionProto)
|
|
353
|
+
if executionCondition.secType: executionConditionProto.secType = executionCondition.secType
|
|
354
|
+
if executionCondition.exchange: executionConditionProto.exchange = executionCondition.exchange
|
|
355
|
+
if executionCondition.symbol: executionConditionProto.symbol = executionCondition.symbol
|
|
356
|
+
return executionConditionProto
|
|
357
|
+
|
|
358
|
+
@staticmethod
|
|
359
|
+
def createVolumeConditionProto(volumeCondition: VolumeCondition) -> OrderConditionProto:
|
|
360
|
+
contractConditionProto = createContractConditionProto(volumeCondition)
|
|
361
|
+
volumeConditionProto = OrderConditionProto()
|
|
362
|
+
volumeConditionProto.MergeFrom(contractConditionProto)
|
|
363
|
+
if isValidIntValue(volumeCondition.volume): volumeConditionProto.volume = volumeCondition.volume
|
|
364
|
+
return volumeConditionProto
|
|
365
|
+
|
|
366
|
+
@staticmethod
|
|
367
|
+
def createPercentChangeConditionProto(percentChangeCondition: PercentChangeCondition) -> OrderConditionProto:
|
|
368
|
+
contractConditionProto = createContractConditionProto(percentChangeCondition)
|
|
369
|
+
percentChangeConditionProto = OrderConditionProto()
|
|
370
|
+
percentChangeConditionProto.MergeFrom(contractConditionProto)
|
|
371
|
+
if isValidFloatValue(percentChangeCondition.changePercent): percentChangeConditionProto.changePercent = percentChangeCondition.changePercent
|
|
372
|
+
return percentChangeConditionProto
|
|
373
|
+
|
|
374
|
+
@staticmethod
|
|
375
|
+
def createSoftDollarTierProto(order: Order) -> SoftDollarTierProto:
|
|
376
|
+
softDollarTierProto = None
|
|
377
|
+
tier = order.softDollarTier
|
|
378
|
+
if tier is not None:
|
|
379
|
+
softDollarTierProto = SoftDollarTierProto()
|
|
380
|
+
if tier.name: softDollarTierProto.name = tier.name
|
|
381
|
+
if tier.val: softDollarTierProto.value = tier.val
|
|
382
|
+
if tier.displayName: softDollarTierProto.displayName = tier.displayName
|
|
383
|
+
return softDollarTierProto
|
|
384
|
+
|
|
385
|
+
@staticmethod
|
|
386
|
+
def fillTagValueList(tagValueList: list, orderProtoMap: dict):
|
|
387
|
+
if tagValueList is not None and tagValueList:
|
|
388
|
+
for tagValue in tagValueList:
|
|
389
|
+
orderProtoMap[tagValue.tag] = tagValue.value
|
|
390
|
+
|
|
391
|
+
@staticmethod
|
|
392
|
+
def createCancelOrderRequestProto(orderId: int, orderCancel: OrderCancelProto) -> CancelOrderRequestProto:
|
|
393
|
+
cancelOrderRequestProto = CancelOrderRequestProto()
|
|
394
|
+
if isValidIntValue(orderId): cancelOrderRequestProto.orderId = orderId
|
|
395
|
+
orderCancelProto = createOrderCancelProto(orderCancel)
|
|
396
|
+
if orderCancelProto is not None: cancelOrderRequestProto.orderCancel.CopyFrom(orderCancelProto)
|
|
397
|
+
return cancelOrderRequestProto
|
|
398
|
+
|
|
399
|
+
@staticmethod
|
|
400
|
+
def createGlobalCancelRequestProto(orderCancel: OrderCancelProto) -> GlobalCancelRequestProto:
|
|
401
|
+
globalCancelRequestProto = GlobalCancelRequestProto()
|
|
402
|
+
orderCancelProto = createOrderCancelProto(orderCancel)
|
|
403
|
+
if orderCancelProto is not None: globalCancelRequestProto.orderCancel.CopyFrom(orderCancelProto)
|
|
404
|
+
return globalCancelRequestProto
|
|
405
|
+
|
|
406
|
+
@staticmethod
|
|
407
|
+
def createOrderCancelProto(orderCancel: OrderCancelProto) -> OrderCancelProto:
|
|
408
|
+
orderCancelProto = OrderCancelProto()
|
|
409
|
+
if orderCancel.manualOrderCancelTime: orderCancelProto.manualOrderCancelTime = orderCancel.manualOrderCancelTime
|
|
410
|
+
if orderCancel.extOperator: orderCancelProto.extOperator = orderCancel.extOperator
|
|
411
|
+
if isValidIntValue(orderCancel.manualOrderIndicator): orderCancelProto.manualOrderIndicator = orderCancel.manualOrderIndicator
|
|
412
|
+
return orderCancelProto
|
ibapi/comm.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (C) 2025 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
|
3
|
+
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
|
4
|
+
|
|
5
|
+
This module has tools for implementing the IB low level messaging.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import struct
|
|
9
|
+
import logging
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
from ibapi.const import UNSET_INTEGER, UNSET_DOUBLE, DOUBLE_INFINITY, INFINITY_STR
|
|
13
|
+
from ibapi.utils import ClientException
|
|
14
|
+
from ibapi.utils import isAsciiPrintable
|
|
15
|
+
from ibapi.errors import INVALID_SYMBOL
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
def make_msg_proto(msgId: int, protobufData: bytes) -> bytes:
|
|
20
|
+
"""adds the length prefix"""
|
|
21
|
+
byteArray = msgId.to_bytes(4, 'big') + protobufData
|
|
22
|
+
msg = struct.pack(f"!I{len(byteArray)}s", len(byteArray), byteArray)
|
|
23
|
+
return msg
|
|
24
|
+
|
|
25
|
+
def make_msg(msgId:int, useRawIntMsgId: bool, text: str) -> bytes:
|
|
26
|
+
"""adds the length prefix"""
|
|
27
|
+
if useRawIntMsgId:
|
|
28
|
+
text = msgId.to_bytes(4, 'big') + str.encode(text)
|
|
29
|
+
else:
|
|
30
|
+
text = str.encode(make_field(msgId) + text)
|
|
31
|
+
|
|
32
|
+
msg = struct.pack(f"!I{len(text)}s", len(text), text)
|
|
33
|
+
return msg
|
|
34
|
+
|
|
35
|
+
def make_initial_msg(text: str) -> bytes:
|
|
36
|
+
"""adds the length prefix"""
|
|
37
|
+
msg = struct.pack(f"!I{len(text)}s", len(text), str.encode(text))
|
|
38
|
+
return msg
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def make_field(val) -> str:
|
|
42
|
+
"""adds the NULL string terminator"""
|
|
43
|
+
if val is None:
|
|
44
|
+
raise ValueError("Cannot send None to TWS")
|
|
45
|
+
|
|
46
|
+
# if string is not empty and contains invalid symbols
|
|
47
|
+
if val is not None and type(val) == str and val and not isAsciiPrintable(val):
|
|
48
|
+
raise ClientException(
|
|
49
|
+
INVALID_SYMBOL.code(),
|
|
50
|
+
INVALID_SYMBOL.msg(),
|
|
51
|
+
val.encode(sys.stdout.encoding, errors="ignore").decode(
|
|
52
|
+
sys.stdout.encoding
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# bool type is encoded as int
|
|
57
|
+
if val is not None and type(val) == bool:
|
|
58
|
+
val = int(val)
|
|
59
|
+
|
|
60
|
+
field = str(val) + "\0"
|
|
61
|
+
return field
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def make_field_handle_empty(val) -> str:
|
|
65
|
+
if val is None:
|
|
66
|
+
raise ValueError("Cannot send None to TWS")
|
|
67
|
+
|
|
68
|
+
if UNSET_INTEGER == val or UNSET_DOUBLE == val:
|
|
69
|
+
val = ""
|
|
70
|
+
|
|
71
|
+
if DOUBLE_INFINITY == val:
|
|
72
|
+
val = INFINITY_STR
|
|
73
|
+
|
|
74
|
+
return make_field(val)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def read_msg(buf: bytes) -> tuple:
|
|
78
|
+
"""first the size prefix and then the corresponding msg payload"""
|
|
79
|
+
|
|
80
|
+
if len(buf) < 4:
|
|
81
|
+
return (0, "", buf)
|
|
82
|
+
size = struct.unpack("!I", buf[0:4])[0]
|
|
83
|
+
logger.debug("read_msg: size: %d", size)
|
|
84
|
+
if len(buf) - 4 >= size:
|
|
85
|
+
text = struct.unpack("!%ds" % size, buf[4 : 4 + size])[0]
|
|
86
|
+
return (size, text, buf[4 + size :])
|
|
87
|
+
else:
|
|
88
|
+
return (size, "", buf)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def read_fields(buf: bytes) -> tuple:
|
|
92
|
+
if isinstance(buf, str):
|
|
93
|
+
buf = buf.encode()
|
|
94
|
+
|
|
95
|
+
""" msg payload is made of fields terminated/separated by NULL chars """
|
|
96
|
+
fields = buf.split(b"\0")
|
|
97
|
+
|
|
98
|
+
return tuple(
|
|
99
|
+
fields[0:-1]
|
|
100
|
+
) # last one is empty; this may slow dow things though, TODO
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (C) 2024 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
|
|
3
|
+
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ibapi.object_implem import Object
|
|
7
|
+
from ibapi.utils import intMaxString
|
|
8
|
+
from ibapi.utils import floatMaxString
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CommissionAndFeesReport(Object):
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.execId = ""
|
|
14
|
+
self.commissionAndFees = 0.0
|
|
15
|
+
self.currency = ""
|
|
16
|
+
self.realizedPNL = 0.0
|
|
17
|
+
self.yield_ = 0.0
|
|
18
|
+
self.yieldRedemptionDate = 0 # YYYYMMDD format
|
|
19
|
+
|
|
20
|
+
def __str__(self):
|
|
21
|
+
return (
|
|
22
|
+
"ExecId: %s, CommissionAndFees: %s, Currency: %s, RealizedPnL: %s, Yield: %s, YieldRedemptionDate: %s"
|
|
23
|
+
% (
|
|
24
|
+
self.execId,
|
|
25
|
+
floatMaxString(self.commissionAndFees),
|
|
26
|
+
self.currency,
|
|
27
|
+
floatMaxString(self.realizedPNL),
|
|
28
|
+
floatMaxString(self.yield_),
|
|
29
|
+
intMaxString(self.yieldRedemptionDate),
|
|
30
|
+
)
|
|
31
|
+
)
|