├── .github ├── release-drafter.yml └── workflows │ └── release-drafter.yml ├── .gitignore ├── LICENSE ├── README.md ├── client.go ├── client_test.go ├── common.go ├── connection.go ├── connection_test.go ├── const.go ├── contract.go ├── decoder.go ├── decoder_test.go ├── errors.go ├── execution.go ├── go.mod ├── go.sum ├── order.go ├── orderCondition.go ├── scanner.go ├── utils.go ├── utils_test.go └── wrapper.go /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'Interactive Brokers API $RESOLVED_VERSION' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | categories: 4 | - title: 'Features' 5 | labels: 6 | - 'feature' 7 | - title: 'Enhancement' 8 | labels: 9 | - 'enhancement' 10 | - title: 'Bug Fixes' 11 | labels: 12 | - 'fix' 13 | - 'bugfix' 14 | - 'bug' 15 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 16 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 17 | version-resolver: 18 | major: 19 | labels: 20 | - 'major' 21 | minor: 22 | labels: 23 | - 'minor' 24 | patch: 25 | labels: 26 | - 'patch' 27 | default: patch 28 | template: | 29 | ## CONTRIBUTORS 30 | 31 | $CONTRIBUTORS -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | tags: 7 | - v* 8 | pull_request: 9 | # Only following types are handled by the action, but one can default to all as well 10 | types: [opened, reopened, synchronize] 11 | 12 | jobs: 13 | update_release_draft: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: release-drafter/release-drafter@v5 17 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 18 | # with: 19 | # config-name: my-config.yml 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | debug.test 2 | .vscode 3 | 4 | .idea 5 | .idea/* 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Hadrianl_yang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Interactive Brokers API - GoLang Implement 2 | 3 | 7 | 8 | * Interactive Brokers API 9.80 9 | * pure golang Implement 10 | * Unofficial, use at you own risk 11 | 12 | ## INSTALL 13 | 14 | `go get -u github.com/hadrianl/ibapi` 15 | 16 | --- 17 | 18 | ## USAGE 19 | 20 | Implement `IbWrapper` to handle datas delivered via tws or gateway, `Wrapper` in demo is a default implement that just output data to std. 21 | [Go to IbWrapper](https://github.com/hadrianl/ibapi/blob/83846bf1194bbdc4f039c8c66033f717e015e9fc/wrapper.go#L11) 22 | 23 | 1. **implement** your own `IbWrapper` 24 | 2. **connect** to TWS or Gateway 25 | 3. **handshake** with TWS or Gateway 26 | 4. **run** the loop 27 | 5. do some **request** 28 | 29 | ### Demo 1 30 | 31 | ```golang 32 | import ( 33 | . "github.com/hadrianl/ibapi" 34 | "time" 35 | ) 36 | 37 | func main(){ 38 | // internal api log is zap log, you could use GetLogger to get the logger 39 | // besides, you could use SetAPILogger to set you own log option 40 | // or you can just use the other logger 41 | log := GetLogger().Sugar() 42 | defer log.Sync() 43 | // implement your own IbWrapper to handle the msg delivered via tws or gateway 44 | // Wrapper{} below is a default implement which just log the msg 45 | ic := NewIbClient(&Wrapper{}) 46 | 47 | // tcp connect with tws or gateway 48 | // fail if tws or gateway had not yet set the trust IP 49 | if err := ic.Connect("127.0.0.1", 4002, 0);err != nil { 50 | log.Panic("Connect failed:", err) 51 | } 52 | 53 | // handshake with tws or gateway, send handshake protocol to tell tws or gateway the version of client 54 | // and receive the server version and connection time from tws or gateway. 55 | // fail if someone else had already connected to tws or gateway with same clientID 56 | if err := ic.HandShake();err != nil { 57 | log.Panic("HandShake failed:", err) 58 | } 59 | 60 | // make some request, msg would be delivered via wrapper. 61 | // req will not send to TWS or Gateway until ic.Run() 62 | // you could just call ic.Run() before these 63 | ic.ReqCurrentTime() 64 | ic.ReqAutoOpenOrders(true) 65 | ic.ReqAccountUpdates(true, "") 66 | ic.ReqExecutions(ic.GetReqID(), ExecutionFilter{}) 67 | 68 | // start to send req and receive msg from tws or gateway after this 69 | ic.Run() 70 | <-time.After(time.Second * 60) 71 | ic.Disconnect() 72 | } 73 | 74 | ``` 75 | 76 | ### Demo 2 with context 77 | 78 | ```golang 79 | import ( 80 | . "github.com/hadrianl/ibapi" 81 | "time" 82 | "context" 83 | ) 84 | 85 | func main(){ 86 | var err error 87 | SetAPILogger(zap.NewDevelopmentConfig()) // log is default for production(json encode, info level), set to development(console encode, debug level) here 88 | log := GetLogger().Sugar() 89 | defer log.Sync() 90 | ibwrapper := &Wrapper{} 91 | ctx, _ := context.WithTimeout(context.Background(), time.Second*30) 92 | ic := NewIbClient(ibwrapper) 93 | ic.SetContext(ctx) 94 | err = ic.Connect("127.0.0.1", 4002, 0) 95 | if err != nil { 96 | log.Panic("Connect failed:", err) 97 | } 98 | 99 | err = ic.HandShake() 100 | if err != nil { 101 | log.Panic("HandShake failed:", err) 102 | } 103 | 104 | ic.ReqCurrentTime() 105 | ic.ReqAutoOpenOrders(true) 106 | ic.ReqAccountUpdates(true, "") 107 | ic.ReqExecutions(ic.GetReqID(), ExecutionFilter{}) 108 | 109 | ic.Run() 110 | err = ic.LoopUntilDone() // block until ctx or ic is done 111 | log.Info(err) 112 | } 113 | 114 | ``` 115 | 116 | --- 117 | 118 | ## Reference 119 | 120 | 1. [Offical Document](https://interactivebrokers.github.io/tws-api/) 121 | 2. [Order Types](https://www.interactivebrokers.com/en/index.php?f=4985) 122 | 3. [Product](https://www.interactivebrokers.com/en/index.php?f=4599) 123 | 4. [Margin](https://www.interactivebrokers.com/en/index.php?f=24176) 124 | 5. [Market Data](https://www.interactivebrokers.com/en/index.php?f=14193) 125 | 6. [Commissions](https://www.interactivebrokers.com/en/index.php?f=1590) 126 | -------------------------------------------------------------------------------- /client_test.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "os" 9 | "os/signal" 10 | "runtime" 11 | "strings" 12 | "syscall" 13 | "testing" 14 | "time" 15 | 16 | "go.uber.org/zap" 17 | // "time" 18 | ) 19 | 20 | func TestClient(t *testing.T) { 21 | SetAPILogger(zap.NewDevelopmentConfig()) 22 | log := GetLogger() 23 | defer log.Sync() 24 | runtime.GOMAXPROCS(4) 25 | 26 | ic := NewIbClient(new(Wrapper)) 27 | 28 | if err := ic.Connect("localhost", 7497, 100); err != nil { 29 | log.Panic("failed to connect", zap.Error(err)) 30 | } 31 | 32 | ic.SetConnectionOptions("+PACEAPI") 33 | 34 | if err := ic.HandShake(); err != nil { 35 | log.Panic("failed to hand shake", zap.Error(err)) 36 | } 37 | ic.Run() 38 | 39 | // ####################### request base info ################################################################## 40 | ic.ReqCurrentTime() 41 | ic.ReqAutoOpenOrders(true) 42 | //ic.ReqAutoOpenOrders(false) 43 | ic.ReqAccountUpdates(true, "") 44 | //ic.ReqAccountUpdates(false, "") 45 | ic.ReqExecutions(ic.GetReqID(), ExecutionFilter{}) 46 | ic.ReqAllOpenOrders() 47 | ic.ReqPositions() 48 | // ic.CancelPositions() 49 | // ic.ReqAccountUpdatesMulti(ic.GetReqID(), "DU1382837", "", true) 50 | // ic.CancelAccountUpdatesMulti() 51 | // ic.ReqPositionsMulti(ic.GetReqID(), "DU1382837", "") 52 | // ic.CancelPositionsMulti() 53 | // tags := []string{"AccountType", "NetLiquidation", "TotalCashValue", "SettledCash", 54 | // "AccruedCash", "BuyingPower", "EquityWithLoanValue", 55 | // "PreviousEquityWithLoanValue", "GrossPositionValue", "ReqTEquity", 56 | // "ReqTMargin", "SMA", "InitMarginReq", "MaintMarginReq", "AvailableFunds", 57 | // "ExcessLiquidity", "Cushion", "FullInitMarginReq", "FullMaintMarginReq", 58 | // "FullAvailableFunds", "FullExcessLiquidity", "LookAheadNextChange", 59 | // "LookAheadInitMarginReq", "LookAheadMaintMarginReq", 60 | // "LookAheadAvailableFunds", "LookAheadExcessLiquidity", 61 | // "HighestSeverity", "DayTradesRemaining", "Leverage", "$LEDGER:ALL"} 62 | // ic.ReqAccountSummary(ic.GetReqID(), "All", strings.Join(tags, ",")) 63 | // ic.CancelAccountSummary() 64 | 65 | // ########################## request market data ################################################# 66 | // hsi := Contract{ContractID: 389657869, Symbol: "HSI", SecurityType: "FUT", Exchange: "HKFE"} 67 | // ic.ReqHistoricalData(ic.GetReqID(), &hsi, "", "4800 S", "1 min", "TRADES", false, 1, true, nil) 68 | // // ic.CancelHistoricalData() 69 | // ic.ReqMktDepth(ic.GetReqID(), &hsi, 5, true, nil) 70 | // //ic.CancelMktDepth() 71 | // ic.ReqMktData(ic.GetReqID(), &hsi, "", false, false, nil) 72 | // // ic.CancelMktData() 73 | // ic.ReqRealTimeBars(ic.GetReqID(), &hsi, 5, "TRADES", false, nil) 74 | // // ic.CancelRealTimeBars() 75 | // ic.ReqTickByTickData(ic.GetReqID(), &hsi, "Last", 5, false) 76 | // ic.CancelTickByTickData() 77 | // ic.ReqHistoricalTicks(ic.GetReqID(), &hsi, "20190916 09:15:00", "", 100, "Trades", false, false, nil) 78 | // ic.ReqHistogramData(ic.GetReqID(), &hsi, false, "3 days") 79 | // // ic.CancelHistogramData() 80 | 81 | // ############################# request combo historical data ################################################ 82 | // hsiSpread := new(Contract) 83 | // hsiSpread.Symbol = "HSI" 84 | // hsiSpread.SecurityType = "BAG" 85 | // hsiSpread.Currency = "HKD" 86 | // hsiSpread.Exchange = "HKFE" 87 | // leg1 := ComboLeg{ContractID: 389657869, Ratio: 1, Action: "BUY", Exchange: "HKFE"} 88 | // leg2 := ComboLeg{ContractID: 424418656, Ratio: 1, Action: "SELL", Exchange: "HKFE"} 89 | // hsiSpread.ComboLegs = append(hsiSpread.ComboLegs, leg1, leg2) 90 | // ic.ReqHistoricalData(ic.GetReqID(), hsiSpread, "", "4800 S", "1 min", "TRADES", false, 1, false, nil) 91 | 92 | // ######################### request contract ############################################################ 93 | // hsi := Contract{Symbol: "HSI", SecurityType: "FUT", Exchange: "HKFE"} 94 | // ic.ReqContractDetails(ic.GetReqID(), &hsi) 95 | // ic.ReqMatchingSymbols(ic.GetReqID(), "IB") 96 | 97 | // ######################### market scanner ############################################################# 98 | // ic.ReqScannerParameters() 99 | // scanSub := new(ScannerSubscription) 100 | // scanSub.Instrument = "FUT.HK" 101 | // scanSub.LocationCode = "FUT.HK" 102 | // scanSub.ScanCode = "HOT_BY_VOLUME" 103 | // // t1 := TagValue{"usdMarketCapAbove", "10000"} 104 | // // t2 := TagValue{"optVolumeAbove", "1000"} 105 | // // t3 := TagValue{"avgVolumeAbove", "100000000"} 106 | // ic.ReqScannerSubscription(ic.GetReqID(), scanSub, nil, nil) 107 | // // ic.CancelScannerSubscription() 108 | 109 | // ############################### display group ######################################################## 110 | // ic.QueryDisplayGroups(ic.GetReqID()) 111 | // subGroupID := ic.GetReqID() 112 | // ic.SubscribeToGroupEvents(subGroupID, 4) 113 | // ic.UpdateDisplayGroup(subGroupID, "389657869@HKFE") 114 | // // ic.UnsubscribeFromGroupEvents(subGroupID) 115 | 116 | // ############################ others ######################################################################### 117 | 118 | // ic.ReqFamilyCodes() 119 | // ic.ReqScannerParameters() 120 | // ic.ReqManagedAccts() 121 | // ic.ReqSoftDollarTiers(ic.GetReqID()) 122 | // ic.ReqNewsProviders() 123 | // ic.ReqMarketDataType(1) 124 | // ic.ReqPnLSingle(ic.GetReqID(), "DU1382837", "", 351872027) 125 | // ic.ReqNewsBulletins(true) 126 | // ic.ReqSmartComponents(ic.GetReqID(), "a6") 127 | // ic.ReqMktDepthExchanges() 128 | // ic.ReqMatchingSymbols(ic.GetReqID(), "HSI") 129 | // ic.ReqSecDefOptParams(ic.GetReqID(), "HSI", "", "IND", 1328298) 130 | // ic.ReqGlobalCancel() 131 | // ic.ReqIDs() 132 | ic.ReqCompletedOrders(false) 133 | // lmtOrder := NewLimitOrder("BUY", 26640, 1) 134 | // mktOrder := NewMarketOrder("BUY", 1) 135 | // ic.PlaceOrder(ibwrapper.GetNextOrderID(), &hsi1909, lmtOrder) 136 | // ic.CancelOrder(ibwrapper.OrderID() - 1) 137 | // ic.ReqHeadTimeStamp(ic.GetReqID(), &hsi, "TRADES", false, 1) 138 | // ic.ReqNewsBulletins(true) 139 | // ic.ReqFundamentalData() 140 | 141 | ic.LoopUntilDone( 142 | func() { 143 | <-time.After(time.Second * 25) 144 | ic.Disconnect() 145 | }) 146 | // loop: 147 | // for { 148 | // select { 149 | // case <-time.After(time.Second * 60): 150 | // ic.Disconnect() 151 | // break loop 152 | // } 153 | // } 154 | 155 | } 156 | 157 | func TestClientReconnect(t *testing.T) { 158 | log, _ = zap.NewDevelopment() // log is default for production(json encode, info level), set to development(console encode, debug level) here 159 | defer log.Sync() 160 | runtime.GOMAXPROCS(4) 161 | 162 | ic := NewIbClient(new(Wrapper)) 163 | 164 | for { 165 | if err := ic.Connect("localhost", 4002, 0); err != nil { 166 | log.Error("failed to connect, reconnect after 5 sec", zap.Error(err)) 167 | time.Sleep(5 * time.Second) 168 | continue 169 | } 170 | 171 | ic.SetConnectionOptions("+PACEAPI") 172 | 173 | if err := ic.HandShake(); err != nil { 174 | log.Error("failed to hand shake, reconnect after 5 sec", zap.Error(err)) 175 | time.Sleep(5 * time.Second) 176 | continue 177 | } 178 | ic.Run() 179 | ic.LoopUntilDone(func() { 180 | <-time.After(25 * time.Second) // block 25 sec and disconnect 181 | ic.Disconnect() 182 | }) 183 | } 184 | 185 | } 186 | 187 | func TestClientWithContext(t *testing.T) { 188 | // log.SetLevel(log.DebugLevel) 189 | runtime.GOMAXPROCS(4) 190 | var err error 191 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*30000) 192 | sigs := make(chan os.Signal, 1) 193 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 194 | ibwrapper := new(Wrapper) 195 | ic := NewIbClient(ibwrapper) 196 | ic.SetContext(ctx) 197 | err = ic.Connect("localhost", 7497, 0) 198 | if err != nil { 199 | log.Panic("failed to connect", zap.Error(err)) 200 | } 201 | 202 | ic.SetConnectionOptions("+PACEAPI") 203 | err = ic.HandShake() 204 | if err != nil { 205 | log.Panic("failed to hand shake", zap.Error(err)) 206 | } 207 | ic.Run() 208 | 209 | ic.ReqCurrentTime() 210 | // ic.ReqAutoOpenOrders(true) 211 | ic.ReqAccountUpdates(true, "") 212 | // ic.ReqExecutions(ic.GetReqID(), ExecutionFilter{}) 213 | 214 | hsi := Contract{ContractID: 500007591, Symbol: "HSI", SecurityType: "FUT", Exchange: "HKFE"} 215 | // hsi := Contract{Symbol: "HSI", SecurityType: "FUT", Exchange: "HKFE"} 216 | // ic.ReqMktDepth(ic.GetReqID(), &hsi1909, 5, true, nil) 217 | ic.ReqContractDetails(ic.GetReqID(), &hsi) 218 | // ic.ReqAllOpenOrders() 219 | ic.ReqMktData(ic.GetReqID(), &hsi, "", false, false, nil) 220 | // ic.ReqPositions() 221 | // ic.ReqRealTimeBars(ic.GetReqID(), &hsi1909, 5, "TRADES", false, nil) 222 | 223 | tags := []string{"AccountType", "NetLiquidation", "TotalCashValue", "SettledCash", 224 | "AccruedCash", "BuyingPower", "EquityWithLoanValue", 225 | "PreviousEquityWithLoanValue", "GrossPositionValue", "ReqTEquity", 226 | "ReqTMargin", "SMA", "InitMarginReq", "MaintMarginReq", "AvailableFunds", 227 | "ExcessLiquidity", "Cushion", "FullInitMarginReq", "FullMaintMarginReq", 228 | "FullAvailableFunds", "FullExcessLiquidity", "LookAheadNextChange", 229 | "LookAheadInitMarginReq", "LookAheadMaintMarginReq", 230 | "LookAheadAvailableFunds", "LookAheadExcessLiquidity", 231 | "HighestSeverity", "DayTradesRemaining", "Leverage", "$LEDGER:ALL"} 232 | ic.ReqAccountSummary(ic.GetReqID(), "All", strings.Join(tags, ",")) 233 | 234 | ic.ReqHistoricalData(ic.GetReqID(), &hsi, "", "4800 S", "1 min", "TRADES", false, 1, true, nil) 235 | 236 | pprofServe := func() { 237 | http.ListenAndServe("localhost:6060", nil) 238 | } 239 | 240 | go pprofServe() 241 | 242 | f := func() { 243 | sig := <-sigs 244 | fmt.Print(sig) 245 | cancel() 246 | } 247 | 248 | err = ic.LoopUntilDone(f) 249 | fmt.Println(err) 250 | 251 | } 252 | 253 | func BenchmarkPlaceOrder(b *testing.B) { 254 | ibwrapper := new(Wrapper) 255 | ic := NewIbClient(ibwrapper) 256 | ic.setConnState(2) 257 | ic.serverVersion = 151 258 | contract := new(Contract) 259 | order := new(Order) 260 | 261 | go func() { 262 | for { 263 | <-ic.reqChan 264 | } 265 | }() 266 | 267 | for i := 0; i < b.N; i++ { 268 | ic.PlaceOrder(1, contract, order) 269 | } 270 | } 271 | 272 | func BenchmarkAppendEmptySlice(b *testing.B) { 273 | arr := []byte("benchmark test of append and copy") 274 | for i := 0; i < b.N; i++ { 275 | _ = append([]byte{}, arr...) 276 | } 277 | } 278 | 279 | func BenchmarkCopySlice(b *testing.B) { 280 | arr := []byte("benchmark test of append and copy") 281 | for i := 0; i < b.N; i++ { 282 | oarr := arr 283 | newSlice := make([]byte, len(oarr)) 284 | copy(newSlice, oarr) 285 | _ = newSlice 286 | } 287 | } 288 | 289 | func TestPlaceOrder(t *testing.T) { 290 | SetAPILogger(zap.NewDevelopmentConfig()) 291 | log := GetLogger() 292 | defer log.Sync() 293 | runtime.GOMAXPROCS(4) 294 | var err error 295 | 296 | ibwrapper := new(Wrapper) 297 | ic := NewIbClient(ibwrapper) 298 | 299 | err = ic.Connect("localhost", 7497, 0) 300 | if err != nil { 301 | log.Panic("failed to connect", zap.Error(err)) 302 | } 303 | 304 | ic.SetConnectionOptions("+PACEAPI") 305 | err = ic.HandShake() 306 | if err != nil { 307 | log.Panic("failed to hand shake", zap.Error(err)) 308 | } 309 | ic.Run() 310 | 311 | ic.ReqCurrentTime() 312 | // ic.ReqAutoOpenOrders(true) 313 | ic.ReqAccountUpdates(true, "") 314 | // ic.ReqExecutions(ic.GetReqID(), ExecutionFilter{}) 315 | 316 | // hsi := Contract{ContractID: 500007591, Symbol: "HSI", SecurityType: "FUT", Exchange: "HKFE"} 317 | aapl := Contract{ContractID: 265598, Symbol: "AAPL", SecurityType: "STK", Exchange: "NYSE"} 318 | ic.ReqContractDetails(ic.GetReqID(), &aapl) 319 | 320 | ic.ReqMktData(ic.GetReqID(), &aapl, "", false, false, nil) 321 | 322 | lmtOrder := NewLimitOrder("BUY", 144, 1) 323 | // mktOrder := NewMarketOrder("BUY", 1) 324 | ic.PlaceOrder(ibwrapper.GetNextOrderID(), &aapl, lmtOrder) 325 | 326 | ic.LoopUntilDone( 327 | func() { 328 | <-time.After(time.Second * 25) 329 | ic.Disconnect() 330 | }) 331 | 332 | fmt.Println(err) 333 | 334 | } 335 | -------------------------------------------------------------------------------- /common.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Account ... 8 | type Account struct { 9 | Name string 10 | } 11 | 12 | // TickAttrib describes additional information for price ticks 13 | type TickAttrib struct { 14 | CanAutoExecute bool 15 | PastLimit bool 16 | PreOpen bool 17 | } 18 | 19 | func (t TickAttrib) String() string { 20 | return fmt.Sprintf("TickAttrib", 21 | t.CanAutoExecute, 22 | t.PastLimit, 23 | t.PreOpen) 24 | } 25 | 26 | // AlgoParams ... 27 | type AlgoParams struct { 28 | } 29 | 30 | // TagValue ... 31 | type TagValue struct { 32 | Tag string 33 | Value string 34 | } 35 | 36 | func (tv TagValue) String() string { 37 | return fmt.Sprintf("TagValue<%s=%s>", tv.Tag, tv.Value) 38 | } 39 | 40 | // OrderComboLeg ... 41 | type OrderComboLeg struct { 42 | Price float64 `default:"UNSETFLOAT"` 43 | } 44 | 45 | func (o OrderComboLeg) String() string { 46 | return fmt.Sprintf("OrderComboLeg;", o.Price) 47 | } 48 | 49 | // ------------ComboLeg-------------------- 50 | 51 | // ComboLegOpenClose ... 52 | type ComboLegOpenClose int64 53 | 54 | // ComboLegShortSaleSlot ... 55 | type ComboLegShortSaleSlot int64 56 | 57 | const ( 58 | SAME_POS ComboLegOpenClose = 0 59 | OPEN_POS = 1 60 | CLOSE_POS = 2 61 | UNKNOWN_POS = 3 62 | ClearingBroker ComboLegShortSaleSlot = 1 63 | ThirdParty = 2 64 | ) 65 | 66 | // ComboLeg ... 67 | type ComboLeg struct { 68 | ContractID int64 69 | Ratio int64 70 | Action string 71 | Exchange string 72 | OpenClose int64 73 | 74 | // for stock legs when doing short sale 75 | ShortSaleSlot int64 76 | DesignatedLocation string 77 | ExemptCode int64 `default:"-1"` 78 | } 79 | 80 | func (c ComboLeg) String() string { 81 | return fmt.Sprintf("ComboLeg<%d, %d, %s, %s, %d, %d, %s, %d>", 82 | c.ContractID, 83 | c.Ratio, 84 | c.Action, 85 | c.Exchange, 86 | c.OpenClose, 87 | c.ShortSaleSlot, 88 | c.DesignatedLocation, 89 | c.ExemptCode) 90 | } 91 | 92 | // ----------------------------------------------------- 93 | 94 | // ExecutionFilter ... 95 | type ExecutionFilter struct { 96 | ClientID int64 97 | AccountCode string 98 | Time string 99 | Symbol string 100 | SecurityType string 101 | Exchange string 102 | Side string 103 | } 104 | 105 | // BarData ... 106 | type BarData struct { 107 | Date string 108 | Open float64 109 | High float64 110 | Low float64 111 | Close float64 112 | Volume float64 113 | BarCount int64 114 | Average float64 115 | } 116 | 117 | func (b BarData) String() string { 118 | return fmt.Sprintf("BarData", 119 | b.Date, 120 | b.Open, 121 | b.High, 122 | b.Low, 123 | b.Close, 124 | b.Volume, 125 | b.Average, 126 | b.BarCount) 127 | } 128 | 129 | // RealTimeBar ... 130 | type RealTimeBar struct { 131 | Time int64 132 | endTime int64 133 | Open float64 134 | High float64 135 | Low float64 136 | Close float64 137 | Volume int64 138 | Wap float64 139 | Count int64 140 | } 141 | 142 | func (rb RealTimeBar) String() string { 143 | return fmt.Sprintf("RealTimeBar", 144 | rb.Time, 145 | rb.Open, 146 | rb.High, 147 | rb.Low, 148 | rb.Close, 149 | rb.Volume, 150 | rb.Wap, 151 | rb.Count) 152 | } 153 | 154 | // CommissionReport ... 155 | type CommissionReport struct { 156 | ExecID string 157 | Commission float64 158 | Currency string 159 | RealizedPNL float64 160 | Yield float64 161 | YieldRedemptionDate int64 //YYYYMMDD 162 | } 163 | 164 | func (cr CommissionReport) String() string { 165 | return fmt.Sprintf("CommissionReport", 166 | cr.ExecID, 167 | cr.Commission, 168 | cr.Currency, 169 | cr.RealizedPNL, 170 | cr.Yield, 171 | cr.YieldRedemptionDate) 172 | } 173 | 174 | // FamilyCode ... 175 | type FamilyCode struct { 176 | AccountID string 177 | FamilyCode string 178 | } 179 | 180 | func (f FamilyCode) String() string { 181 | return fmt.Sprintf("FamilyCode", 182 | f.AccountID, 183 | f.FamilyCode) 184 | } 185 | 186 | // SmartComponent ... 187 | type SmartComponent struct { 188 | BitNumber int64 189 | Exchange string 190 | ExchangeLetter string 191 | } 192 | 193 | func (s SmartComponent) String() string { 194 | return fmt.Sprintf("SmartComponent", 195 | s.BitNumber, 196 | s.Exchange, 197 | s.ExchangeLetter) 198 | } 199 | 200 | // DepthMktDataDescription ... 201 | type DepthMktDataDescription struct { 202 | Exchange string 203 | SecurityType string 204 | ListingExchange string 205 | ServiceDataType string 206 | AggGroup int64 `default:"UNSETINT"` 207 | } 208 | 209 | // DepthMktDataDescription ... 210 | func (d DepthMktDataDescription) String() string { 211 | aggGroup := "" 212 | if d.AggGroup != UNSETINT { 213 | aggGroup = fmt.Sprint(d.AggGroup) 214 | } 215 | 216 | return fmt.Sprintf("DepthMktDataDescription", 217 | d.Exchange, 218 | d.SecurityType, 219 | d.ListingExchange, 220 | d.ServiceDataType, 221 | aggGroup) 222 | } 223 | 224 | // NewsProvider ... 225 | type NewsProvider struct { 226 | Code string 227 | Name string 228 | } 229 | 230 | func (np NewsProvider) String() string { 231 | return fmt.Sprintf("NewsProvider", 232 | np.Code, 233 | np.Name) 234 | } 235 | 236 | // HistogramData ... 237 | type HistogramData struct { 238 | Price float64 239 | Count int64 240 | } 241 | 242 | func (hgd HistogramData) String() string { 243 | return fmt.Sprintf("HistogramData", 244 | hgd.Price, 245 | hgd.Count) 246 | } 247 | 248 | // PriceIncrement ... 249 | type PriceIncrement struct { 250 | LowEdge float64 251 | Increment float64 252 | } 253 | 254 | func (p PriceIncrement) String() string { 255 | return fmt.Sprintf("PriceIncrement", 256 | p.LowEdge, 257 | p.Increment) 258 | } 259 | 260 | // HistoricalTick is the historical tick's description. 261 | // Used when requesting historical tick data with whatToShow = MIDPOINT 262 | type HistoricalTick struct { 263 | Time int64 264 | Price float64 265 | Size int64 266 | } 267 | 268 | func (h HistoricalTick) String() string { 269 | return fmt.Sprintf("Tick", 270 | h.Time, 271 | h.Price, 272 | h.Size) 273 | } 274 | 275 | // HistoricalTickBidAsk is the historical tick's description. 276 | // Used when requesting historical tick data with whatToShow = BID_ASK 277 | type HistoricalTickBidAsk struct { 278 | Time int64 279 | TickAttirbBidAsk TickAttribBidAsk 280 | PriceBid float64 281 | PriceAsk float64 282 | SizeBid int64 283 | SizeAsk int64 284 | } 285 | 286 | func (h HistoricalTickBidAsk) String() string { 287 | return fmt.Sprintf("TickBidAsk", 288 | h.Time, 289 | h.TickAttirbBidAsk, 290 | h.PriceBid, 291 | h.PriceAsk, 292 | h.SizeBid, 293 | h.SizeAsk) 294 | } 295 | 296 | // TickAttribBidAsk ... 297 | type TickAttribBidAsk struct { 298 | BidPastLow bool 299 | AskPastHigh bool 300 | } 301 | 302 | func (t TickAttribBidAsk) String() string { 303 | return fmt.Sprintf("TickAttribBidAsk", 304 | t.BidPastLow, 305 | t.AskPastHigh) 306 | } 307 | 308 | // HistoricalTickLast is the historical last tick's description. 309 | // Used when requesting historical tick data with whatToShow = TRADES 310 | type HistoricalTickLast struct { 311 | Time int64 312 | TickAttribLast TickAttribLast 313 | Price float64 314 | Size int64 315 | Exchange string 316 | SpecialConditions string 317 | } 318 | 319 | func (h HistoricalTickLast) String() string { 320 | return fmt.Sprintf("TickLast", 321 | h.Time, 322 | h.TickAttribLast, 323 | h.Price, 324 | h.Size, 325 | h.Exchange, 326 | h.SpecialConditions) 327 | } 328 | 329 | // TickAttribLast ... 330 | type TickAttribLast struct { 331 | PastLimit bool 332 | Unreported bool 333 | } 334 | 335 | func (t TickAttribLast) String() string { 336 | return fmt.Sprintf("TickAttribLast", 337 | t.PastLimit, 338 | t.Unreported) 339 | } 340 | -------------------------------------------------------------------------------- /connection.go: -------------------------------------------------------------------------------- 1 | /* connection handle the tcp socket to the TWS or IB Gateway*/ 2 | 3 | package ibapi 4 | 5 | import ( 6 | "net" 7 | "strconv" 8 | 9 | "go.uber.org/zap" 10 | ) 11 | 12 | // IbConnection wrap the tcp connection with TWS or Gateway 13 | type IbConnection struct { 14 | *net.TCPConn 15 | host string 16 | port int 17 | clientID int64 18 | state int 19 | numBytesSent int 20 | numMsgSent int 21 | numBytesRecv int 22 | numMsgRecv int 23 | } 24 | 25 | func (ibconn *IbConnection) Write(bs []byte) (int, error) { 26 | n, err := ibconn.TCPConn.Write(bs) 27 | 28 | ibconn.numBytesSent += n 29 | ibconn.numMsgSent++ 30 | 31 | log.Debug("conn write", zap.Int("nBytes", n)) 32 | 33 | return n, err 34 | } 35 | 36 | func (ibconn *IbConnection) Read(bs []byte) (int, error) { 37 | n, err := ibconn.TCPConn.Read(bs) 38 | 39 | ibconn.numBytesRecv += n 40 | ibconn.numMsgRecv++ 41 | 42 | log.Debug("conn read", zap.Int("nBytes", n)) 43 | 44 | return n, err 45 | } 46 | 47 | func (ibconn *IbConnection) setState(state int) { 48 | ibconn.state = state 49 | } 50 | 51 | func (ibconn *IbConnection) reset() { 52 | ibconn.numBytesSent = 0 53 | ibconn.numBytesRecv = 0 54 | ibconn.numMsgSent = 0 55 | ibconn.numMsgRecv = 0 56 | } 57 | 58 | func (ibconn *IbConnection) disconnect() error { 59 | log.Debug("conn disconnect", 60 | zap.Int("nMsgSent", ibconn.numMsgSent), 61 | zap.Int("nBytesSent", ibconn.numBytesSent), 62 | zap.Int("nMsgRecv", ibconn.numMsgRecv), 63 | zap.Int("nBytesRecv", ibconn.numBytesRecv), 64 | ) 65 | return ibconn.Close() 66 | } 67 | 68 | func (ibconn *IbConnection) connect(host string, port int) error { 69 | var err error 70 | var addr *net.TCPAddr 71 | ibconn.host = host 72 | ibconn.port = port 73 | ibconn.reset() 74 | 75 | server := ibconn.host + ":" + strconv.Itoa(port) 76 | if addr, err = net.ResolveTCPAddr("tcp4", server); err != nil { 77 | log.Error("failed to resove tcp address", zap.Error(err), zap.String("host", server)) 78 | return err 79 | } 80 | 81 | if ibconn.TCPConn, err = net.DialTCP("tcp4", nil, addr); err != nil { 82 | log.Error("failed to dial tcp", zap.Error(err), zap.Any("address", addr)) 83 | return err 84 | } 85 | 86 | log.Debug("tcp socket connected", zap.Any("address", ibconn.TCPConn.RemoteAddr())) 87 | 88 | return err 89 | } 90 | -------------------------------------------------------------------------------- /connection_test.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "go.uber.org/zap" 8 | ) 9 | 10 | func TestConnection(t *testing.T) { 11 | fmt.Println("connection testing!") 12 | conn := &IbConnection{} 13 | conn.connect("127.0.0.1", 4002) 14 | buf := make([]byte, 4096) 15 | _, err := conn.Read(buf) 16 | if err != nil { 17 | log.Panic("read error", zap.Error(err)) 18 | } 19 | fmt.Println(string(buf)) 20 | conn.disconnect() 21 | } 22 | -------------------------------------------------------------------------------- /const.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | // IN identifies the msg type of the buf received from TWS or Gateway 4 | type IN = int64 5 | 6 | // OUT identifies the msg type of the buf sended to TWS or Gateway 7 | type OUT = int64 8 | type FiledType = int64 9 | type Version = int 10 | 11 | const ( 12 | INT FiledType = 1 13 | STR FiledType = 2 14 | FLT FiledType = 3 15 | ) 16 | 17 | const ( 18 | mTICK_PRICE IN = 1 19 | mTICK_SIZE IN = 2 20 | mORDER_STATUS IN = 3 21 | mERR_MSG IN = 4 22 | mOPEN_ORDER IN = 5 23 | mACCT_VALUE IN = 6 24 | mPORTFOLIO_VALUE IN = 7 25 | mACCT_UPDATE_TIME IN = 8 26 | mNEXT_VALID_ID IN = 9 27 | mCONTRACT_DATA IN = 10 28 | mEXECUTION_DATA IN = 11 29 | mMARKET_DEPTH IN = 12 30 | mMARKET_DEPTH_L2 IN = 13 31 | mNEWS_BULLETINS IN = 14 32 | mMANAGED_ACCTS IN = 15 33 | mRECEIVE_FA IN = 16 34 | mHISTORICAL_DATA IN = 17 35 | mBOND_CONTRACT_DATA IN = 18 36 | mSCANNER_PARAMETERS IN = 19 37 | mSCANNER_DATA IN = 20 38 | mTICK_OPTION_COMPUTATION IN = 21 39 | mTICK_GENERIC IN = 45 40 | mTICK_STRING IN = 46 41 | mTICK_EFP IN = 47 42 | mCURRENT_TIME IN = 49 43 | mREAL_TIME_BARS IN = 50 44 | mFUNDAMENTAL_DATA IN = 51 45 | mCONTRACT_DATA_END IN = 52 46 | mOPEN_ORDER_END IN = 53 47 | mACCT_DOWNLOAD_END IN = 54 48 | mEXECUTION_DATA_END IN = 55 49 | mDELTA_NEUTRAL_VALIDATION IN = 56 50 | mTICK_SNAPSHOT_END IN = 57 51 | mMARKET_DATA_TYPE IN = 58 52 | mCOMMISSION_REPORT IN = 59 53 | mPOSITION_DATA IN = 61 54 | mPOSITION_END IN = 62 55 | mACCOUNT_SUMMARY IN = 63 56 | mACCOUNT_SUMMARY_END IN = 64 57 | mVERIFY_MESSAGE_API IN = 65 58 | mVERIFY_COMPLETED IN = 66 59 | mDISPLAY_GROUP_LIST IN = 67 60 | mDISPLAY_GROUP_UPDATED IN = 68 61 | mVERIFY_AND_AUTH_MESSAGE_API IN = 69 62 | mVERIFY_AND_AUTH_COMPLETED IN = 70 63 | mPOSITION_MULTI IN = 71 64 | mPOSITION_MULTI_END IN = 72 65 | mACCOUNT_UPDATE_MULTI IN = 73 66 | mACCOUNT_UPDATE_MULTI_END IN = 74 67 | mSECURITY_DEFINITION_OPTION_PARAMETER IN = 75 68 | mSECURITY_DEFINITION_OPTION_PARAMETER_END IN = 76 69 | mSOFT_DOLLAR_TIERS IN = 77 70 | mFAMILY_CODES IN = 78 71 | mSYMBOL_SAMPLES IN = 79 72 | mMKT_DEPTH_EXCHANGES IN = 80 73 | mTICK_REQ_PARAMS IN = 81 74 | mSMART_COMPONENTS IN = 82 75 | mNEWS_ARTICLE IN = 83 76 | mTICK_NEWS IN = 84 77 | mNEWS_PROVIDERS IN = 85 78 | mHISTORICAL_NEWS IN = 86 79 | mHISTORICAL_NEWS_END IN = 87 80 | mHEAD_TIMESTAMP IN = 88 81 | mHISTOGRAM_DATA IN = 89 82 | mHISTORICAL_DATA_UPDATE IN = 90 83 | mREROUTE_MKT_DATA_REQ IN = 91 84 | mREROUTE_MKT_DEPTH_REQ IN = 92 85 | mMARKET_RULE IN = 93 86 | mPNL IN = 94 87 | mPNL_SINGLE IN = 95 88 | mHISTORICAL_TICKS IN = 96 89 | mHISTORICAL_TICKS_BID_ASK IN = 97 90 | mHISTORICAL_TICKS_LAST IN = 98 91 | mTICK_BY_TICK IN = 99 92 | mORDER_BOUND IN = 100 93 | mCOMPLETED_ORDER IN = 101 94 | mCOMPLETED_ORDERS_END IN = 102 95 | mREPLACE_FA_END IN = 103 96 | mWSH_META_DATA IN = 104 97 | mWSH_EVENT_DATA IN = 105 98 | ) 99 | 100 | const ( 101 | mREQ_MKT_DATA OUT = 1 102 | mCANCEL_MKT_DATA OUT = 2 103 | mPLACE_ORDER OUT = 3 104 | mCANCEL_ORDER OUT = 4 105 | mREQ_OPEN_ORDERS OUT = 5 106 | mREQ_ACCT_DATA OUT = 6 107 | mREQ_EXECUTIONS OUT = 7 108 | mREQ_IDS OUT = 8 109 | mREQ_CONTRACT_DATA OUT = 9 110 | mREQ_MKT_DEPTH OUT = 10 111 | mCANCEL_MKT_DEPTH OUT = 11 112 | mREQ_NEWS_BULLETINS OUT = 12 113 | mCANCEL_NEWS_BULLETINS OUT = 13 114 | mSET_SERVER_LOGLEVEL OUT = 14 115 | mREQ_AUTO_OPEN_ORDERS OUT = 15 116 | mREQ_ALL_OPEN_ORDERS OUT = 16 117 | mREQ_MANAGED_ACCTS OUT = 17 118 | mREQ_FA OUT = 18 119 | mREPLACE_FA OUT = 19 120 | mREQ_HISTORICAL_DATA OUT = 20 121 | mEXERCISE_OPTIONS OUT = 21 122 | mREQ_SCANNER_SUBSCRIPTION OUT = 22 123 | mCANCEL_SCANNER_SUBSCRIPTION OUT = 23 124 | mREQ_SCANNER_PARAMETERS OUT = 24 125 | mCANCEL_HISTORICAL_DATA OUT = 25 126 | mREQ_CURRENT_TIME OUT = 49 127 | mREQ_REAL_TIME_BARS OUT = 50 128 | mCANCEL_REAL_TIME_BARS OUT = 51 129 | mREQ_FUNDAMENTAL_DATA OUT = 52 130 | mCANCEL_FUNDAMENTAL_DATA OUT = 53 131 | mREQ_CALC_IMPLIED_VOLAT OUT = 54 132 | mREQ_CALC_OPTION_PRICE OUT = 55 133 | mCANCEL_CALC_IMPLIED_VOLAT OUT = 56 134 | mCANCEL_CALC_OPTION_PRICE OUT = 57 135 | mREQ_GLOBAL_CANCEL OUT = 58 136 | mREQ_MARKET_DATA_TYPE OUT = 59 137 | mREQ_POSITIONS OUT = 61 138 | mREQ_ACCOUNT_SUMMARY OUT = 62 139 | mCANCEL_ACCOUNT_SUMMARY OUT = 63 140 | mCANCEL_POSITIONS OUT = 64 141 | mVERIFY_REQUEST OUT = 65 142 | mVERIFY_MESSAGE OUT = 66 143 | mQUERY_DISPLAY_GROUPS OUT = 67 144 | mSUBSCRIBE_TO_GROUP_EVENTS OUT = 68 145 | mUPDATE_DISPLAY_GROUP OUT = 69 146 | mUNSUBSCRIBE_FROM_GROUP_EVENTS OUT = 70 147 | mSTART_API OUT = 71 148 | mVERIFY_AND_AUTH_REQUEST OUT = 72 149 | mVERIFY_AND_AUTH_MESSAGE OUT = 73 150 | mREQ_POSITIONS_MULTI OUT = 74 151 | mCANCEL_POSITIONS_MULTI OUT = 75 152 | mREQ_ACCOUNT_UPDATES_MULTI OUT = 76 153 | mCANCEL_ACCOUNT_UPDATES_MULTI OUT = 77 154 | mREQ_SEC_DEF_OPT_PARAMS OUT = 78 155 | mREQ_SOFT_DOLLAR_TIERS OUT = 79 156 | mREQ_FAMILY_CODES OUT = 80 157 | mREQ_MATCHING_SYMBOLS OUT = 81 158 | mREQ_MKT_DEPTH_EXCHANGES OUT = 82 159 | mREQ_SMART_COMPONENTS OUT = 83 160 | mREQ_NEWS_ARTICLE OUT = 84 161 | mREQ_NEWS_PROVIDERS OUT = 85 162 | mREQ_HISTORICAL_NEWS OUT = 86 163 | mREQ_HEAD_TIMESTAMP OUT = 87 164 | mREQ_HISTOGRAM_DATA OUT = 88 165 | mCANCEL_HISTOGRAM_DATA OUT = 89 166 | mCANCEL_HEAD_TIMESTAMP OUT = 90 167 | mREQ_MARKET_RULE OUT = 91 168 | mREQ_PNL OUT = 92 169 | mCANCEL_PNL OUT = 93 170 | mREQ_PNL_SINGLE OUT = 94 171 | mCANCEL_PNL_SINGLE OUT = 95 172 | mREQ_HISTORICAL_TICKS OUT = 96 173 | mREQ_TICK_BY_TICK_DATA OUT = 97 174 | mCANCEL_TICK_BY_TICK_DATA OUT = 98 175 | mREQ_COMPLETED_ORDERS OUT = 99 176 | mREQ_WSH_META_DATA OUT = 100 177 | mCANCEL_WSH_META_DATA OUT = 101 178 | mREQ_WSH_EVENT_DATA OUT = 102 179 | mCANCEL_WSH_EVENT_DATA OUT = 103 180 | ) 181 | 182 | const ( 183 | // mMIN_SERVER_VER_REAL_TIME_BARS = 34 184 | // mMIN_SERVER_VER_SCALE_ORDERS = 35 185 | // mMIN_SERVER_VER_SNAPSHOT_MKT_DATA = 35 186 | // mMIN_SERVER_VER_SSHORT_COMBO_LEGS = 35 187 | // mMIN_SERVER_VER_WHAT_IF_ORDERS = 36 188 | // mMIN_SERVER_VER_CONTRACT_CONID = 37 189 | mMIN_SERVER_VER_PTA_ORDERS Version = 39 190 | mMIN_SERVER_VER_FUNDAMENTAL_DATA Version = 40 191 | mMIN_SERVER_VER_DELTA_NEUTRAL Version = 40 192 | mMIN_SERVER_VER_CONTRACT_DATA_CHAIN Version = 40 193 | mMIN_SERVER_VER_SCALE_ORDERS2 Version = 40 194 | mMIN_SERVER_VER_ALGO_ORDERS Version = 41 195 | mMIN_SERVER_VER_EXECUTION_DATA_CHAIN Version = 42 196 | mMIN_SERVER_VER_NOT_HELD Version = 44 197 | mMIN_SERVER_VER_SEC_ID_TYPE Version = 45 198 | mMIN_SERVER_VER_PLACE_ORDER_CONID Version = 46 199 | mMIN_SERVER_VER_REQ_MKT_DATA_CONID Version = 47 200 | mMIN_SERVER_VER_REQ_CALC_IMPLIED_VOLAT Version = 49 201 | mMIN_SERVER_VER_REQ_CALC_OPTION_PRICE Version = 50 202 | mMIN_SERVER_VER_SSHORTX_OLD Version = 51 203 | mMIN_SERVER_VER_SSHORTX Version = 52 204 | mMIN_SERVER_VER_REQ_GLOBAL_CANCEL Version = 53 205 | mMIN_SERVER_VER_HEDGE_ORDERS Version = 54 206 | mMIN_SERVER_VER_REQ_MARKET_DATA_TYPE Version = 55 207 | mMIN_SERVER_VER_OPT_OUT_SMART_ROUTING Version = 56 208 | mMIN_SERVER_VER_SMART_COMBO_ROUTING_PARAMS Version = 57 209 | mMIN_SERVER_VER_DELTA_NEUTRAL_CONID Version = 58 210 | mMIN_SERVER_VER_SCALE_ORDERS3 Version = 60 211 | mMIN_SERVER_VER_ORDER_COMBO_LEGS_PRICE Version = 61 212 | mMIN_SERVER_VER_TRAILING_PERCENT Version = 62 213 | mMIN_SERVER_VER_DELTA_NEUTRAL_OPEN_CLOSE Version = 66 214 | mMIN_SERVER_VER_POSITIONS Version = 67 215 | mMIN_SERVER_VER_ACCOUNT_SUMMARY Version = 67 216 | mMIN_SERVER_VER_TRADING_CLASS Version = 68 217 | mMIN_SERVER_VER_SCALE_TABLE Version = 69 218 | mMIN_SERVER_VER_LINKING Version = 70 219 | mMIN_SERVER_VER_ALGO_ID Version = 71 220 | mMIN_SERVER_VER_OPTIONAL_CAPABILITIES Version = 72 221 | mMIN_SERVER_VER_ORDER_SOLICITED Version = 73 222 | mMIN_SERVER_VER_LINKING_AUTH Version = 74 223 | mMIN_SERVER_VER_PRIMARYEXCH Version = 75 224 | mMIN_SERVER_VER_RANDOMIZE_SIZE_AND_PRICE Version = 76 225 | mMIN_SERVER_VER_FRACTIONAL_POSITIONS Version = 101 226 | mMIN_SERVER_VER_PEGGED_TO_BENCHMARK Version = 102 227 | mMIN_SERVER_VER_MODELS_SUPPORT Version = 103 228 | mMIN_SERVER_VER_SEC_DEF_OPT_PARAMS_REQ Version = 104 229 | mMIN_SERVER_VER_EXT_OPERATOR Version = 105 230 | mMIN_SERVER_VER_SOFT_DOLLAR_TIER Version = 106 231 | mMIN_SERVER_VER_REQ_FAMILY_CODES Version = 107 232 | mMIN_SERVER_VER_REQ_MATCHING_SYMBOLS Version = 108 233 | mMIN_SERVER_VER_PAST_LIMIT Version = 109 234 | mMIN_SERVER_VER_MD_SIZE_MULTIPLIER Version = 110 235 | mMIN_SERVER_VER_CASH_QTY Version = 111 236 | mMIN_SERVER_VER_REQ_MKT_DEPTH_EXCHANGES Version = 112 237 | mMIN_SERVER_VER_TICK_NEWS Version = 113 238 | mMIN_SERVER_VER_REQ_SMART_COMPONENTS Version = 114 239 | mMIN_SERVER_VER_REQ_NEWS_PROVIDERS Version = 115 240 | mMIN_SERVER_VER_REQ_NEWS_ARTICLE Version = 116 241 | mMIN_SERVER_VER_REQ_HISTORICAL_NEWS Version = 117 242 | mMIN_SERVER_VER_REQ_HEAD_TIMESTAMP Version = 118 243 | mMIN_SERVER_VER_REQ_HISTOGRAM Version = 119 244 | mMIN_SERVER_VER_SERVICE_DATA_TYPE Version = 120 245 | mMIN_SERVER_VER_AGG_GROUP Version = 121 246 | mMIN_SERVER_VER_UNDERLYING_INFO Version = 122 247 | mMIN_SERVER_VER_CANCEL_HEADTIMESTAMP Version = 123 248 | mMIN_SERVER_VER_SYNT_REALTIME_BARS Version = 124 249 | mMIN_SERVER_VER_CFD_REROUTE Version = 125 250 | mMIN_SERVER_VER_MARKET_RULES Version = 126 251 | mMIN_SERVER_VER_PNL Version = 127 252 | mMIN_SERVER_VER_NEWS_QUERY_ORIGINS Version = 128 253 | mMIN_SERVER_VER_UNREALIZED_PNL Version = 129 254 | mMIN_SERVER_VER_HISTORICAL_TICKS Version = 130 255 | mMIN_SERVER_VER_MARKET_CAP_PRICE Version = 131 256 | mMIN_SERVER_VER_PRE_OPEN_BID_ASK Version = 132 257 | mMIN_SERVER_VER_REAL_EXPIRATION_DATE Version = 134 258 | mMIN_SERVER_VER_REALIZED_PNL Version = 135 259 | mMIN_SERVER_VER_LAST_LIQUIDITY Version = 136 260 | mMIN_SERVER_VER_TICK_BY_TICK Version = 137 261 | mMIN_SERVER_VER_DECISION_MAKER Version = 138 262 | mMIN_SERVER_VER_MIFID_EXECUTION Version = 139 263 | mMIN_SERVER_VER_TICK_BY_TICK_IGNORE_SIZE Version = 140 264 | mMIN_SERVER_VER_AUTO_PRICE_FOR_HEDGE Version = 141 265 | mMIN_SERVER_VER_WHAT_IF_EXT_FIELDS Version = 142 266 | mMIN_SERVER_VER_SCANNER_GENERIC_OPTS Version = 143 267 | mMIN_SERVER_VER_API_BIND_ORDER Version = 144 268 | mMIN_SERVER_VER_ORDER_CONTAINER Version = 145 269 | mMIN_SERVER_VER_SMART_DEPTH Version = 146 270 | mMIN_SERVER_VER_REMOVE_NULL_ALL_CASTING Version = 147 271 | mMIN_SERVER_VER_D_PEG_ORDERS Version = 148 272 | mMIN_SERVER_VER_MKT_DEPTH_PRIM_EXCHANGE Version = 149 273 | mMIN_SERVER_VER_COMPLETED_ORDERS Version = 150 274 | mMIN_SERVER_VER_PRICE_MGMT_ALGO Version = 151 275 | mMIN_SERVER_VER_STOCK_TYPE Version = 152 276 | mMIN_SERVER_VER_ENCODE_MSG_ASCII7 Version = 153 277 | mMIN_SERVER_VER_SEND_ALL_FAMILY_CODES Version = 154 278 | mMIN_SERVER_VER_NO_DEFAULT_OPEN_CLOSE Version = 155 279 | mMIN_SERVER_VER_PRICE_BASED_VOLATILITY Version = 156 280 | mMIN_SERVER_VER_REPLACE_FA_END Version = 157 281 | mMIN_SERVER_VER_DURATION Version = 158 282 | mMIN_SERVER_VER_MARKET_DATA_IN_SHARES Version = 159 283 | mMIN_SERVER_VER_POST_TO_ATS Version = 160 284 | mMIN_SERVER_VER_WSHE_CALENDAR Version = 161 285 | 286 | MIN_CLIENT_VER Version = 100 287 | MAX_CLIENT_VER Version = mMIN_SERVER_VER_WSHE_CALENDAR 288 | ) 289 | 290 | // tick const 291 | const ( 292 | BID_SIZE = iota 293 | BID 294 | ASK 295 | ASK_SIZE 296 | LAST 297 | LAST_SIZE 298 | HIGH 299 | LOW 300 | VOLUME 301 | CLOSE 302 | BID_OPTION_COMPUTATION 303 | ASK_OPTION_COMPUTATION 304 | LAST_OPTION_COMPUTATION 305 | MODEL_OPTION 306 | OPEN 307 | LOW_13_WEEK 308 | HIGH_13_WEEK 309 | LOW_26_WEEK 310 | HIGH_26_WEEK 311 | LOW_52_WEEK 312 | HIGH_52_WEEK 313 | AVG_VOLUME 314 | OPEN_INTEREST 315 | OPTION_HISTORICAL_VOL 316 | OPTION_IMPLIED_VOL 317 | OPTION_BID_EXCH 318 | OPTION_ASK_EXCH 319 | OPTION_CALL_OPEN_INTEREST 320 | OPTION_PUT_OPEN_INTEREST 321 | OPTION_CALL_VOLUME 322 | OPTION_PUT_VOLUME 323 | INDEX_FUTURE_PREMIUM 324 | BID_EXCH 325 | ASK_EXCH 326 | AUCTION_VOLUME 327 | AUCTION_PRICE 328 | AUCTION_IMBALANCE 329 | MARK_PRICE 330 | BID_EFP_COMPUTATION 331 | ASK_EFP_COMPUTATION 332 | LAST_EFP_COMPUTATION 333 | OPEN_EFP_COMPUTATION 334 | HIGH_EFP_COMPUTATION 335 | LOW_EFP_COMPUTATION 336 | CLOSE_EFP_COMPUTATION 337 | LAST_TIMESTAMP 338 | SHORTABLE 339 | FUNDAMENTAL_RATIOS 340 | RT_VOLUME 341 | HALTED 342 | BID_YIELD 343 | ASK_YIELD 344 | LAST_YIELD 345 | CUST_OPTION_COMPUTATION 346 | TRADE_COUNT 347 | TRADE_RATE 348 | VOLUME_RATE 349 | LAST_RTH_TRADE 350 | RT_HISTORICAL_VOL 351 | IB_DIVIDENDS 352 | BOND_FACTOR_MULTIPLIER 353 | REGULATORY_IMBALANCE 354 | NEWS_TICK 355 | SHORT_TERM_VOLUME_3_MIN 356 | SHORT_TERM_VOLUME_5_MIN 357 | SHORT_TERM_VOLUME_10_MIN 358 | DELAYED_BID 359 | DELAYED_ASK 360 | DELAYED_LAST 361 | DELAYED_BID_SIZE 362 | DELAYED_ASK_SIZE 363 | DELAYED_LAST_SIZE 364 | DELAYED_HIGH 365 | DELAYED_LOW 366 | DELAYED_VOLUME 367 | DELAYED_CLOSE 368 | DELAYED_OPEN 369 | RT_TRD_VOLUME 370 | CREDITMAN_MARK_PRICE 371 | CREDITMAN_SLOW_MARK_PRICE 372 | DELAYED_BID_OPTION 373 | DELAYED_ASK_OPTION 374 | DELAYED_LAST_OPTION 375 | DELAYED_MODEL_OPTION 376 | LAST_EXCH 377 | LAST_REG_TIME 378 | FUTURES_OPEN_INTEREST 379 | AVG_OPT_VOLUME 380 | DELAYED_LAST_TIMESTAMP 381 | SHORTABLE_SHARES 382 | NOT_SET 383 | ) 384 | 385 | // ConnectionState 386 | const ( 387 | DISCONNECTED = iota 388 | CONNECTING 389 | CONNECTED 390 | REDIRECT 391 | ) 392 | -------------------------------------------------------------------------------- /contract.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import "fmt" 4 | 5 | //Contract describes an instrument's definition 6 | type Contract struct { 7 | ContractID int64 8 | Symbol string 9 | SecurityType string 10 | Expiry string 11 | Strike float64 12 | Right string 13 | Multiplier string 14 | Exchange string 15 | Currency string 16 | LocalSymbol string 17 | TradingClass string 18 | PrimaryExchange string // pick an actual (ie non-aggregate) exchange that the contract trades on. DO NOT SET TO SMART. 19 | IncludeExpired bool 20 | SecurityIDType string // CUSIP;SEDOL;ISIN;RIC 21 | SecurityID string 22 | 23 | // combos les 24 | ComboLegsDescription string 25 | ComboLegs []ComboLeg 26 | // UnderComp *UnderComp 27 | 28 | DeltaNeutralContract *DeltaNeutralContract 29 | } 30 | 31 | func (c Contract) String() string { 32 | basicStr := fmt.Sprintf("Contract", c.Expiry, c.Multiplier, c.SecurityID, c.SecurityIDType) 42 | case "OPT": 43 | basicStr += fmt.Sprintf(", Expiry: %s, Strike: %f, Right: %s, SecurityID: %s, SecurityIDType: %s>", c.Expiry, c.Strike, c.Right, c.SecurityID, c.SecurityIDType) 44 | default: 45 | basicStr += fmt.Sprint(">") 46 | } 47 | 48 | for i, leg := range c.ComboLegs { 49 | basicStr += fmt.Sprintf("-Leg<%d>: %s", i, leg) 50 | } 51 | 52 | if c.DeltaNeutralContract != nil { 53 | basicStr += fmt.Sprintf("-%s", c.DeltaNeutralContract) 54 | } 55 | 56 | return basicStr 57 | } 58 | 59 | // DeltaNeutralContract is Delta-Neutral Contract 60 | type DeltaNeutralContract struct { 61 | ContractID int64 62 | Delta float64 63 | Price float64 64 | } 65 | 66 | func (c DeltaNeutralContract) String() string { 67 | return fmt.Sprintf("DeltaNeutralContract", 68 | c.ContractID, 69 | c.Delta, 70 | c.Price) 71 | } 72 | 73 | // ContractDetails contain a Contract and other details about this contract, can be request by ReqContractDetails 74 | type ContractDetails struct { 75 | Contract Contract 76 | MarketName string 77 | MinTick float64 78 | OrderTypes string 79 | ValidExchanges string 80 | PriceMagnifier int64 81 | 82 | UnderContractID int64 83 | LongName string 84 | ContractMonth string 85 | Industry string 86 | Category string 87 | Subcategory string 88 | TimezoneID string 89 | TradingHours string 90 | LiquidHours string 91 | EVRule string 92 | EVMultiplier int64 93 | MdSizeMultiplier int64 94 | AggGroup int64 95 | UnderSymbol string 96 | UnderSecurityType string 97 | MarketRuleIDs string 98 | SecurityIDList []TagValue 99 | RealExpirationDate string 100 | LastTradeTime string 101 | StockType string 102 | 103 | // BOND values 104 | Cusip string 105 | Ratings string 106 | DescAppend string 107 | BondType string 108 | CouponType string 109 | Callable bool 110 | Putable bool 111 | Coupon int64 112 | Convertible bool 113 | Maturity string 114 | IssueDate string 115 | NextOptionDate string 116 | NextOptionType string 117 | NextOptionPartial bool 118 | Notes string 119 | } 120 | 121 | func (c ContractDetails) String() string { 122 | return fmt.Sprintf("ContractDetails", c.Contract, c.MarketName, c.UnderContractID, c.LongName) 123 | } 124 | 125 | // ContractDescription includes contract and DerivativeSecTypes 126 | type ContractDescription struct { 127 | Contract Contract 128 | DerivativeSecTypes []string 129 | } 130 | 131 | // NewComboLeg create a default comboleg 132 | func NewComboLeg() ComboLeg { 133 | comboLeg := ComboLeg{} 134 | comboLeg.ExemptCode = -1 135 | return comboLeg 136 | } 137 | -------------------------------------------------------------------------------- /decoder.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | "go.uber.org/zap" 8 | ) 9 | 10 | const ( 11 | TIME_FORMAT string = "2006-01-02 15:04:05 +0700 CST" 12 | ) 13 | 14 | // ibDecoder help to decode the msg bytes received from TWS or Gateway 15 | type ibDecoder struct { 16 | wrapper IbWrapper 17 | version Version 18 | msgID2process map[IN]func(*MsgBuffer) 19 | // errChan chan error 20 | } 21 | 22 | func (d *ibDecoder) setVersion(version Version) { 23 | d.version = version 24 | } 25 | 26 | func (d *ibDecoder) setWrapper(w IbWrapper) { 27 | d.wrapper = w 28 | } 29 | 30 | func (d *ibDecoder) interpret(msgBytes []byte) { 31 | msgBuf := NewMsgBuffer(msgBytes) 32 | if msgBuf.Len() == 0 { 33 | log.Debug("no fields") 34 | return 35 | } 36 | 37 | // try not to handle the error produced by decoder and wrapper, user should be responsible for this 38 | // // if decode error ocours,handle the error 39 | // defer func() { 40 | // if err := recover(); err != nil { 41 | // log.Error("failed to decode", zap.Error(err.(error))) 42 | // d.errChan <- err.(error) 43 | // } 44 | // }() 45 | 46 | // log.Debug("interpret", zap.Binary("MsgBytes", msgBuf.Bytes())) 47 | 48 | // read the msg type 49 | MsgID := msgBuf.readInt() 50 | if processer, ok := d.msgID2process[MsgID]; ok { 51 | processer(msgBuf) 52 | } else { 53 | log.Warn("msg ID not found!!!", zap.Int64("msgID", MsgID), zap.Binary("MsgBytes", msgBuf.Bytes())) 54 | } 55 | 56 | } 57 | 58 | // func (d *ibDecoder) interpretWithSignature(fs [][]byte, processer interface{}) { 59 | // if processer == nil { 60 | // fmt.Println("No processer") 61 | // } 62 | 63 | // processerType := reflect.TypeOf(processer) 64 | // params := make([]interface{}, processerType.NumIn()) 65 | // for i, f := range fs[1:] { 66 | // switch processerType.In(i).Kind() { 67 | // case reflect.Int: 68 | // param := strconv.Atoi(string(f)) 69 | // case reflect.Float64: 70 | // param, _ := strconv.ParseFloat(string(f), 64) 71 | // default: 72 | // param := string(f) 73 | // } 74 | // params = append(params, param) 75 | 76 | // } 77 | 78 | // processer(params...) 79 | // } 80 | 81 | func (d *ibDecoder) setmsgID2process() { 82 | d.msgID2process = map[IN]func(*MsgBuffer){ 83 | mTICK_PRICE: d.processTickPriceMsg, 84 | mTICK_SIZE: d.wrapTickSize, 85 | mORDER_STATUS: d.processOrderStatusMsg, 86 | mERR_MSG: d.wrapError, 87 | mOPEN_ORDER: d.processOpenOrder, 88 | mACCT_VALUE: d.wrapUpdateAccountValue, 89 | mPORTFOLIO_VALUE: d.processPortfolioValueMsg, 90 | mACCT_UPDATE_TIME: d.wrapUpdateAccountTime, 91 | mNEXT_VALID_ID: d.wrapNextValidID, 92 | mCONTRACT_DATA: d.processContractDataMsg, 93 | mEXECUTION_DATA: d.processExecutionDataMsg, 94 | mMARKET_DEPTH: d.wrapUpdateMktDepth, 95 | mMARKET_DEPTH_L2: d.wrapUpdateMktDepthL2, 96 | mNEWS_BULLETINS: d.wrapUpdateNewsBulletin, 97 | mMANAGED_ACCTS: d.wrapManagedAccounts, 98 | mRECEIVE_FA: d.wrapReceiveFA, 99 | mHISTORICAL_DATA: d.processHistoricalDataMsg, 100 | mHISTORICAL_DATA_UPDATE: d.processHistoricalDataUpdateMsg, 101 | mBOND_CONTRACT_DATA: d.processBondContractDataMsg, 102 | mSCANNER_PARAMETERS: d.wrapScannerParameters, 103 | mSCANNER_DATA: d.processScannerDataMsg, 104 | mTICK_OPTION_COMPUTATION: d.processTickOptionComputationMsg, 105 | mTICK_GENERIC: d.wrapTickGeneric, 106 | mTICK_STRING: d.wrapTickString, 107 | mTICK_EFP: d.wrapTickEFP, 108 | mCURRENT_TIME: d.wrapCurrentTime, 109 | mREAL_TIME_BARS: d.processRealTimeBarMsg, 110 | mFUNDAMENTAL_DATA: d.wrapFundamentalData, 111 | mCONTRACT_DATA_END: d.wrapContractDetailsEnd, 112 | 113 | mACCT_DOWNLOAD_END: d.wrapAccountDownloadEnd, 114 | mOPEN_ORDER_END: d.wrapOpenOrderEnd, 115 | mEXECUTION_DATA_END: d.wrapExecDetailsEnd, 116 | mDELTA_NEUTRAL_VALIDATION: d.processDeltaNeutralValidationMsg, 117 | mTICK_SNAPSHOT_END: d.wrapTickSnapshotEnd, 118 | mMARKET_DATA_TYPE: d.wrapMarketDataType, 119 | mCOMMISSION_REPORT: d.processCommissionReportMsg, 120 | mPOSITION_DATA: d.processPositionDataMsg, 121 | mPOSITION_END: d.wrapPositionEnd, 122 | mACCOUNT_SUMMARY: d.wrapAccountSummary, 123 | mACCOUNT_SUMMARY_END: d.wrapAccountSummaryEnd, 124 | mVERIFY_MESSAGE_API: d.wrapVerifyMessageAPI, 125 | mVERIFY_COMPLETED: d.wrapVerifyCompleted, 126 | mDISPLAY_GROUP_LIST: d.wrapDisplayGroupList, 127 | mDISPLAY_GROUP_UPDATED: d.wrapDisplayGroupUpdated, 128 | mVERIFY_AND_AUTH_MESSAGE_API: d.wrapVerifyAndAuthMessageAPI, 129 | mVERIFY_AND_AUTH_COMPLETED: d.wrapVerifyAndAuthCompleted, 130 | mPOSITION_MULTI: d.processPositionMultiMsg, 131 | mPOSITION_MULTI_END: d.wrapPositionMultiEnd, 132 | mACCOUNT_UPDATE_MULTI: d.wrapAccountUpdateMulti, 133 | mACCOUNT_UPDATE_MULTI_END: d.wrapAccountUpdateMultiEnd, 134 | mSECURITY_DEFINITION_OPTION_PARAMETER: d.processSecurityDefinitionOptionParameterMsg, 135 | mSECURITY_DEFINITION_OPTION_PARAMETER_END: d.wrapSecurityDefinitionOptionParameterEndMsg, 136 | mSOFT_DOLLAR_TIERS: d.processSoftDollarTiersMsg, 137 | mFAMILY_CODES: d.processFamilyCodesMsg, 138 | mSYMBOL_SAMPLES: d.processSymbolSamplesMsg, 139 | mSMART_COMPONENTS: d.processSmartComponents, 140 | mTICK_REQ_PARAMS: d.processTickReqParams, 141 | mMKT_DEPTH_EXCHANGES: d.processMktDepthExchanges, 142 | mHEAD_TIMESTAMP: d.processHeadTimestamp, 143 | mTICK_NEWS: d.processTickNews, 144 | mNEWS_PROVIDERS: d.processNewsProviders, 145 | mNEWS_ARTICLE: d.processNewsArticle, 146 | mHISTORICAL_NEWS: d.processHistoricalNews, 147 | mHISTORICAL_NEWS_END: d.processHistoricalNewsEnd, 148 | mHISTOGRAM_DATA: d.processHistogramData, 149 | mREROUTE_MKT_DATA_REQ: d.processRerouteMktDataReq, 150 | mREROUTE_MKT_DEPTH_REQ: d.processRerouteMktDepthReq, 151 | mMARKET_RULE: d.processMarketRuleMsg, 152 | mPNL: d.processPnLMsg, 153 | mPNL_SINGLE: d.processPnLSingleMsg, 154 | mHISTORICAL_TICKS: d.processHistoricalTicks, 155 | mHISTORICAL_TICKS_BID_ASK: d.processHistoricalTicksBidAsk, 156 | mHISTORICAL_TICKS_LAST: d.processHistoricalTicksLast, 157 | mTICK_BY_TICK: d.processTickByTickMsg, 158 | mORDER_BOUND: d.processOrderBoundMsg, 159 | mCOMPLETED_ORDER: d.processCompletedOrderMsg, 160 | mCOMPLETED_ORDERS_END: d.processCompletedOrdersEndMsg, 161 | mREPLACE_FA_END: d.processReplaceFAEndMsg, 162 | mWSH_META_DATA: d.processWshMetaDataMsg, 163 | mWSH_EVENT_DATA: d.processWshEventDataMsg, 164 | } 165 | } 166 | 167 | func (d *ibDecoder) wrapTickSize(msgBuf *MsgBuffer) { 168 | _ = msgBuf.readString() 169 | reqID := msgBuf.readInt() 170 | tickType := msgBuf.readInt() 171 | size := msgBuf.readInt() 172 | d.wrapper.TickSize(reqID, tickType, size) 173 | } 174 | 175 | func (d *ibDecoder) wrapNextValidID(msgBuf *MsgBuffer) { 176 | _ = msgBuf.readString() 177 | reqID := msgBuf.readInt() 178 | d.wrapper.NextValidID(reqID) 179 | 180 | } 181 | 182 | func (d *ibDecoder) wrapManagedAccounts(msgBuf *MsgBuffer) { 183 | _ = msgBuf.readString() 184 | accNames := msgBuf.readString() 185 | accsList := strings.Split(accNames, ",") 186 | d.wrapper.ManagedAccounts(accsList) 187 | 188 | } 189 | 190 | func (d *ibDecoder) wrapUpdateAccountValue(msgBuf *MsgBuffer) { 191 | _ = msgBuf.readString() 192 | tag := msgBuf.readString() 193 | val := msgBuf.readString() 194 | currency := msgBuf.readString() 195 | accName := msgBuf.readString() 196 | 197 | d.wrapper.UpdateAccountValue(tag, val, currency, accName) 198 | } 199 | 200 | func (d *ibDecoder) wrapUpdateAccountTime(msgBuf *MsgBuffer) { 201 | _ = msgBuf.readString() 202 | ts := msgBuf.readString() 203 | today := time.Now() 204 | // time. 205 | t, err := time.ParseInLocation("04:05", ts, time.Local) 206 | if err != nil { 207 | log.Panic("failed to parse account time", zap.Error(err)) 208 | } 209 | t = t.AddDate(today.Year(), int(today.Month())-1, today.Day()-1) 210 | 211 | d.wrapper.UpdateAccountTime(t) 212 | } 213 | 214 | func (d *ibDecoder) wrapError(msgBuf *MsgBuffer) { 215 | _ = msgBuf.readString() 216 | reqID := msgBuf.readInt() 217 | errorCode := msgBuf.readInt() 218 | errorString := msgBuf.readString() 219 | 220 | d.wrapper.Error(reqID, errorCode, errorString) 221 | } 222 | 223 | func (d *ibDecoder) wrapCurrentTime(msgBuf *MsgBuffer) { 224 | _ = msgBuf.readString() 225 | ts := msgBuf.readInt() 226 | t := time.Unix(ts, 0) 227 | 228 | d.wrapper.CurrentTime(t) 229 | } 230 | 231 | func (d *ibDecoder) wrapUpdateMktDepth(msgBuf *MsgBuffer) { 232 | _ = msgBuf.readString() 233 | reqID := msgBuf.readInt() 234 | position := msgBuf.readInt() 235 | operation := msgBuf.readInt() 236 | side := msgBuf.readInt() 237 | price := msgBuf.readFloat() 238 | size := msgBuf.readInt() 239 | 240 | d.wrapper.UpdateMktDepth(reqID, position, operation, side, price, size) 241 | 242 | } 243 | 244 | func (d *ibDecoder) wrapUpdateMktDepthL2(msgBuf *MsgBuffer) { 245 | _ = msgBuf.readString() 246 | reqID := msgBuf.readInt() 247 | position := msgBuf.readInt() 248 | marketMaker := msgBuf.readString() 249 | operation := msgBuf.readInt() 250 | side := msgBuf.readInt() 251 | price := msgBuf.readFloat() 252 | size := msgBuf.readInt() 253 | isSmartDepth := msgBuf.readBool() 254 | 255 | d.wrapper.UpdateMktDepthL2(reqID, position, marketMaker, operation, side, price, size, isSmartDepth) 256 | 257 | } 258 | 259 | func (d *ibDecoder) wrapUpdateNewsBulletin(msgBuf *MsgBuffer) { 260 | _ = msgBuf.readString() 261 | msgID := msgBuf.readInt() 262 | msgType := msgBuf.readInt() 263 | newsMessage := msgBuf.readString() 264 | originExch := msgBuf.readString() 265 | 266 | d.wrapper.UpdateNewsBulletin(msgID, msgType, newsMessage, originExch) 267 | } 268 | 269 | func (d *ibDecoder) wrapReceiveFA(msgBuf *MsgBuffer) { 270 | _ = msgBuf.readString() 271 | faData := msgBuf.readInt() 272 | cxml := msgBuf.readString() 273 | 274 | d.wrapper.ReceiveFA(faData, cxml) 275 | } 276 | 277 | func (d *ibDecoder) wrapScannerParameters(msgBuf *MsgBuffer) { 278 | _ = msgBuf.readString() 279 | xml := msgBuf.readString() 280 | 281 | d.wrapper.ScannerParameters(xml) 282 | } 283 | 284 | func (d *ibDecoder) wrapTickGeneric(msgBuf *MsgBuffer) { 285 | _ = msgBuf.readString() 286 | reqID := msgBuf.readInt() 287 | tickType := msgBuf.readInt() 288 | value := msgBuf.readFloat() 289 | 290 | d.wrapper.TickGeneric(reqID, tickType, value) 291 | 292 | } 293 | 294 | func (d *ibDecoder) wrapTickString(msgBuf *MsgBuffer) { 295 | _ = msgBuf.readString() 296 | reqID := msgBuf.readInt() 297 | tickType := msgBuf.readInt() 298 | value := msgBuf.readString() 299 | 300 | d.wrapper.TickString(reqID, tickType, value) 301 | 302 | } 303 | 304 | func (d *ibDecoder) wrapTickEFP(msgBuf *MsgBuffer) { 305 | _ = msgBuf.readString() 306 | reqID := msgBuf.readInt() 307 | tickType := msgBuf.readInt() 308 | basisPoints := msgBuf.readFloat() 309 | formattedBasisPoints := msgBuf.readString() 310 | totalDividends := msgBuf.readFloat() 311 | holdDays := msgBuf.readInt() 312 | futureLastTradeDate := msgBuf.readString() 313 | dividendImpact := msgBuf.readFloat() 314 | dividendsToLastTradeDate := msgBuf.readFloat() 315 | 316 | d.wrapper.TickEFP(reqID, tickType, basisPoints, formattedBasisPoints, totalDividends, holdDays, futureLastTradeDate, dividendImpact, dividendsToLastTradeDate) 317 | 318 | } 319 | 320 | func (d *ibDecoder) wrapMarketDataType(msgBuf *MsgBuffer) { 321 | _ = msgBuf.readString() 322 | reqID := msgBuf.readInt() 323 | marketDataType := msgBuf.readInt() 324 | 325 | d.wrapper.MarketDataType(reqID, marketDataType) 326 | } 327 | 328 | func (d *ibDecoder) wrapAccountSummary(msgBuf *MsgBuffer) { 329 | _ = msgBuf.readString() 330 | reqID := msgBuf.readInt() 331 | account := msgBuf.readString() 332 | tag := msgBuf.readString() 333 | value := msgBuf.readString() 334 | currency := msgBuf.readString() 335 | 336 | d.wrapper.AccountSummary(reqID, account, tag, value, currency) 337 | } 338 | 339 | func (d *ibDecoder) wrapVerifyMessageAPI(msgBuf *MsgBuffer) { 340 | // Deprecated Function: keep it temporarily, not know how it works 341 | _ = msgBuf.readString() 342 | apiData := msgBuf.readString() 343 | 344 | d.wrapper.VerifyMessageAPI(apiData) 345 | } 346 | 347 | func (d *ibDecoder) wrapVerifyCompleted(msgBuf *MsgBuffer) { 348 | _ = msgBuf.readString() 349 | isSuccessful := msgBuf.readBool() 350 | err := msgBuf.readString() 351 | 352 | d.wrapper.VerifyCompleted(isSuccessful, err) 353 | } 354 | 355 | func (d *ibDecoder) wrapDisplayGroupList(msgBuf *MsgBuffer) { 356 | _ = msgBuf.readString() 357 | reqID := msgBuf.readInt() 358 | groups := msgBuf.readString() 359 | 360 | d.wrapper.DisplayGroupList(reqID, groups) 361 | } 362 | 363 | func (d *ibDecoder) wrapDisplayGroupUpdated(msgBuf *MsgBuffer) { 364 | _ = msgBuf.readString() 365 | reqID := msgBuf.readInt() 366 | contractInfo := msgBuf.readString() 367 | 368 | d.wrapper.DisplayGroupUpdated(reqID, contractInfo) 369 | } 370 | 371 | func (d *ibDecoder) wrapVerifyAndAuthMessageAPI(msgBuf *MsgBuffer) { 372 | _ = msgBuf.readString() 373 | apiData := msgBuf.readString() 374 | xyzChallange := msgBuf.readString() 375 | 376 | d.wrapper.VerifyAndAuthMessageAPI(apiData, xyzChallange) 377 | } 378 | 379 | func (d *ibDecoder) wrapVerifyAndAuthCompleted(msgBuf *MsgBuffer) { 380 | _ = msgBuf.readString() 381 | isSuccessful := msgBuf.readBool() 382 | err := msgBuf.readString() 383 | 384 | d.wrapper.VerifyAndAuthCompleted(isSuccessful, err) 385 | } 386 | 387 | func (d *ibDecoder) wrapAccountUpdateMulti(msgBuf *MsgBuffer) { 388 | _ = msgBuf.readString() 389 | reqID := msgBuf.readInt() 390 | acc := msgBuf.readString() 391 | modelCode := msgBuf.readString() 392 | tag := msgBuf.readString() 393 | val := msgBuf.readString() 394 | currency := msgBuf.readString() 395 | 396 | d.wrapper.AccountUpdateMulti(reqID, acc, modelCode, tag, val, currency) 397 | } 398 | 399 | func (d *ibDecoder) wrapFundamentalData(msgBuf *MsgBuffer) { 400 | _ = msgBuf.readString() 401 | reqID := msgBuf.readInt() 402 | data := msgBuf.readString() 403 | 404 | d.wrapper.FundamentalData(reqID, data) 405 | } 406 | 407 | //--------------wrap end func --------------------------------- 408 | 409 | func (d *ibDecoder) wrapAccountDownloadEnd(msgBuf *MsgBuffer) { 410 | _ = msgBuf.readString() 411 | accName := msgBuf.readString() 412 | 413 | d.wrapper.AccountDownloadEnd(accName) 414 | } 415 | 416 | func (d *ibDecoder) wrapOpenOrderEnd(msgBuf *MsgBuffer) { 417 | 418 | d.wrapper.OpenOrderEnd() 419 | } 420 | 421 | func (d *ibDecoder) wrapExecDetailsEnd(msgBuf *MsgBuffer) { 422 | _ = msgBuf.readString() 423 | reqID := msgBuf.readInt() 424 | 425 | d.wrapper.ExecDetailsEnd(reqID) 426 | } 427 | 428 | func (d *ibDecoder) wrapTickSnapshotEnd(msgBuf *MsgBuffer) { 429 | _ = msgBuf.readString() 430 | reqID := msgBuf.readInt() 431 | 432 | d.wrapper.TickSnapshotEnd(reqID) 433 | } 434 | 435 | func (d *ibDecoder) wrapPositionEnd(msgBuf *MsgBuffer) { 436 | 437 | d.wrapper.PositionEnd() 438 | } 439 | 440 | func (d *ibDecoder) wrapAccountSummaryEnd(msgBuf *MsgBuffer) { 441 | _ = msgBuf.readString() 442 | reqID := msgBuf.readInt() 443 | 444 | d.wrapper.AccountSummaryEnd(reqID) 445 | } 446 | 447 | func (d *ibDecoder) wrapPositionMultiEnd(msgBuf *MsgBuffer) { 448 | _ = msgBuf.readString() 449 | reqID := msgBuf.readInt() 450 | 451 | d.wrapper.PositionMultiEnd(reqID) 452 | } 453 | 454 | func (d *ibDecoder) wrapAccountUpdateMultiEnd(msgBuf *MsgBuffer) { 455 | _ = msgBuf.readString() 456 | reqID := msgBuf.readInt() 457 | 458 | d.wrapper.AccountUpdateMultiEnd(reqID) 459 | } 460 | 461 | func (d *ibDecoder) wrapSecurityDefinitionOptionParameterEndMsg(msgBuf *MsgBuffer) { 462 | reqID := msgBuf.readInt() 463 | 464 | d.wrapper.SecurityDefinitionOptionParameterEnd(reqID) 465 | } 466 | 467 | func (d *ibDecoder) wrapContractDetailsEnd(msgBuf *MsgBuffer) { 468 | _ = msgBuf.readString() 469 | reqID := msgBuf.readInt() 470 | 471 | d.wrapper.ContractDetailsEnd(reqID) 472 | } 473 | 474 | // ------------------------------------------------------------------ 475 | 476 | func (d *ibDecoder) processTickPriceMsg(msgBuf *MsgBuffer) { 477 | _ = msgBuf.readString() 478 | reqID := msgBuf.readInt() 479 | tickType := msgBuf.readInt() 480 | price := msgBuf.readFloat() 481 | size := msgBuf.readInt() 482 | attrMask := msgBuf.readInt() 483 | 484 | attrib := TickAttrib{} 485 | attrib.CanAutoExecute = attrMask == 1 486 | 487 | if d.version >= mMIN_SERVER_VER_PAST_LIMIT { 488 | attrib.CanAutoExecute = attrMask&0x1 != 0 489 | attrib.PastLimit = attrMask&0x2 != 0 490 | if d.version >= mMIN_SERVER_VER_PRE_OPEN_BID_ASK { 491 | attrib.PreOpen = attrMask&0x4 != 0 492 | } 493 | } 494 | 495 | d.wrapper.TickPrice(reqID, tickType, price, attrib) 496 | 497 | var sizeTickType int64 498 | switch tickType { 499 | case BID: 500 | sizeTickType = BID_SIZE 501 | case ASK: 502 | sizeTickType = ASK_SIZE 503 | case LAST: 504 | sizeTickType = LAST_SIZE 505 | case DELAYED_BID: 506 | sizeTickType = DELAYED_BID_SIZE 507 | case DELAYED_ASK: 508 | sizeTickType = DELAYED_ASK_SIZE 509 | case DELAYED_LAST: 510 | sizeTickType = DELAYED_LAST_SIZE 511 | default: 512 | sizeTickType = NOT_SET 513 | } 514 | 515 | if sizeTickType != NOT_SET { 516 | d.wrapper.TickSize(reqID, sizeTickType, size) 517 | } 518 | 519 | } 520 | 521 | func (d *ibDecoder) processOrderStatusMsg(msgBuf *MsgBuffer) { 522 | if d.version < mMIN_SERVER_VER_MARKET_CAP_PRICE { 523 | _ = msgBuf.readString() 524 | } 525 | orderID := msgBuf.readInt() 526 | status := msgBuf.readString() 527 | 528 | filled := msgBuf.readFloat() 529 | 530 | remaining := msgBuf.readFloat() 531 | 532 | avgFilledPrice := msgBuf.readFloat() 533 | 534 | permID := msgBuf.readInt() 535 | parentID := msgBuf.readInt() 536 | lastFillPrice := msgBuf.readFloat() 537 | clientID := msgBuf.readInt() 538 | whyHeld := msgBuf.readString() 539 | 540 | mktCapPrice := 0.0 541 | if d.version >= mMIN_SERVER_VER_MARKET_CAP_PRICE { 542 | mktCapPrice = msgBuf.readFloat() 543 | } 544 | 545 | d.wrapper.OrderStatus(orderID, status, filled, remaining, avgFilledPrice, permID, parentID, lastFillPrice, clientID, whyHeld, mktCapPrice) 546 | 547 | } 548 | 549 | func (d *ibDecoder) processOpenOrder(msgBuf *MsgBuffer) { 550 | 551 | var version int64 552 | if d.version < mMIN_SERVER_VER_ORDER_CONTAINER { 553 | version = msgBuf.readInt() 554 | } else { 555 | version = int64(d.version) 556 | } 557 | 558 | o := &Order{} 559 | o.OrderID = msgBuf.readInt() 560 | 561 | c := &Contract{} 562 | 563 | // read contract fields 564 | c.ContractID = msgBuf.readInt() 565 | c.Symbol = msgBuf.readString() 566 | c.SecurityType = msgBuf.readString() 567 | c.Expiry = msgBuf.readString() 568 | c.Strike = msgBuf.readFloat() 569 | c.Right = msgBuf.readString() 570 | if version >= 32 { 571 | c.Multiplier = msgBuf.readString() 572 | } 573 | c.Exchange = msgBuf.readString() 574 | c.Currency = msgBuf.readString() 575 | c.LocalSymbol = msgBuf.readString() 576 | if version >= 32 { 577 | c.TradingClass = msgBuf.readString() 578 | } 579 | 580 | // read order fields 581 | o.Action = msgBuf.readString() 582 | if d.version >= mMIN_SERVER_VER_FRACTIONAL_POSITIONS { 583 | o.TotalQuantity = msgBuf.readFloat() 584 | } else { 585 | o.TotalQuantity = float64(msgBuf.readInt()) 586 | } 587 | o.OrderType = msgBuf.readString() 588 | if version < 29 { 589 | o.LimitPrice = msgBuf.readFloat() 590 | } else { 591 | o.LimitPrice = msgBuf.readFloatCheckUnset() 592 | } 593 | if version < 30 { 594 | o.AuxPrice = msgBuf.readFloat() 595 | } else { 596 | o.AuxPrice = msgBuf.readFloatCheckUnset() 597 | } 598 | o.TIF = msgBuf.readString() 599 | o.OCAGroup = msgBuf.readString() 600 | o.Account = msgBuf.readString() 601 | o.OpenClose = msgBuf.readString() 602 | o.Origin = msgBuf.readInt() 603 | o.OrderRef = msgBuf.readString() 604 | o.ClientID = msgBuf.readInt() 605 | o.PermID = msgBuf.readInt() 606 | o.OutsideRTH = msgBuf.readBool() 607 | o.Hidden = msgBuf.readBool() 608 | o.DiscretionaryAmount = msgBuf.readFloat() 609 | o.GoodAfterTime = msgBuf.readString() 610 | _ = msgBuf.readString() // skip sharesAllocation 611 | 612 | // FAParams 613 | o.FAGroup = msgBuf.readString() 614 | o.FAMethod = msgBuf.readString() 615 | o.FAPercentage = msgBuf.readString() 616 | o.FAProfile = msgBuf.readString() 617 | // --------- 618 | if d.version >= mMIN_SERVER_VER_MODELS_SUPPORT { 619 | o.ModelCode = msgBuf.readString() 620 | } 621 | o.GoodTillDate = msgBuf.readString() 622 | o.Rule80A = msgBuf.readString() 623 | o.PercentOffset = msgBuf.readFloatCheckUnset() //show_unset 624 | o.SettlingFirm = msgBuf.readString() 625 | 626 | // ShortSaleParams 627 | o.ShortSaleSlot = msgBuf.readInt() 628 | o.DesignatedLocation = msgBuf.readString() 629 | if d.version == mMIN_SERVER_VER_SSHORTX_OLD { 630 | _ = msgBuf.readString() 631 | } else if version >= 23 { 632 | o.ExemptCode = msgBuf.readInt() 633 | } 634 | // ---------- 635 | o.AuctionStrategy = msgBuf.readInt() 636 | 637 | // BoxOrderParams 638 | o.StartingPrice = msgBuf.readFloatCheckUnset() //show_unset 639 | o.StockRefPrice = msgBuf.readFloatCheckUnset() //show_unset 640 | o.Delta = msgBuf.readFloatCheckUnset() //show_unset 641 | // ---------- 642 | 643 | // PegToStkOrVolOrderParams 644 | o.StockRangeLower = msgBuf.readFloatCheckUnset() //show_unset 645 | o.StockRangeUpper = msgBuf.readFloatCheckUnset() //show_unset 646 | // ---------- 647 | 648 | o.DisplaySize = msgBuf.readInt() 649 | o.BlockOrder = msgBuf.readBool() 650 | o.SweepToFill = msgBuf.readBool() 651 | o.AllOrNone = msgBuf.readBool() 652 | o.MinQty = msgBuf.readIntCheckUnset() //show_unset 653 | o.OCAType = msgBuf.readInt() 654 | o.ETradeOnly = msgBuf.readBool() 655 | o.FirmQuoteOnly = msgBuf.readBool() 656 | o.NBBOPriceCap = msgBuf.readFloatCheckUnset() //show_unset 657 | o.ParentID = msgBuf.readInt() 658 | o.TriggerMethod = msgBuf.readInt() 659 | 660 | // VolOrderParams 661 | o.Volatility = msgBuf.readFloatCheckUnset() //show_unset 662 | o.VolatilityType = msgBuf.readInt() 663 | o.DeltaNeutralOrderType = msgBuf.readString() 664 | o.DeltaNeutralAuxPrice = msgBuf.readFloatCheckUnset() //show_unset 665 | if version >= 27 && o.DeltaNeutralOrderType != "" { 666 | o.DeltaNeutralContractID = msgBuf.readInt() 667 | o.DeltaNeutralSettlingFirm = msgBuf.readString() 668 | o.DeltaNeutralClearingAccount = msgBuf.readString() 669 | o.DeltaNeutralClearingIntent = msgBuf.readString() 670 | } 671 | if version >= 31 && o.DeltaNeutralOrderType != "" { 672 | o.DeltaNeutralOpenClose = msgBuf.readString() 673 | o.DeltaNeutralShortSale = msgBuf.readBool() 674 | o.DeltaNeutralShortSaleSlot = msgBuf.readInt() 675 | o.DeltaNeutralDesignatedLocation = msgBuf.readString() 676 | } 677 | o.ContinuousUpdate = msgBuf.readBool() 678 | o.ReferencePriceType = msgBuf.readInt() 679 | // --------- 680 | 681 | // TrailParams 682 | o.TrailStopPrice = msgBuf.readFloatCheckUnset() 683 | if version >= 30 { 684 | o.TrailingPercent = msgBuf.readFloatCheckUnset() //show_unset 685 | } 686 | // ---------- 687 | 688 | // BasisPoints 689 | o.BasisPoints = msgBuf.readFloatCheckUnset() 690 | o.BasisPointsType = msgBuf.readIntCheckUnset() 691 | // ---------- 692 | 693 | // ComboLegs 694 | c.ComboLegsDescription = msgBuf.readString() 695 | if version >= 29 { 696 | { 697 | n := msgBuf.readInt() 698 | c.ComboLegs = make([]ComboLeg, 0, n) 699 | for ; n > 0; n-- { 700 | comboleg := ComboLeg{} 701 | comboleg.ContractID = msgBuf.readInt() 702 | comboleg.Ratio = msgBuf.readInt() 703 | comboleg.Action = msgBuf.readString() 704 | comboleg.Exchange = msgBuf.readString() 705 | comboleg.OpenClose = msgBuf.readInt() 706 | comboleg.ShortSaleSlot = msgBuf.readInt() 707 | comboleg.DesignatedLocation = msgBuf.readString() 708 | comboleg.ExemptCode = msgBuf.readInt() 709 | c.ComboLegs = append(c.ComboLegs, comboleg) 710 | } 711 | } 712 | 713 | { 714 | n := msgBuf.readInt() 715 | o.OrderComboLegs = make([]OrderComboLeg, 0, n) 716 | for ; n > 0; n-- { 717 | orderComboLeg := OrderComboLeg{} 718 | orderComboLeg.Price = msgBuf.readFloatCheckUnset() 719 | o.OrderComboLegs = append(o.OrderComboLegs, orderComboLeg) 720 | } 721 | } 722 | 723 | } 724 | if version >= 26 { 725 | n := msgBuf.readInt() 726 | o.SmartComboRoutingParams = make([]TagValue, 0, n) 727 | for ; n > 0; n-- { 728 | tagValue := TagValue{} 729 | tagValue.Tag = msgBuf.readString() 730 | tagValue.Value = msgBuf.readString() 731 | o.SmartComboRoutingParams = append(o.SmartComboRoutingParams, tagValue) 732 | } 733 | } 734 | // ---------- 735 | 736 | // ScaleOrderParams 737 | if version >= 20 { 738 | o.ScaleInitLevelSize = msgBuf.readIntCheckUnset() //show_unset 739 | o.ScaleSubsLevelSize = msgBuf.readIntCheckUnset() //show_unset 740 | } else { 741 | o.NotSuppScaleNumComponents = msgBuf.readIntCheckUnset() 742 | o.ScaleInitLevelSize = msgBuf.readIntCheckUnset() 743 | } 744 | o.ScalePriceIncrement = msgBuf.readFloatCheckUnset() 745 | if version >= 28 && o.ScalePriceIncrement != UNSETFLOAT && o.ScalePriceIncrement > 0.0 { 746 | o.ScalePriceAdjustValue = msgBuf.readFloatCheckUnset() 747 | o.ScalePriceAdjustInterval = msgBuf.readIntCheckUnset() 748 | o.ScaleProfitOffset = msgBuf.readFloatCheckUnset() 749 | o.ScaleAutoReset = msgBuf.readBool() 750 | o.ScaleInitPosition = msgBuf.readIntCheckUnset() 751 | o.ScaleInitFillQty = msgBuf.readIntCheckUnset() 752 | o.ScaleRandomPercent = msgBuf.readBool() 753 | } 754 | // ---------- 755 | 756 | if version >= 24 { 757 | o.HedgeType = msgBuf.readString() 758 | if o.HedgeType != "" { 759 | o.HedgeParam = msgBuf.readString() 760 | } 761 | } 762 | 763 | if version >= 25 { 764 | o.OptOutSmartRouting = msgBuf.readBool() 765 | } 766 | 767 | // ClearingParams 768 | o.ClearingAccount = msgBuf.readString() 769 | o.ClearingIntent = msgBuf.readString() 770 | // ---------- 771 | 772 | if version >= 22 { 773 | o.NotHeld = msgBuf.readBool() 774 | } 775 | 776 | // DeltaNeutral 777 | if version >= 20 { 778 | deltaNeutralContractPresent := msgBuf.readBool() 779 | if deltaNeutralContractPresent { 780 | c.DeltaNeutralContract = new(DeltaNeutralContract) 781 | c.DeltaNeutralContract.ContractID = msgBuf.readInt() 782 | c.DeltaNeutralContract.Delta = msgBuf.readFloat() 783 | c.DeltaNeutralContract.Price = msgBuf.readFloat() 784 | } 785 | } 786 | // ---------- 787 | 788 | // AlgoParams 789 | if version >= 21 { 790 | o.AlgoStrategy = msgBuf.readString() 791 | if o.AlgoStrategy != "" { 792 | n := msgBuf.readInt() 793 | o.AlgoParams = make([]TagValue, 0, n) 794 | for ; n > 0; n-- { 795 | tagValue := TagValue{} 796 | tagValue.Tag = msgBuf.readString() 797 | tagValue.Value = msgBuf.readString() 798 | o.AlgoParams = append(o.AlgoParams, tagValue) 799 | } 800 | } 801 | } 802 | // ---------- 803 | 804 | if version >= 33 { 805 | o.Solictied = msgBuf.readBool() 806 | } 807 | 808 | orderState := &OrderState{} 809 | 810 | // WhatIfInfoAndCommission 811 | o.WhatIf = msgBuf.readBool() 812 | orderState.Status = msgBuf.readString() 813 | if d.version >= mMIN_SERVER_VER_WHAT_IF_EXT_FIELDS { 814 | orderState.InitialMarginBefore = msgBuf.readString() 815 | orderState.MaintenanceMarginBefore = msgBuf.readString() 816 | orderState.EquityWithLoanBefore = msgBuf.readString() 817 | orderState.InitialMarginChange = msgBuf.readString() 818 | orderState.MaintenanceMarginChange = msgBuf.readString() 819 | orderState.EquityWithLoanChange = msgBuf.readString() 820 | } 821 | 822 | orderState.InitialMarginAfter = msgBuf.readString() 823 | orderState.MaintenanceMarginAfter = msgBuf.readString() 824 | orderState.EquityWithLoanAfter = msgBuf.readString() 825 | 826 | orderState.Commission = msgBuf.readFloatCheckUnset() 827 | orderState.MinCommission = msgBuf.readFloatCheckUnset() 828 | orderState.MaxCommission = msgBuf.readFloatCheckUnset() 829 | orderState.CommissionCurrency = msgBuf.readString() 830 | orderState.WarningText = msgBuf.readString() 831 | // ---------- 832 | 833 | // VolRandomizeFlags 834 | if version >= 34 { 835 | o.RandomizeSize = msgBuf.readBool() 836 | o.RandomizePrice = msgBuf.readBool() 837 | } 838 | // ---------- 839 | 840 | if d.version >= mMIN_SERVER_VER_PEGGED_TO_BENCHMARK { 841 | // PegToBenchParams 842 | if o.OrderType == "PEG BENCH" { 843 | o.ReferenceContractID = msgBuf.readInt() 844 | o.IsPeggedChangeAmountDecrease = msgBuf.readBool() 845 | o.PeggedChangeAmount = msgBuf.readFloat() 846 | o.ReferenceChangeAmount = msgBuf.readFloat() 847 | o.ReferenceExchangeID = msgBuf.readString() 848 | } 849 | // ---------- 850 | 851 | // Conditions 852 | n := msgBuf.readInt() 853 | o.Conditions = make([]OrderConditioner, 0, n) 854 | if n > 0 { 855 | for ; n > 0; n-- { 856 | conditionType := msgBuf.readInt() 857 | cond, _ := InitOrderCondition(conditionType) 858 | cond.decode(msgBuf) 859 | 860 | o.Conditions = append(o.Conditions, cond) 861 | } 862 | o.ConditionsIgnoreRth = msgBuf.readBool() 863 | o.ConditionsCancelOrder = msgBuf.readBool() 864 | } 865 | // ---------- 866 | 867 | // AdjustedOrderParams 868 | o.AdjustedOrderType = msgBuf.readString() 869 | o.TriggerPrice = msgBuf.readFloat() 870 | o.TrailStopPrice = msgBuf.readFloat() 871 | o.LimitPriceOffset = msgBuf.readFloat() 872 | o.AdjustedStopPrice = msgBuf.readFloat() 873 | o.AdjustedStopLimitPrice = msgBuf.readFloat() 874 | o.AdjustedTrailingAmount = msgBuf.readFloat() 875 | o.AdjustableTrailingUnit = msgBuf.readInt() 876 | // ---------- 877 | } 878 | 879 | // SoftDollarTier 880 | if d.version >= mMIN_SERVER_VER_SOFT_DOLLAR_TIER { 881 | name := msgBuf.readString() 882 | value := msgBuf.readString() 883 | displayName := msgBuf.readString() 884 | o.SoftDollarTier = SoftDollarTier{name, value, displayName} 885 | } 886 | // ---------- 887 | 888 | if d.version >= mMIN_SERVER_VER_CASH_QTY { 889 | o.CashQty = msgBuf.readFloat() 890 | } 891 | 892 | if d.version >= mMIN_SERVER_VER_AUTO_PRICE_FOR_HEDGE { 893 | o.DontUseAutoPriceForHedge = msgBuf.readBool() 894 | } 895 | 896 | if d.version >= mMIN_SERVER_VER_ORDER_CONTAINER { 897 | o.IsOmsContainer = msgBuf.readBool() 898 | } 899 | 900 | if d.version >= mMIN_SERVER_VER_D_PEG_ORDERS { 901 | o.DiscretionaryUpToLimitPrice = msgBuf.readBool() 902 | } 903 | 904 | if d.version >= mMIN_SERVER_VER_PRICE_MGMT_ALGO { 905 | o.UsePriceMgmtAlgo = msgBuf.readBool() 906 | } 907 | 908 | if d.version >= mMIN_SERVER_VER_DURATION { 909 | o.Duration = msgBuf.readIntCheckUnset() 910 | } 911 | 912 | if d.version >= mMIN_SERVER_VER_POST_TO_ATS { 913 | o.PostToAts = msgBuf.readIntCheckUnset() 914 | } 915 | 916 | d.wrapper.OpenOrder(o.OrderID, c, o, orderState) 917 | 918 | } 919 | 920 | func (d *ibDecoder) processPortfolioValueMsg(msgBuf *MsgBuffer) { 921 | v := msgBuf.readInt() 922 | 923 | c := &Contract{} 924 | c.ContractID = msgBuf.readInt() 925 | c.Symbol = msgBuf.readString() 926 | c.SecurityType = msgBuf.readString() 927 | c.Expiry = msgBuf.readString() 928 | c.Strike = msgBuf.readFloat() 929 | c.Right = msgBuf.readString() 930 | if v >= 7 { 931 | c.Multiplier = msgBuf.readString() 932 | c.PrimaryExchange = msgBuf.readString() 933 | } 934 | c.Currency = msgBuf.readString() 935 | c.LocalSymbol = msgBuf.readString() 936 | if v >= 8 { 937 | c.TradingClass = msgBuf.readString() 938 | } 939 | var position float64 940 | if d.version >= mMIN_SERVER_VER_FRACTIONAL_POSITIONS { 941 | position = msgBuf.readFloat() 942 | } else { 943 | position = float64(msgBuf.readInt()) 944 | } 945 | marketPrice := msgBuf.readFloat() 946 | marketValue := msgBuf.readFloat() 947 | averageCost := msgBuf.readFloat() 948 | unrealizedPNL := msgBuf.readFloat() 949 | realizedPNL := msgBuf.readFloat() 950 | accName := msgBuf.readString() 951 | if v == 6 && d.version == 39 { 952 | c.PrimaryExchange = msgBuf.readString() 953 | } 954 | 955 | d.wrapper.UpdatePortfolio(c, position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accName) 956 | 957 | } 958 | 959 | func (d *ibDecoder) processContractDataMsg(msgBuf *MsgBuffer) { 960 | v := msgBuf.readInt() 961 | var reqID int64 = -1 962 | if v >= 3 { 963 | reqID = msgBuf.readInt() 964 | } 965 | 966 | cd := ContractDetails{} 967 | cd.Contract = Contract{} 968 | cd.Contract.Symbol = msgBuf.readString() 969 | cd.Contract.SecurityType = msgBuf.readString() 970 | 971 | lastTradeDateOrContractMonth := msgBuf.readString() 972 | if lastTradeDateOrContractMonth != "" { 973 | splitted := strings.Split(lastTradeDateOrContractMonth, " ") 974 | l := len(splitted) 975 | 976 | if l > 0 { 977 | cd.Contract.Expiry = splitted[0] 978 | } 979 | if l > 1 { 980 | cd.LastTradeTime = splitted[1] 981 | } 982 | } 983 | 984 | cd.Contract.Strike = msgBuf.readFloat() 985 | cd.Contract.Right = msgBuf.readString() 986 | cd.Contract.Exchange = msgBuf.readString() 987 | cd.Contract.Currency = msgBuf.readString() 988 | cd.Contract.LocalSymbol = msgBuf.readString() 989 | cd.MarketName = msgBuf.readString() 990 | cd.Contract.TradingClass = msgBuf.readString() 991 | cd.Contract.ContractID = msgBuf.readInt() 992 | cd.MinTick = msgBuf.readFloat() 993 | if d.version >= mMIN_SERVER_VER_MD_SIZE_MULTIPLIER { 994 | cd.MdSizeMultiplier = msgBuf.readInt() 995 | } 996 | cd.Contract.Multiplier = msgBuf.readString() 997 | cd.OrderTypes = msgBuf.readString() 998 | cd.ValidExchanges = msgBuf.readString() 999 | cd.PriceMagnifier = msgBuf.readInt() 1000 | if v >= 4 { 1001 | cd.UnderContractID = msgBuf.readInt() 1002 | } 1003 | if v >= 5 { 1004 | if d.version >= mMIN_SERVER_VER_ENCODE_MSG_ASCII7 { 1005 | cd.LongName = msgBuf.readString() // FIXME: unicode-escape 1006 | } else { 1007 | cd.LongName = msgBuf.readString() 1008 | } 1009 | 1010 | cd.Contract.PrimaryExchange = msgBuf.readString() 1011 | } 1012 | if v >= 6 { 1013 | cd.ContractMonth = msgBuf.readString() 1014 | cd.Industry = msgBuf.readString() 1015 | cd.Category = msgBuf.readString() 1016 | cd.Subcategory = msgBuf.readString() 1017 | cd.TimezoneID = msgBuf.readString() 1018 | cd.TradingHours = msgBuf.readString() 1019 | cd.LiquidHours = msgBuf.readString() 1020 | } 1021 | if v >= 8 { 1022 | cd.EVRule = msgBuf.readString() 1023 | cd.EVMultiplier = msgBuf.readInt() 1024 | } 1025 | if v >= 7 { 1026 | n := msgBuf.readInt() 1027 | cd.SecurityIDList = make([]TagValue, 0, n) 1028 | for ; n > 0; n-- { 1029 | tagValue := TagValue{} 1030 | tagValue.Tag = msgBuf.readString() 1031 | tagValue.Value = msgBuf.readString() 1032 | cd.SecurityIDList = append(cd.SecurityIDList, tagValue) 1033 | } 1034 | } 1035 | 1036 | if d.version >= mMIN_SERVER_VER_AGG_GROUP { 1037 | cd.AggGroup = msgBuf.readInt() 1038 | } 1039 | 1040 | if d.version >= mMIN_SERVER_VER_UNDERLYING_INFO { 1041 | cd.UnderSymbol = msgBuf.readString() 1042 | cd.UnderSecurityType = msgBuf.readString() 1043 | } 1044 | 1045 | if d.version >= mMIN_SERVER_VER_MARKET_RULES { 1046 | cd.MarketRuleIDs = msgBuf.readString() 1047 | } 1048 | 1049 | if d.version >= mMIN_SERVER_VER_REAL_EXPIRATION_DATE { 1050 | cd.RealExpirationDate = msgBuf.readString() 1051 | } 1052 | 1053 | if d.version >= mMIN_SERVER_VER_STOCK_TYPE { 1054 | cd.StockType = msgBuf.readString() 1055 | } 1056 | 1057 | d.wrapper.ContractDetails(reqID, &cd) 1058 | 1059 | } 1060 | func (d *ibDecoder) processBondContractDataMsg(msgBuf *MsgBuffer) { 1061 | v := msgBuf.readInt() 1062 | 1063 | var reqID int64 = -1 1064 | 1065 | if v >= 3 { 1066 | reqID = msgBuf.readInt() 1067 | } 1068 | 1069 | c := &ContractDetails{} 1070 | c.Contract.Symbol = msgBuf.readString() 1071 | c.Contract.SecurityType = msgBuf.readString() 1072 | c.Cusip = msgBuf.readString() 1073 | c.Coupon = msgBuf.readInt() 1074 | 1075 | splittedExpiry := strings.Split(msgBuf.readString(), " ") 1076 | switch s := len(splittedExpiry); { 1077 | case s > 0: 1078 | c.Maturity = splittedExpiry[0] 1079 | fallthrough 1080 | case s > 1: 1081 | c.LastTradeTime = splittedExpiry[1] 1082 | fallthrough 1083 | case s > 2: 1084 | c.TimezoneID = splittedExpiry[2] 1085 | } 1086 | 1087 | c.IssueDate = msgBuf.readString() 1088 | c.Ratings = msgBuf.readString() 1089 | c.BondType = msgBuf.readString() 1090 | c.CouponType = msgBuf.readString() 1091 | c.Convertible = msgBuf.readBool() 1092 | c.Callable = msgBuf.readBool() 1093 | c.Putable = msgBuf.readBool() 1094 | c.DescAppend = msgBuf.readString() 1095 | c.Contract.Exchange = msgBuf.readString() 1096 | c.Contract.Currency = msgBuf.readString() 1097 | c.MarketName = msgBuf.readString() 1098 | c.Contract.TradingClass = msgBuf.readString() 1099 | c.Contract.ContractID = msgBuf.readInt() 1100 | c.MinTick = msgBuf.readFloat() 1101 | 1102 | if d.version >= mMIN_SERVER_VER_MD_SIZE_MULTIPLIER { 1103 | c.MdSizeMultiplier = msgBuf.readInt() 1104 | } 1105 | 1106 | c.OrderTypes = msgBuf.readString() 1107 | c.ValidExchanges = msgBuf.readString() 1108 | c.NextOptionDate = msgBuf.readString() 1109 | c.NextOptionType = msgBuf.readString() 1110 | c.NextOptionPartial = msgBuf.readBool() 1111 | c.Notes = msgBuf.readString() 1112 | 1113 | if v >= 4 { 1114 | c.LongName = msgBuf.readString() 1115 | } 1116 | 1117 | if v >= 6 { 1118 | c.EVRule = msgBuf.readString() 1119 | c.EVMultiplier = msgBuf.readInt() 1120 | } 1121 | 1122 | if v >= 5 { 1123 | n := msgBuf.readInt() 1124 | c.SecurityIDList = make([]TagValue, 0, n) 1125 | for ; n > 0; n-- { 1126 | tagValue := TagValue{} 1127 | tagValue.Tag = msgBuf.readString() 1128 | tagValue.Value = msgBuf.readString() 1129 | c.SecurityIDList = append(c.SecurityIDList, tagValue) 1130 | } 1131 | } 1132 | 1133 | if d.version >= mMIN_SERVER_VER_AGG_GROUP { 1134 | c.AggGroup = msgBuf.readInt() 1135 | } 1136 | 1137 | if d.version >= mMIN_SERVER_VER_MARKET_RULES { 1138 | c.MarketRuleIDs = msgBuf.readString() 1139 | } 1140 | 1141 | d.wrapper.BondContractDetails(reqID, c) 1142 | 1143 | } 1144 | func (d *ibDecoder) processScannerDataMsg(msgBuf *MsgBuffer) { 1145 | _ = msgBuf.readString() 1146 | reqID := msgBuf.readInt() 1147 | for n := msgBuf.readInt(); n > 0; n-- { 1148 | sd := ScanData{} 1149 | sd.ContractDetails = ContractDetails{} 1150 | 1151 | sd.Rank = msgBuf.readInt() 1152 | sd.ContractDetails.Contract.ContractID = msgBuf.readInt() 1153 | sd.ContractDetails.Contract.Symbol = msgBuf.readString() 1154 | sd.ContractDetails.Contract.SecurityType = msgBuf.readString() 1155 | sd.ContractDetails.Contract.Expiry = msgBuf.readString() 1156 | sd.ContractDetails.Contract.Strike = msgBuf.readFloat() 1157 | sd.ContractDetails.Contract.Right = msgBuf.readString() 1158 | sd.ContractDetails.Contract.Exchange = msgBuf.readString() 1159 | sd.ContractDetails.Contract.Currency = msgBuf.readString() 1160 | sd.ContractDetails.Contract.LocalSymbol = msgBuf.readString() 1161 | sd.ContractDetails.MarketName = msgBuf.readString() 1162 | sd.ContractDetails.Contract.TradingClass = msgBuf.readString() 1163 | sd.Distance = msgBuf.readString() 1164 | sd.Benchmark = msgBuf.readString() 1165 | sd.Projection = msgBuf.readString() 1166 | sd.Legs = msgBuf.readString() 1167 | 1168 | d.wrapper.ScannerData(reqID, sd.Rank, &(sd.ContractDetails), sd.Distance, sd.Benchmark, sd.Projection, sd.Legs) 1169 | 1170 | } 1171 | 1172 | d.wrapper.ScannerDataEnd(reqID) 1173 | 1174 | } 1175 | func (d *ibDecoder) processExecutionDataMsg(msgBuf *MsgBuffer) { 1176 | var v int64 1177 | if d.version < mMIN_SERVER_VER_LAST_LIQUIDITY { 1178 | v = msgBuf.readInt() 1179 | } else { 1180 | v = int64(d.version) 1181 | } 1182 | 1183 | var reqID int64 = -1 1184 | if v >= 7 { 1185 | reqID = msgBuf.readInt() 1186 | } 1187 | 1188 | orderID := msgBuf.readInt() 1189 | 1190 | // read contact fields 1191 | c := Contract{} 1192 | c.ContractID = msgBuf.readInt() 1193 | c.Symbol = msgBuf.readString() 1194 | c.SecurityType = msgBuf.readString() 1195 | c.Expiry = msgBuf.readString() 1196 | c.Strike = msgBuf.readFloat() 1197 | c.Right = msgBuf.readString() 1198 | if v >= 9 { 1199 | c.Multiplier = msgBuf.readString() 1200 | } 1201 | c.Exchange = msgBuf.readString() 1202 | c.Currency = msgBuf.readString() 1203 | c.LocalSymbol = msgBuf.readString() 1204 | if v >= 10 { 1205 | c.TradingClass = msgBuf.readString() 1206 | } 1207 | 1208 | // read execution fields 1209 | e := Execution{} 1210 | e.OrderID = orderID 1211 | e.ExecID = msgBuf.readString() 1212 | e.Time = msgBuf.readString() 1213 | e.AccountCode = msgBuf.readString() 1214 | e.Exchange = msgBuf.readString() 1215 | e.Side = msgBuf.readString() 1216 | e.Shares = msgBuf.readFloat() 1217 | e.Price = msgBuf.readFloat() 1218 | e.PermID = msgBuf.readInt() 1219 | e.ClientID = msgBuf.readInt() 1220 | e.Liquidation = msgBuf.readInt() 1221 | if v >= 6 { 1222 | e.CumQty = msgBuf.readFloat() 1223 | e.AveragePrice = msgBuf.readFloat() 1224 | } 1225 | if v >= 8 { 1226 | e.OrderRef = msgBuf.readString() 1227 | } 1228 | if v >= 9 { 1229 | e.EVRule = msgBuf.readString() 1230 | e.EVMultiplier = msgBuf.readFloat() 1231 | } 1232 | if d.version >= mMIN_SERVER_VER_MODELS_SUPPORT { 1233 | e.ModelCode = msgBuf.readString() 1234 | } 1235 | if d.version >= mMIN_SERVER_VER_LAST_LIQUIDITY { 1236 | e.LastLiquidity = msgBuf.readInt() 1237 | } 1238 | 1239 | d.wrapper.ExecDetails(reqID, &c, &e) 1240 | 1241 | } 1242 | 1243 | func (d *ibDecoder) processHistoricalDataMsg(msgBuf *MsgBuffer) { 1244 | if d.version < mMIN_SERVER_VER_SYNT_REALTIME_BARS { 1245 | _ = msgBuf.readString() 1246 | } 1247 | 1248 | reqID := msgBuf.readInt() 1249 | startDatestr := msgBuf.readString() 1250 | endDateStr := msgBuf.readString() 1251 | 1252 | for n := msgBuf.readInt(); n > 0; n-- { 1253 | bar := &BarData{} 1254 | bar.Date = msgBuf.readString() 1255 | bar.Open = msgBuf.readFloat() 1256 | bar.High = msgBuf.readFloat() 1257 | bar.Low = msgBuf.readFloat() 1258 | bar.Close = msgBuf.readFloat() 1259 | bar.Volume = msgBuf.readFloat() 1260 | bar.Average = msgBuf.readFloat() 1261 | if d.version < mMIN_SERVER_VER_SYNT_REALTIME_BARS { 1262 | _ = msgBuf.readString() 1263 | } 1264 | bar.BarCount = msgBuf.readInt() 1265 | 1266 | d.wrapper.HistoricalData(reqID, bar) 1267 | } 1268 | 1269 | d.wrapper.HistoricalDataEnd(reqID, startDatestr, endDateStr) 1270 | 1271 | } 1272 | func (d *ibDecoder) processHistoricalDataUpdateMsg(msgBuf *MsgBuffer) { 1273 | reqID := msgBuf.readInt() 1274 | bar := &BarData{} 1275 | bar.BarCount = msgBuf.readInt() 1276 | bar.Date = msgBuf.readString() 1277 | bar.Open = msgBuf.readFloat() 1278 | bar.Close = msgBuf.readFloat() 1279 | bar.High = msgBuf.readFloat() 1280 | bar.Low = msgBuf.readFloat() 1281 | bar.Average = msgBuf.readFloat() 1282 | bar.Volume = msgBuf.readFloat() 1283 | 1284 | d.wrapper.HistoricalDataUpdate(reqID, bar) 1285 | 1286 | } 1287 | func (d *ibDecoder) processRealTimeBarMsg(msgBuf *MsgBuffer) { 1288 | _ = msgBuf.readString() 1289 | reqID := msgBuf.readInt() 1290 | 1291 | rtb := &RealTimeBar{} 1292 | rtb.Time = msgBuf.readInt() 1293 | rtb.Open = msgBuf.readFloat() 1294 | rtb.High = msgBuf.readFloat() 1295 | rtb.Low = msgBuf.readFloat() 1296 | rtb.Close = msgBuf.readFloat() 1297 | rtb.Volume = msgBuf.readInt() 1298 | rtb.Wap = msgBuf.readFloat() 1299 | rtb.Count = msgBuf.readInt() 1300 | 1301 | d.wrapper.RealtimeBar(reqID, rtb.Time, rtb.Open, rtb.High, rtb.Low, rtb.Close, rtb.Volume, rtb.Wap, rtb.Count) 1302 | } 1303 | 1304 | /* 1305 | void tickOptionComputation ( 1306 | int tickerId, -- the request's unique identifier. 1307 | int field, -- Specifies the type of option computation. Pass the field value into TickType.getField(int tickType) to retrieve the field description. For example, a field value of 13 will map to modelOptComp, etc. 10 = Bid 11 = Ask 12 = Las 1308 | int tickAttrib, -- 0 - return based, 1- price based. 1309 | double impliedVolatility, 1310 | double delta, 1311 | double optPrice, 1312 | double pvDividend, 1313 | double gamma, 1314 | double vega, 1315 | double theta, 1316 | double undPrice 1317 | ) 1318 | */ 1319 | func (d *ibDecoder) processTickOptionComputationMsg(msgBuf *MsgBuffer) { 1320 | optPrice := UNSETFLOAT 1321 | pvDividend := UNSETFLOAT 1322 | gamma := UNSETFLOAT 1323 | vega := UNSETFLOAT 1324 | theta := UNSETFLOAT 1325 | undPrice := UNSETFLOAT 1326 | 1327 | var v int64 1328 | if d.version < mMIN_SERVER_VER_PRICE_BASED_VOLATILITY { 1329 | v = msgBuf.readInt() 1330 | } else { 1331 | v = int64(d.version) 1332 | } 1333 | 1334 | reqID := msgBuf.readInt() // tickerId 1335 | tickType := msgBuf.readInt() // field 1336 | 1337 | var tickAttrib int64 1338 | if d.version >= mMIN_SERVER_VER_PRICE_BASED_VOLATILITY { 1339 | tickAttrib = msgBuf.readInt() // tickAtrib 1340 | } 1341 | 1342 | impliedVol := msgBuf.readFloat() 1343 | delta := msgBuf.readFloat() 1344 | 1345 | if v >= 6 || tickType == MODEL_OPTION || tickType == DELAYED_MODEL_OPTION { 1346 | optPrice = msgBuf.readFloat() 1347 | pvDividend = msgBuf.readFloat() 1348 | } 1349 | 1350 | if v >= 6 { 1351 | gamma = msgBuf.readFloat() 1352 | vega = msgBuf.readFloat() 1353 | theta = msgBuf.readFloat() 1354 | undPrice = msgBuf.readFloat() 1355 | 1356 | } 1357 | 1358 | switch { 1359 | case impliedVol < 0: 1360 | impliedVol = UNSETFLOAT 1361 | fallthrough 1362 | case delta == -2: 1363 | delta = UNSETFLOAT 1364 | fallthrough 1365 | case optPrice == -1: 1366 | optPrice = UNSETFLOAT 1367 | fallthrough 1368 | case pvDividend == -1: 1369 | pvDividend = UNSETFLOAT 1370 | fallthrough 1371 | case gamma == -2: 1372 | gamma = UNSETFLOAT 1373 | fallthrough 1374 | case vega == -2: 1375 | vega = UNSETFLOAT 1376 | fallthrough 1377 | case theta == -2: 1378 | theta = UNSETFLOAT 1379 | fallthrough 1380 | case undPrice == -1: 1381 | undPrice = UNSETFLOAT 1382 | } 1383 | 1384 | d.wrapper.TickOptionComputation(reqID, tickType, tickAttrib, impliedVol, delta, optPrice, pvDividend, gamma, vega, theta, undPrice) 1385 | 1386 | } 1387 | 1388 | func (d *ibDecoder) processDeltaNeutralValidationMsg(msgBuf *MsgBuffer) { 1389 | _ = msgBuf.readString() 1390 | reqID := msgBuf.readInt() 1391 | deltaNeutralContract := DeltaNeutralContract{} 1392 | 1393 | deltaNeutralContract.ContractID = msgBuf.readInt() 1394 | deltaNeutralContract.Delta = msgBuf.readFloat() 1395 | deltaNeutralContract.Price = msgBuf.readFloat() 1396 | 1397 | d.wrapper.DeltaNeutralValidation(reqID, deltaNeutralContract) 1398 | 1399 | } 1400 | 1401 | // func (d *ibDecoder) processMarketDataTypeMsg(msgBuf *MsgBuffer) { 1402 | 1403 | // } 1404 | func (d *ibDecoder) processCommissionReportMsg(msgBuf *MsgBuffer) { 1405 | _ = msgBuf.readString() 1406 | cr := CommissionReport{} 1407 | cr.ExecID = msgBuf.readString() 1408 | cr.Commission = msgBuf.readFloat() 1409 | cr.Currency = msgBuf.readString() 1410 | cr.RealizedPNL = msgBuf.readFloat() 1411 | cr.Yield = msgBuf.readFloat() 1412 | cr.YieldRedemptionDate = msgBuf.readInt() 1413 | 1414 | d.wrapper.CommissionReport(cr) 1415 | 1416 | } 1417 | 1418 | func (d *ibDecoder) processPositionDataMsg(msgBuf *MsgBuffer) { 1419 | v := msgBuf.readInt() 1420 | acc := msgBuf.readString() 1421 | 1422 | // read contract fields 1423 | c := new(Contract) 1424 | c.ContractID = msgBuf.readInt() 1425 | c.Symbol = msgBuf.readString() 1426 | c.SecurityType = msgBuf.readString() 1427 | c.Expiry = msgBuf.readString() 1428 | c.Strike = msgBuf.readFloat() 1429 | c.Right = msgBuf.readString() 1430 | c.Multiplier = msgBuf.readString() 1431 | c.Exchange = msgBuf.readString() 1432 | c.Currency = msgBuf.readString() 1433 | c.LocalSymbol = msgBuf.readString() 1434 | if v >= 2 { 1435 | c.TradingClass = msgBuf.readString() 1436 | } 1437 | 1438 | var p float64 1439 | if d.version >= mMIN_SERVER_VER_FRACTIONAL_POSITIONS { 1440 | p = msgBuf.readFloat() 1441 | } else { 1442 | p = float64(msgBuf.readInt()) 1443 | } 1444 | 1445 | var avgCost float64 1446 | if v >= 3 { 1447 | avgCost = msgBuf.readFloat() 1448 | } 1449 | 1450 | d.wrapper.Position(acc, c, p, avgCost) 1451 | 1452 | } 1453 | 1454 | func (d *ibDecoder) processPositionMultiMsg(msgBuf *MsgBuffer) { 1455 | _ = msgBuf.readString() 1456 | reqID := msgBuf.readInt() 1457 | acc := msgBuf.readString() 1458 | 1459 | // read contract fields 1460 | c := new(Contract) 1461 | c.ContractID = msgBuf.readInt() 1462 | c.Symbol = msgBuf.readString() 1463 | c.SecurityType = msgBuf.readString() 1464 | c.Expiry = msgBuf.readString() 1465 | c.Strike = msgBuf.readFloat() 1466 | c.Right = msgBuf.readString() 1467 | c.Multiplier = msgBuf.readString() 1468 | c.Exchange = msgBuf.readString() 1469 | c.Currency = msgBuf.readString() 1470 | c.LocalSymbol = msgBuf.readString() 1471 | c.TradingClass = msgBuf.readString() 1472 | 1473 | p := msgBuf.readFloat() 1474 | avgCost := msgBuf.readFloat() 1475 | modelCode := msgBuf.readString() 1476 | 1477 | d.wrapper.PositionMulti(reqID, acc, modelCode, c, p, avgCost) 1478 | 1479 | } 1480 | 1481 | func (d *ibDecoder) processSecurityDefinitionOptionParameterMsg(msgBuf *MsgBuffer) { 1482 | reqID := msgBuf.readInt() 1483 | exchange := msgBuf.readString() 1484 | underlyingContractID := msgBuf.readInt() 1485 | tradingClass := msgBuf.readString() 1486 | multiplier := msgBuf.readString() 1487 | 1488 | expN := msgBuf.readInt() 1489 | expirations := make([]string, 0, expN) 1490 | for ; expN > 0; expN-- { 1491 | expiration := msgBuf.readString() 1492 | expirations = append(expirations, expiration) 1493 | } 1494 | 1495 | strikeN := msgBuf.readInt() 1496 | strikes := make([]float64, 0, strikeN) 1497 | for ; strikeN > 0; strikeN-- { 1498 | strike := msgBuf.readFloat() 1499 | strikes = append(strikes, strike) 1500 | } 1501 | 1502 | d.wrapper.SecurityDefinitionOptionParameter(reqID, exchange, underlyingContractID, tradingClass, multiplier, expirations, strikes) 1503 | 1504 | } 1505 | 1506 | func (d *ibDecoder) processSoftDollarTiersMsg(msgBuf *MsgBuffer) { 1507 | reqID := msgBuf.readInt() 1508 | 1509 | n := msgBuf.readInt() 1510 | tiers := make([]SoftDollarTier, 0, n) 1511 | for ; n > 0; n-- { 1512 | tier := SoftDollarTier{} 1513 | tier.Name = msgBuf.readString() 1514 | tier.Value = msgBuf.readString() 1515 | tier.DisplayName = msgBuf.readString() 1516 | tiers = append(tiers, tier) 1517 | } 1518 | 1519 | d.wrapper.SoftDollarTiers(reqID, tiers) 1520 | 1521 | } 1522 | 1523 | func (d *ibDecoder) processFamilyCodesMsg(msgBuf *MsgBuffer) { 1524 | n := msgBuf.readInt() 1525 | familyCodes := make([]FamilyCode, 0, n) 1526 | for ; n > 0; n-- { 1527 | familyCode := FamilyCode{} 1528 | familyCode.AccountID = msgBuf.readString() 1529 | familyCode.FamilyCode = msgBuf.readString() 1530 | familyCodes = append(familyCodes, familyCode) 1531 | } 1532 | 1533 | d.wrapper.FamilyCodes(familyCodes) 1534 | 1535 | } 1536 | 1537 | func (d *ibDecoder) processSymbolSamplesMsg(msgBuf *MsgBuffer) { 1538 | reqID := msgBuf.readInt() 1539 | 1540 | n := msgBuf.readInt() 1541 | contractDescriptions := make([]ContractDescription, 0, n) 1542 | for ; n > 0; n-- { 1543 | cd := ContractDescription{} 1544 | cd.Contract.ContractID = msgBuf.readInt() 1545 | cd.Contract.Symbol = msgBuf.readString() 1546 | cd.Contract.SecurityType = msgBuf.readString() 1547 | cd.Contract.PrimaryExchange = msgBuf.readString() 1548 | cd.Contract.Currency = msgBuf.readString() 1549 | 1550 | sdtN := msgBuf.readInt() 1551 | cd.DerivativeSecTypes = make([]string, 0, sdtN) 1552 | for ; sdtN > 0; sdtN-- { 1553 | derivativeSecType := msgBuf.readString() 1554 | cd.DerivativeSecTypes = append(cd.DerivativeSecTypes, derivativeSecType) 1555 | } 1556 | contractDescriptions = append(contractDescriptions, cd) 1557 | } 1558 | 1559 | d.wrapper.SymbolSamples(reqID, contractDescriptions) 1560 | 1561 | } 1562 | 1563 | func (d *ibDecoder) processSmartComponents(msgBuf *MsgBuffer) { 1564 | reqID := msgBuf.readInt() 1565 | 1566 | n := msgBuf.readInt() 1567 | smartComponents := make([]SmartComponent, 0, n) 1568 | for ; n > 0; n-- { 1569 | smartComponent := SmartComponent{} 1570 | smartComponent.BitNumber = msgBuf.readInt() 1571 | smartComponent.Exchange = msgBuf.readString() 1572 | smartComponent.ExchangeLetter = msgBuf.readString() 1573 | smartComponents = append(smartComponents, smartComponent) 1574 | } 1575 | 1576 | d.wrapper.SmartComponents(reqID, smartComponents) 1577 | 1578 | } 1579 | 1580 | func (d *ibDecoder) processTickReqParams(msgBuf *MsgBuffer) { 1581 | tickerID := msgBuf.readInt() 1582 | minTick := msgBuf.readFloat() 1583 | bboExchange := msgBuf.readString() 1584 | snapshotPermissions := msgBuf.readInt() 1585 | 1586 | d.wrapper.TickReqParams(tickerID, minTick, bboExchange, snapshotPermissions) 1587 | } 1588 | 1589 | func (d *ibDecoder) processMktDepthExchanges(msgBuf *MsgBuffer) { 1590 | n := msgBuf.readInt() 1591 | depthMktDataDescriptions := make([]DepthMktDataDescription, 0, n) 1592 | for ; n > 0; n-- { 1593 | desc := DepthMktDataDescription{} 1594 | desc.Exchange = msgBuf.readString() 1595 | desc.SecurityType = msgBuf.readString() 1596 | if d.version >= mMIN_SERVER_VER_SERVICE_DATA_TYPE { 1597 | desc.ListingExchange = msgBuf.readString() 1598 | desc.SecurityType = msgBuf.readString() 1599 | desc.AggGroup = msgBuf.readInt() 1600 | } else { 1601 | _ = msgBuf.readString() 1602 | } 1603 | 1604 | depthMktDataDescriptions = append(depthMktDataDescriptions, desc) 1605 | } 1606 | 1607 | d.wrapper.MktDepthExchanges(depthMktDataDescriptions) 1608 | } 1609 | 1610 | func (d *ibDecoder) processHeadTimestamp(msgBuf *MsgBuffer) { 1611 | reqID := msgBuf.readInt() 1612 | headTimestamp := msgBuf.readString() 1613 | 1614 | d.wrapper.HeadTimestamp(reqID, headTimestamp) 1615 | } 1616 | 1617 | func (d *ibDecoder) processTickNews(msgBuf *MsgBuffer) { 1618 | tickerID := msgBuf.readInt() 1619 | timeStamp := msgBuf.readInt() 1620 | providerCode := msgBuf.readString() 1621 | articleID := msgBuf.readString() 1622 | headline := msgBuf.readString() 1623 | extraData := msgBuf.readString() 1624 | 1625 | d.wrapper.TickNews(tickerID, timeStamp, providerCode, articleID, headline, extraData) 1626 | } 1627 | 1628 | func (d *ibDecoder) processNewsProviders(msgBuf *MsgBuffer) { 1629 | n := msgBuf.readInt() 1630 | newsProviders := make([]NewsProvider, 0, n) 1631 | for ; n > 0; n-- { 1632 | provider := NewsProvider{} 1633 | provider.Name = msgBuf.readString() 1634 | provider.Code = msgBuf.readString() 1635 | newsProviders = append(newsProviders, provider) 1636 | } 1637 | 1638 | d.wrapper.NewsProviders(newsProviders) 1639 | } 1640 | 1641 | func (d *ibDecoder) processNewsArticle(msgBuf *MsgBuffer) { 1642 | reqID := msgBuf.readInt() 1643 | articleType := msgBuf.readInt() 1644 | articleText := msgBuf.readString() 1645 | 1646 | d.wrapper.NewsArticle(reqID, articleType, articleText) 1647 | } 1648 | 1649 | func (d *ibDecoder) processHistoricalNews(msgBuf *MsgBuffer) { 1650 | reqID := msgBuf.readInt() 1651 | time := msgBuf.readString() 1652 | providerCode := msgBuf.readString() 1653 | articleID := msgBuf.readString() 1654 | headline := msgBuf.readString() 1655 | 1656 | d.wrapper.HistoricalNews(reqID, time, providerCode, articleID, headline) 1657 | } 1658 | 1659 | func (d *ibDecoder) processHistoricalNewsEnd(msgBuf *MsgBuffer) { 1660 | reqID := msgBuf.readInt() 1661 | hasMore := msgBuf.readBool() 1662 | 1663 | d.wrapper.HistoricalNewsEnd(reqID, hasMore) 1664 | } 1665 | 1666 | func (d *ibDecoder) processHistogramData(msgBuf *MsgBuffer) { 1667 | reqID := msgBuf.readInt() 1668 | 1669 | n := msgBuf.readInt() 1670 | histogram := make([]HistogramData, 0, n) 1671 | for ; n > 0; n-- { 1672 | p := HistogramData{} 1673 | p.Price = msgBuf.readFloat() 1674 | p.Count = msgBuf.readInt() 1675 | histogram = append(histogram, p) 1676 | } 1677 | 1678 | d.wrapper.HistogramData(reqID, histogram) 1679 | } 1680 | 1681 | func (d *ibDecoder) processRerouteMktDataReq(msgBuf *MsgBuffer) { 1682 | reqID := msgBuf.readInt() 1683 | contractID := msgBuf.readInt() 1684 | exchange := msgBuf.readString() 1685 | 1686 | d.wrapper.RerouteMktDataReq(reqID, contractID, exchange) 1687 | } 1688 | 1689 | func (d *ibDecoder) processRerouteMktDepthReq(msgBuf *MsgBuffer) { 1690 | reqID := msgBuf.readInt() 1691 | contractID := msgBuf.readInt() 1692 | exchange := msgBuf.readString() 1693 | 1694 | d.wrapper.RerouteMktDepthReq(reqID, contractID, exchange) 1695 | } 1696 | 1697 | func (d *ibDecoder) processMarketRuleMsg(msgBuf *MsgBuffer) { 1698 | marketRuleID := msgBuf.readInt() 1699 | 1700 | n := msgBuf.readInt() 1701 | priceIncrements := make([]PriceIncrement, 0, n) 1702 | for ; n > 0; n-- { 1703 | priceInc := PriceIncrement{} 1704 | priceInc.LowEdge = msgBuf.readFloat() 1705 | priceInc.Increment = msgBuf.readFloat() 1706 | priceIncrements = append(priceIncrements, priceInc) 1707 | } 1708 | 1709 | d.wrapper.MarketRule(marketRuleID, priceIncrements) 1710 | } 1711 | 1712 | func (d *ibDecoder) processPnLMsg(msgBuf *MsgBuffer) { 1713 | reqID := msgBuf.readInt() 1714 | dailyPnL := msgBuf.readFloat() 1715 | var unrealizedPnL float64 1716 | var realizedPnL float64 1717 | 1718 | if d.version >= mMIN_SERVER_VER_UNREALIZED_PNL { 1719 | unrealizedPnL = msgBuf.readFloat() 1720 | } 1721 | 1722 | if d.version >= mMIN_SERVER_VER_REALIZED_PNL { 1723 | realizedPnL = msgBuf.readFloat() 1724 | } 1725 | 1726 | d.wrapper.Pnl(reqID, dailyPnL, unrealizedPnL, realizedPnL) 1727 | 1728 | } 1729 | func (d *ibDecoder) processPnLSingleMsg(msgBuf *MsgBuffer) { 1730 | reqID := msgBuf.readInt() 1731 | position := msgBuf.readInt() 1732 | dailyPnL := msgBuf.readFloat() 1733 | var unrealizedPnL float64 1734 | var realizedPnL float64 1735 | 1736 | if d.version >= mMIN_SERVER_VER_UNREALIZED_PNL { 1737 | unrealizedPnL = msgBuf.readFloat() 1738 | } 1739 | 1740 | if d.version >= mMIN_SERVER_VER_REALIZED_PNL { 1741 | realizedPnL = msgBuf.readFloat() 1742 | } 1743 | 1744 | value := msgBuf.readFloat() 1745 | 1746 | d.wrapper.PnlSingle(reqID, position, dailyPnL, unrealizedPnL, realizedPnL, value) 1747 | } 1748 | func (d *ibDecoder) processHistoricalTicks(msgBuf *MsgBuffer) { 1749 | reqID := msgBuf.readInt() 1750 | 1751 | n := msgBuf.readInt() 1752 | ticks := make([]HistoricalTick, 0, n) 1753 | for ; n > 0; n-- { 1754 | historicalTick := HistoricalTick{} 1755 | historicalTick.Time = msgBuf.readInt() 1756 | _ = msgBuf.readString() 1757 | historicalTick.Price = msgBuf.readFloat() 1758 | historicalTick.Size = msgBuf.readInt() 1759 | ticks = append(ticks, historicalTick) 1760 | } 1761 | 1762 | done := msgBuf.readBool() 1763 | 1764 | d.wrapper.HistoricalTicks(reqID, ticks, done) 1765 | } 1766 | func (d *ibDecoder) processHistoricalTicksBidAsk(msgBuf *MsgBuffer) { 1767 | reqID := msgBuf.readInt() 1768 | 1769 | n := msgBuf.readInt() 1770 | ticks := make([]HistoricalTickBidAsk, 0, n) 1771 | for ; n > 0; n-- { 1772 | historicalTickBidAsk := HistoricalTickBidAsk{} 1773 | historicalTickBidAsk.Time = msgBuf.readInt() 1774 | 1775 | mask := msgBuf.readInt() 1776 | tickAttribBidAsk := TickAttribBidAsk{} 1777 | tickAttribBidAsk.AskPastHigh = mask&1 != 0 1778 | tickAttribBidAsk.BidPastLow = mask&2 != 0 1779 | 1780 | historicalTickBidAsk.TickAttirbBidAsk = tickAttribBidAsk 1781 | historicalTickBidAsk.PriceBid = msgBuf.readFloat() 1782 | historicalTickBidAsk.PriceAsk = msgBuf.readFloat() 1783 | historicalTickBidAsk.SizeBid = msgBuf.readInt() 1784 | historicalTickBidAsk.SizeAsk = msgBuf.readInt() 1785 | ticks = append(ticks, historicalTickBidAsk) 1786 | } 1787 | 1788 | done := msgBuf.readBool() 1789 | 1790 | d.wrapper.HistoricalTicksBidAsk(reqID, ticks, done) 1791 | } 1792 | func (d *ibDecoder) processHistoricalTicksLast(msgBuf *MsgBuffer) { 1793 | reqID := msgBuf.readInt() 1794 | 1795 | n := msgBuf.readInt() 1796 | ticks := make([]HistoricalTickLast, 0, n) 1797 | for ; n > 0; n-- { 1798 | historicalTickLast := HistoricalTickLast{} 1799 | historicalTickLast.Time = msgBuf.readInt() 1800 | 1801 | mask := msgBuf.readInt() 1802 | tickAttribLast := TickAttribLast{} 1803 | tickAttribLast.PastLimit = mask&1 != 0 1804 | tickAttribLast.Unreported = mask&2 != 0 1805 | 1806 | historicalTickLast.TickAttribLast = tickAttribLast 1807 | historicalTickLast.Price = msgBuf.readFloat() 1808 | historicalTickLast.Size = msgBuf.readInt() 1809 | historicalTickLast.Exchange = msgBuf.readString() 1810 | historicalTickLast.SpecialConditions = msgBuf.readString() 1811 | ticks = append(ticks, historicalTickLast) 1812 | } 1813 | 1814 | done := msgBuf.readBool() 1815 | 1816 | d.wrapper.HistoricalTicksLast(reqID, ticks, done) 1817 | } 1818 | func (d *ibDecoder) processTickByTickMsg(msgBuf *MsgBuffer) { 1819 | reqID := msgBuf.readInt() 1820 | tickType := msgBuf.readInt() 1821 | time := msgBuf.readInt() 1822 | 1823 | switch tickType { 1824 | case 0: 1825 | case 1, 2: 1826 | price := msgBuf.readFloat() 1827 | size := msgBuf.readInt() 1828 | 1829 | mask := msgBuf.readInt() 1830 | tickAttribLast := TickAttribLast{} 1831 | tickAttribLast.PastLimit = mask&1 != 0 1832 | tickAttribLast.Unreported = mask&2 != 0 1833 | 1834 | exchange := msgBuf.readString() 1835 | specialConditions := msgBuf.readString() 1836 | 1837 | d.wrapper.TickByTickAllLast(reqID, tickType, time, price, size, tickAttribLast, exchange, specialConditions) 1838 | case 3: 1839 | bidPrice := msgBuf.readFloat() 1840 | askPrice := msgBuf.readFloat() 1841 | bidSize := msgBuf.readInt() 1842 | askSize := msgBuf.readInt() 1843 | 1844 | mask := msgBuf.readInt() 1845 | tickAttribBidAsk := TickAttribBidAsk{} 1846 | tickAttribBidAsk.BidPastLow = mask&1 != 0 1847 | tickAttribBidAsk.AskPastHigh = mask&2 != 0 1848 | 1849 | d.wrapper.TickByTickBidAsk(reqID, time, bidPrice, askPrice, bidSize, askSize, tickAttribBidAsk) 1850 | case 4: 1851 | midPoint := msgBuf.readFloat() 1852 | 1853 | d.wrapper.TickByTickMidPoint(reqID, time, midPoint) 1854 | } 1855 | } 1856 | 1857 | func (d *ibDecoder) processOrderBoundMsg(msgBuf *MsgBuffer) { 1858 | reqID := msgBuf.readInt() 1859 | apiClientID := msgBuf.readInt() 1860 | apiOrderID := msgBuf.readInt() 1861 | 1862 | d.wrapper.OrderBound(reqID, apiClientID, apiOrderID) 1863 | 1864 | } 1865 | 1866 | func (d *ibDecoder) processMarketDepthL2Msg(msgBuf *MsgBuffer) { 1867 | _ = msgBuf.readString() 1868 | _ = msgBuf.readInt() 1869 | reqID := msgBuf.readInt() 1870 | 1871 | position := msgBuf.readInt() 1872 | marketMaker := msgBuf.readString() 1873 | operation := msgBuf.readInt() 1874 | side := msgBuf.readInt() 1875 | price := msgBuf.readFloat() 1876 | size := msgBuf.readInt() 1877 | 1878 | isSmartDepth := false 1879 | if d.version >= mMIN_SERVER_VER_SMART_DEPTH { 1880 | isSmartDepth = msgBuf.readBool() 1881 | } 1882 | 1883 | d.wrapper.UpdateMktDepthL2(reqID, position, marketMaker, operation, side, price, size, isSmartDepth) 1884 | } 1885 | 1886 | func (d *ibDecoder) processCompletedOrderMsg(msgBuf *MsgBuffer) { 1887 | o := &Order{} 1888 | c := &Contract{} 1889 | orderState := &OrderState{} 1890 | 1891 | version := UNSETINT 1892 | 1893 | c.ContractID = msgBuf.readInt() 1894 | c.Symbol = msgBuf.readString() 1895 | c.SecurityType = msgBuf.readString() 1896 | c.Expiry = msgBuf.readString() 1897 | c.Strike = msgBuf.readFloat() 1898 | c.Right = msgBuf.readString() 1899 | 1900 | if d.version >= 32 { 1901 | c.Multiplier = msgBuf.readString() 1902 | } 1903 | 1904 | c.Exchange = msgBuf.readString() 1905 | c.Currency = msgBuf.readString() 1906 | c.LocalSymbol = msgBuf.readString() 1907 | 1908 | if d.version >= 32 { 1909 | c.TradingClass = msgBuf.readString() 1910 | } 1911 | 1912 | o.Action = msgBuf.readString() 1913 | if d.version >= mMIN_SERVER_VER_FRACTIONAL_POSITIONS { 1914 | o.TotalQuantity = msgBuf.readFloat() 1915 | } else { 1916 | o.TotalQuantity = float64(msgBuf.readInt()) 1917 | } 1918 | 1919 | o.OrderType = msgBuf.readString() 1920 | if version < 29 { 1921 | o.LimitPrice = msgBuf.readFloat() 1922 | } else { 1923 | o.LimitPrice = msgBuf.readFloatCheckUnset() 1924 | } 1925 | 1926 | if version < 30 { 1927 | o.AuxPrice = msgBuf.readFloat() 1928 | } else { 1929 | o.AuxPrice = msgBuf.readFloatCheckUnset() 1930 | } 1931 | 1932 | o.TIF = msgBuf.readString() 1933 | o.OCAGroup = msgBuf.readString() 1934 | o.Account = msgBuf.readString() 1935 | o.OpenClose = msgBuf.readString() 1936 | 1937 | o.Origin = msgBuf.readInt() 1938 | 1939 | o.OrderRef = msgBuf.readString() 1940 | o.PermID = msgBuf.readInt() 1941 | 1942 | o.OutsideRTH = msgBuf.readBool() 1943 | o.Hidden = msgBuf.readBool() 1944 | o.DiscretionaryAmount = msgBuf.readFloat() 1945 | o.GoodAfterTime = msgBuf.readString() 1946 | 1947 | o.FAGroup = msgBuf.readString() 1948 | o.FAMethod = msgBuf.readString() 1949 | o.FAPercentage = msgBuf.readString() 1950 | o.FAProfile = msgBuf.readString() 1951 | 1952 | if d.version >= mMIN_SERVER_VER_MODELS_SUPPORT { 1953 | o.ModelCode = msgBuf.readString() 1954 | } 1955 | 1956 | o.GoodTillDate = msgBuf.readString() 1957 | 1958 | o.Rule80A = msgBuf.readString() 1959 | o.PercentOffset = msgBuf.readFloatCheckUnset() //show_unset 1960 | o.SettlingFirm = msgBuf.readString() 1961 | 1962 | //ShortSaleParams 1963 | o.ShortSaleSlot = msgBuf.readInt() 1964 | o.DesignatedLocation = msgBuf.readString() 1965 | 1966 | if d.version == mMIN_SERVER_VER_SSHORTX_OLD { 1967 | _ = msgBuf.readString() 1968 | } else if version >= 23 { 1969 | o.ExemptCode = msgBuf.readInt() 1970 | } 1971 | 1972 | //BoxOrderParams 1973 | o.StartingPrice = msgBuf.readFloatCheckUnset() //show_unset 1974 | o.StockRefPrice = msgBuf.readFloatCheckUnset() //show_unset 1975 | o.Delta = msgBuf.readFloatCheckUnset() //show_unset 1976 | 1977 | //PegToStkOrVolOrderParams 1978 | o.StockRangeLower = msgBuf.readFloatCheckUnset() //show_unset 1979 | o.StockRangeUpper = msgBuf.readFloatCheckUnset() //show_unset 1980 | 1981 | o.DisplaySize = msgBuf.readInt() 1982 | o.SweepToFill = msgBuf.readBool() 1983 | o.AllOrNone = msgBuf.readBool() 1984 | o.MinQty = msgBuf.readIntCheckUnset() //show_unset 1985 | o.OCAType = msgBuf.readInt() 1986 | o.TriggerMethod = msgBuf.readInt() 1987 | 1988 | //VolOrderParams 1989 | o.Volatility = msgBuf.readFloatCheckUnset() //show_unset 1990 | o.VolatilityType = msgBuf.readInt() 1991 | o.DeltaNeutralOrderType = msgBuf.readString() 1992 | o.DeltaNeutralAuxPrice = msgBuf.readFloatCheckUnset() 1993 | 1994 | if version >= 27 && o.DeltaNeutralOrderType != "" { 1995 | o.DeltaNeutralContractID = msgBuf.readInt() 1996 | } 1997 | 1998 | if version >= 31 && o.DeltaNeutralOrderType != "" { 1999 | o.DeltaNeutralShortSale = msgBuf.readBool() 2000 | o.DeltaNeutralShortSaleSlot = msgBuf.readInt() 2001 | o.DeltaNeutralDesignatedLocation = msgBuf.readString() 2002 | } 2003 | 2004 | o.ContinuousUpdate = msgBuf.readBool() 2005 | o.ReferencePriceType = msgBuf.readInt() 2006 | 2007 | //TrailParams 2008 | o.TrailStopPrice = msgBuf.readFloatCheckUnset() 2009 | if version >= 30 { 2010 | o.TrailingPercent = msgBuf.readFloatCheckUnset() //show_unset 2011 | } 2012 | 2013 | //ComboLegs 2014 | c.ComboLegsDescription = msgBuf.readString() 2015 | if version >= 29 { 2016 | combolegN := msgBuf.readInt() 2017 | c.ComboLegs = make([]ComboLeg, 0, combolegN) 2018 | for ; combolegN > 0; combolegN-- { 2019 | // fmt.Println("comboLegsCount:", comboLegsCount) 2020 | comboleg := ComboLeg{} 2021 | comboleg.ContractID = msgBuf.readInt() 2022 | comboleg.Ratio = msgBuf.readInt() 2023 | comboleg.Action = msgBuf.readString() 2024 | comboleg.Exchange = msgBuf.readString() 2025 | comboleg.OpenClose = msgBuf.readInt() 2026 | comboleg.ShortSaleSlot = msgBuf.readInt() 2027 | comboleg.DesignatedLocation = msgBuf.readString() 2028 | comboleg.ExemptCode = msgBuf.readInt() 2029 | c.ComboLegs = append(c.ComboLegs, comboleg) 2030 | } 2031 | 2032 | orderComboLegN := msgBuf.readInt() 2033 | o.OrderComboLegs = make([]OrderComboLeg, 0, orderComboLegN) 2034 | for ; orderComboLegN > 0; orderComboLegN-- { 2035 | orderComboLeg := OrderComboLeg{} 2036 | orderComboLeg.Price = msgBuf.readFloatCheckUnset() 2037 | o.OrderComboLegs = append(o.OrderComboLegs, orderComboLeg) 2038 | } 2039 | } 2040 | 2041 | //SmartComboRoutingParams 2042 | if version >= 26 { 2043 | n := msgBuf.readInt() 2044 | o.SmartComboRoutingParams = make([]TagValue, 0, n) 2045 | for ; n > 0; n-- { 2046 | tagValue := TagValue{} 2047 | tagValue.Tag = msgBuf.readString() 2048 | tagValue.Value = msgBuf.readString() 2049 | o.SmartComboRoutingParams = append(o.SmartComboRoutingParams, tagValue) 2050 | } 2051 | } 2052 | 2053 | //ScaleOrderParams 2054 | if version >= 20 { 2055 | o.ScaleInitLevelSize = msgBuf.readIntCheckUnset() //show_unset 2056 | o.ScaleSubsLevelSize = msgBuf.readIntCheckUnset() //show_unset 2057 | } else { 2058 | o.NotSuppScaleNumComponents = msgBuf.readIntCheckUnset() 2059 | o.ScaleInitLevelSize = msgBuf.readIntCheckUnset() 2060 | } 2061 | 2062 | o.ScalePriceIncrement = msgBuf.readFloatCheckUnset() 2063 | 2064 | if version >= 28 && o.ScalePriceIncrement != UNSETFLOAT && o.ScalePriceIncrement > 0.0 { 2065 | o.ScalePriceAdjustValue = msgBuf.readFloatCheckUnset() 2066 | o.ScalePriceAdjustInterval = msgBuf.readIntCheckUnset() 2067 | o.ScaleProfitOffset = msgBuf.readFloatCheckUnset() 2068 | o.ScaleAutoReset = msgBuf.readBool() 2069 | o.ScaleInitPosition = msgBuf.readIntCheckUnset() 2070 | o.ScaleInitFillQty = msgBuf.readIntCheckUnset() 2071 | o.ScaleRandomPercent = msgBuf.readBool() 2072 | } 2073 | 2074 | //HedgeParams 2075 | if version >= 24 { 2076 | o.HedgeType = msgBuf.readString() 2077 | if o.HedgeType != "" { 2078 | o.HedgeParam = msgBuf.readString() 2079 | } 2080 | } 2081 | 2082 | o.ClearingAccount = msgBuf.readString() 2083 | o.ClearingIntent = msgBuf.readString() 2084 | 2085 | if version >= 22 { 2086 | o.NotHeld = msgBuf.readBool() 2087 | } 2088 | 2089 | // DeltaNeutral 2090 | if version >= 20 { 2091 | deltaNeutralContractPresent := msgBuf.readBool() 2092 | if deltaNeutralContractPresent { 2093 | c.DeltaNeutralContract = new(DeltaNeutralContract) 2094 | c.DeltaNeutralContract.ContractID = msgBuf.readInt() 2095 | c.DeltaNeutralContract.Delta = msgBuf.readFloat() 2096 | c.DeltaNeutralContract.Price = msgBuf.readFloat() 2097 | } 2098 | } 2099 | 2100 | if version >= 21 { 2101 | o.AlgoStrategy = msgBuf.readString() 2102 | if o.AlgoStrategy != "" { 2103 | n := msgBuf.readInt() 2104 | o.AlgoParams = make([]TagValue, 0, n) 2105 | for ; n > 0; n-- { 2106 | tagValue := TagValue{} 2107 | tagValue.Tag = msgBuf.readString() 2108 | tagValue.Value = msgBuf.readString() 2109 | o.AlgoParams = append(o.AlgoParams, tagValue) 2110 | } 2111 | } 2112 | } 2113 | 2114 | if version >= 33 { 2115 | o.Solictied = msgBuf.readBool() 2116 | } 2117 | 2118 | orderState.Status = msgBuf.readString() 2119 | 2120 | // VolRandomizeFlags 2121 | if version >= 34 { 2122 | o.RandomizeSize = msgBuf.readBool() 2123 | o.RandomizePrice = msgBuf.readBool() 2124 | } 2125 | 2126 | if d.version >= mMIN_SERVER_VER_PEGGED_TO_BENCHMARK { 2127 | // PegToBenchParams 2128 | if o.OrderType == "PEG BENCH" { 2129 | o.ReferenceContractID = msgBuf.readInt() 2130 | o.IsPeggedChangeAmountDecrease = msgBuf.readBool() 2131 | o.PeggedChangeAmount = msgBuf.readFloat() 2132 | o.ReferenceChangeAmount = msgBuf.readFloat() 2133 | o.ReferenceExchangeID = msgBuf.readString() 2134 | } 2135 | 2136 | // Conditions 2137 | if n := msgBuf.readInt(); n > 0 { 2138 | o.Conditions = make([]OrderConditioner, 0, n) 2139 | for ; n > 0; n-- { 2140 | conditionType := msgBuf.readInt() 2141 | cond, _ := InitOrderCondition(conditionType) 2142 | cond.decode(msgBuf) 2143 | 2144 | o.Conditions = append(o.Conditions, cond) 2145 | } 2146 | o.ConditionsIgnoreRth = msgBuf.readBool() 2147 | o.ConditionsCancelOrder = msgBuf.readBool() 2148 | } 2149 | } 2150 | 2151 | // StopPriceAndLmtPriceOffset 2152 | o.TrailStopPrice = msgBuf.readFloat() 2153 | o.LimitPriceOffset = msgBuf.readFloat() 2154 | 2155 | if d.version >= mMIN_SERVER_VER_CASH_QTY { 2156 | o.CashQty = msgBuf.readFloat() 2157 | } 2158 | 2159 | if d.version >= mMIN_SERVER_VER_AUTO_PRICE_FOR_HEDGE { 2160 | o.DontUseAutoPriceForHedge = msgBuf.readBool() 2161 | } 2162 | 2163 | if d.version >= mMIN_SERVER_VER_ORDER_CONTAINER { 2164 | o.IsOmsContainer = msgBuf.readBool() 2165 | } 2166 | 2167 | o.AutoCancelDate = msgBuf.readString() 2168 | o.FilledQuantity = msgBuf.readFloat() 2169 | o.RefFuturesConID = msgBuf.readInt() 2170 | o.AutoCancelParent = msgBuf.readBool() 2171 | o.Shareholder = msgBuf.readString() 2172 | o.ImbalanceOnly = msgBuf.readBool() 2173 | o.RouteMarketableToBbo = msgBuf.readBool() 2174 | o.ParentPermID = msgBuf.readInt() 2175 | 2176 | orderState.CompletedTime = msgBuf.readString() 2177 | orderState.CompletedStatus = msgBuf.readString() 2178 | 2179 | d.wrapper.CompletedOrder(c, o, orderState) 2180 | } 2181 | 2182 | // ---------------------------------------------------- 2183 | 2184 | func (d *ibDecoder) processCompletedOrdersEndMsg(msgBuf *MsgBuffer) { 2185 | d.wrapper.CompletedOrdersEnd() 2186 | } 2187 | 2188 | func (d *ibDecoder) processReplaceFAEndMsg(msgBuf *MsgBuffer) { 2189 | reqID := msgBuf.readInt() 2190 | text := msgBuf.readString() 2191 | 2192 | d.wrapper.ReplaceFAEnd(reqID, text) 2193 | } 2194 | 2195 | func (d *ibDecoder) processWshMetaDataMsg(msgBuf *MsgBuffer) { 2196 | reqID := msgBuf.readInt() 2197 | dataJson := msgBuf.readString() 2198 | 2199 | d.wrapper.WshMetaData(reqID, dataJson) 2200 | } 2201 | 2202 | func (d *ibDecoder) processWshEventDataMsg(msgBuf *MsgBuffer) { 2203 | reqID := msgBuf.readInt() 2204 | dataJson := msgBuf.readString() 2205 | 2206 | d.wrapper.WshEventData(reqID, dataJson) 2207 | } 2208 | -------------------------------------------------------------------------------- /decoder_test.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | var decoder = &ibDecoder{ 9 | wrapper: &Wrapper{}, 10 | } 11 | 12 | func init() { 13 | decoder.setVersion(151) 14 | decoder.setmsgID2process() 15 | } 16 | 17 | func TestDecodeLongName(t *testing.T) { 18 | longName := "\\xef" 19 | fmt.Println(longName) 20 | } 21 | 22 | func BenchmarkDecode(b *testing.B) { 23 | // log, _ = zap.NewDevelopment() 24 | msgUpdateAccountValue := []byte{54, 0, 50, 0, 78, 101, 116, 76, 105, 113, 117, 105, 100, 97, 116, 105, 111, 110, 66, 121, 67, 117, 114, 114, 101, 110, 99, 121, 0, 45, 49, 49, 48, 53, 54, 49, 50, 0, 72, 75, 68, 0, 68, 85, 49, 51, 56, 50, 56, 51, 55, 0} 25 | msgHistoricalDataUpdate := []byte{57, 48, 0, 50, 0, 50, 48, 57, 0, 50, 48, 50, 48, 48, 53, 50, 54, 32, 32, 49, 54, 58, 50, 48, 58, 48, 48, 0, 50, 51, 52, 48, 51, 0, 50, 51, 52, 48, 52, 0, 50, 51, 52, 48, 54, 0, 50, 51, 52, 48, 48, 0, 50, 51, 52, 48, 51, 46, 52, 51, 56, 49, 54, 50, 53, 52, 52, 49, 55, 0, 50, 56, 51, 0} 26 | msgUpdateMktDepthL2 := []byte{49, 51, 0, 49, 0, 51, 0, 48, 0, 72, 75, 70, 69, 0, 49, 0, 49, 0, 50, 51, 52, 48, 51, 0, 56, 0, 49, 0} 27 | msgError := []byte{52, 0, 50, 0, 45, 49, 0, 50, 49, 48, 54, 0, 72, 77, 68, 83, 32, 100, 97, 116, 97, 32, 102, 97, 114, 109, 32, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 32, 105, 115, 32, 79, 75, 58, 102, 117, 110, 100, 102, 97, 114, 109, 0} 28 | // var updateAccountValueMsgBuf = NewMsgBuffer(nil) 29 | for i := 0; i < b.N; i++ { 30 | // updateAccountValueMsgBuf.Write(msgBytes) 31 | decoder.interpret(msgUpdateAccountValue) 32 | decoder.interpret(msgHistoricalDataUpdate) 33 | decoder.interpret(msgUpdateMktDepthL2) 34 | decoder.interpret(msgError) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | // IbError is ib internal errors 4 | type IbError struct { 5 | code int64 6 | msg string 7 | } 8 | 9 | func (ie IbError) Error() string { 10 | return ie.msg 11 | } 12 | 13 | var ( 14 | ALREADY_CONNECTED = IbError{501, "Already connected."} 15 | CONNECT_FAIL = IbError{502, `Couldn't connect to TWS. Confirm that "Enable ActiveX and Socket EClients" 16 | is enabled and connection port is the same as "Socket Port" on the 17 | TWS "Edit->Global Configuration...->API->Settings" menu. Live Trading ports: 18 | TWS: 7496; IB Gateway: 4001. Simulated Trading ports for new installations 19 | of version 954.1 or newer: TWS: 7497; IB Gateway: 4002`} 20 | UPDATE_TWS = IbError{503, "The TWS is out of date and must be upgraded."} 21 | NOT_CONNECTED = IbError{504, "Not connected"} 22 | UNKNOWN_ID = IbError{505, "Fatal Error: Unknown message id."} 23 | UNSUPPORTED_VERSION = IbError{506, "Unsupported version"} 24 | BAD_LENGTH = IbError{507, "Bad message length"} 25 | BAD_MESSAGE = IbError{508, "Bad message"} 26 | SOCKET_EXCEPTION = IbError{509, "Exception caught while reading socket - "} 27 | FAIL_CREATE_SOCK = IbError{520, "Failed to create socket"} 28 | SSL_FAIL = IbError{530, "SSL specific error: "} 29 | ) 30 | -------------------------------------------------------------------------------- /execution.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import "fmt" 4 | 5 | // Execution is the information of an order`s execution 6 | type Execution struct { 7 | ExecID string 8 | Time string 9 | AccountCode string 10 | Exchange string 11 | Side string 12 | Shares float64 13 | Price float64 14 | PermID int64 15 | ClientID int64 16 | OrderID int64 17 | Liquidation int64 18 | CumQty float64 19 | AveragePrice float64 20 | OrderRef string 21 | EVRule string 22 | EVMultiplier float64 23 | ModelCode string 24 | LastLiquidity int64 25 | } 26 | 27 | func (e Execution) String() string { 28 | return fmt.Sprintf("ExecId: %s, Time: %s, Account: %s, Exchange: %s, Side: %s, Shares: %f, Price: %f, PermId: %d, ClientId: %d, OrderId: %d, Liquidation: %d, CumQty: %f, AvgPrice: %f, OrderRef: %s, EvRule: %s, EvMultiplier: %f, ModelCode: %s, LastLiquidity: %d", 29 | e.ExecID, e.Time, e.AccountCode, e.Exchange, e.Side, e.Shares, e.Price, e.PermID, e.ClientID, e.OrderID, e.Liquidation, e.CumQty, e.AveragePrice, e.OrderRef, e.EVRule, e.EVMultiplier, e.ModelCode, e.LastLiquidity) 30 | } 31 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hadrianl/ibapi 2 | 3 | go 1.14 4 | 5 | require ( 6 | go.uber.org/multierr v1.6.0 // indirect 7 | go.uber.org/zap v1.16.0 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 7 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 8 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 9 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 10 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 11 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 12 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 13 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 14 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 15 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 16 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 17 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 18 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 19 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 20 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 21 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 22 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 23 | go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= 24 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 25 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 26 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 27 | go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= 28 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 29 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 30 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 31 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= 32 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 33 | go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= 34 | go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= 35 | go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= 36 | go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 37 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 38 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 39 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= 40 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 41 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 42 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 43 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 44 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 45 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 46 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 47 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 48 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 49 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 50 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 51 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 52 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= 53 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 54 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 55 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 56 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 57 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 58 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 59 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 60 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 61 | honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= 62 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 63 | -------------------------------------------------------------------------------- /order.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | const ( 8 | CUSTOMER int64 = iota 9 | FIRM 10 | UNKNOWN 11 | ) 12 | 13 | const ( 14 | AUCTION_UNSET int64 = iota 15 | AUCTION_MATCH 16 | AUCTION_IMPROVEMENT 17 | AUCTION_TRANSPARENT 18 | ) 19 | 20 | //Order is the origin type of order,do not try to new one unless you definitely know how to fill all the fields!Use NewDefaultOrder instead! 21 | type Order struct { 22 | OrderID int64 23 | ClientID int64 24 | PermID int64 25 | Action string 26 | TotalQuantity float64 27 | OrderType string 28 | LimitPrice float64 `default:"UNSETFLOAT"` 29 | AuxPrice float64 `default:"UNSETFLOAT"` 30 | TIF string 31 | ActiveStartTime string 32 | ActiveStopTime string 33 | OCAGroup string 34 | OCAType int64 // 1 = CANCEL_WITH_BLOCK, 2 = REDUCE_WITH_BLOCK, 3 = REDUCE_NON_BLOCK 35 | OrderRef string 36 | Transmit bool `default:"true"` 37 | ParentID int64 38 | BlockOrder bool 39 | SweepToFill bool 40 | DisplaySize int64 41 | TriggerMethod int64 // 0=Default, 1=Double_Bid_Ask, 2=Last, 3=Double_Last, 4=Bid_Ask, 7=Last_or_Bid_Ask, 8=Mid-point 42 | OutsideRTH bool 43 | Hidden bool 44 | GoodAfterTime string // Format: 20060505 08:00:00 {time zone} 45 | GoodTillDate string // Format: 20060505 08:00:00 {time zone} 46 | OverridePercentageConstraints bool 47 | Rule80A string // Individual = 'I', Agency = 'A', AgentOtherMember = 'W', IndividualPTIA = 'J', AgencyPTIA = 'U', AgentOtherMemberPTIA = 'M', IndividualPT = 'K', AgencyPT = 'Y', AgentOtherMemberPT = 'N' 48 | AllOrNone bool 49 | MinQty int64 `default:"UNSETINT"` 50 | PercentOffset float64 `default:"UNSETFLOAT"` // REL orders only 51 | TrailStopPrice float64 `default:"UNSETFLOAT"` 52 | TrailingPercent float64 `default:"UNSETFLOAT"` // TRAILLIMIT orders only 53 | //---- financial advisors only ----- 54 | FAGroup string 55 | FAProfile string 56 | FAMethod string 57 | FAPercentage string 58 | // --------------------------------- 59 | // ---------institutional only------ 60 | OpenClose string // O=Open, C=Close 61 | Origin int64 // 0=Customer, 1=Firm 62 | ShortSaleSlot int64 // 1 if you hold the shares, 2 if they will be delivered from elsewhere. Only for Action=SSHORT 63 | DesignatedLocation string // used only when shortSaleSlot=2 64 | ExemptCode int64 `default:"-1"` 65 | // --------------------------------- 66 | // ------- SMART routing only ------ 67 | DiscretionaryAmount float64 68 | ETradeOnly bool `default:"true"` 69 | FirmQuoteOnly bool `default:"true"` 70 | NBBOPriceCap float64 `default:"UNSETFLOAT"` 71 | OptOutSmartRouting bool 72 | // -------------------------------- 73 | // ---BOX exchange orders only ---- 74 | AuctionStrategy int64 75 | StartingPrice float64 `default:"UNSETFLOAT"` 76 | StockRefPrice float64 `default:"UNSETFLOAT"` 77 | Delta float64 `default:"UNSETFLOAT"` 78 | // -------------------------------- 79 | // --pegged to stock and VOL orders only-- 80 | StockRangeLower float64 `default:"UNSETFLOAT"` 81 | StockRangeUpper float64 `default:"UNSETFLOAT"` 82 | 83 | RandomizePrice bool 84 | RandomizeSize bool 85 | 86 | // ---VOLATILITY ORDERS ONLY-------- 87 | Volatility float64 `default:"UNSETFLOAT"` 88 | VolatilityType int64 `default:"UNSETINT"` 89 | DeltaNeutralOrderType string 90 | DeltaNeutralAuxPrice float64 `default:"UNSETFLOAT"` 91 | DeltaNeutralContractID int64 92 | DeltaNeutralSettlingFirm string 93 | DeltaNeutralClearingAccount string 94 | DeltaNeutralClearingIntent string 95 | DeltaNeutralOpenClose string 96 | DeltaNeutralShortSale bool 97 | DeltaNeutralShortSaleSlot int64 98 | DeltaNeutralDesignatedLocation string 99 | ContinuousUpdate bool 100 | ReferencePriceType int64 `default:"UNSETINT"` // 1=Average, 2 = BidOrAsk 101 | // DeltaNeutral DeltaNeutralData `when:"DeltaNeutralOrderType" cond:"is" value:""` 102 | // ------------------------------------- 103 | // ------COMBO ORDERS ONLY----------- 104 | BasisPoints float64 `default:"UNSETFLOAT"` // EFP orders only 105 | BasisPointsType int64 `default:"UNSETINT"` // EFP orders only 106 | // ----------------------------------- 107 | //-----------SCALE ORDERS ONLY------------ 108 | ScaleInitLevelSize int64 `default:"UNSETINT"` 109 | ScaleSubsLevelSize int64 `default:"UNSETINT"` 110 | ScalePriceIncrement float64 `default:"UNSETFLOAT"` 111 | ScalePriceAdjustValue float64 `default:"UNSETFLOAT"` 112 | ScalePriceAdjustInterval int64 `default:"UNSETINT"` 113 | ScaleProfitOffset float64 `default:"UNSETFLOAT"` 114 | ScaleAutoReset bool 115 | ScaleInitPosition int64 `default:"UNSETINT"` 116 | ScaleInitFillQty int64 `default:"UNSETINT"` 117 | ScaleRandomPercent bool 118 | ScaleTable string 119 | NotSuppScaleNumComponents int64 120 | //-------------------------------------- 121 | // ---------HEDGE ORDERS-------------- 122 | HedgeType string 123 | HedgeParam string 124 | //-------------------------------------- 125 | //-----------Clearing info ---------------- 126 | Account string 127 | SettlingFirm string 128 | ClearingAccount string // True beneficiary of the order 129 | ClearingIntent string // "" (Default), "IB", "Away", "PTA" (PostTrade) 130 | // ---------------------------------------- 131 | // --------- ALGO ORDERS ONLY -------------- 132 | AlgoStrategy string 133 | 134 | AlgoParams []TagValue 135 | SmartComboRoutingParams []TagValue 136 | AlgoID string 137 | // ----------------------------------------- 138 | 139 | // ----------what if order ------------------- 140 | WhatIf bool 141 | 142 | // --------------Not Held ------------------ 143 | NotHeld bool 144 | Solictied bool 145 | //-------------------------------------- 146 | 147 | // models 148 | ModelCode string 149 | 150 | // ------order combo legs ----------------- 151 | OrderComboLegs []OrderComboLeg 152 | OrderMiscOptions []TagValue 153 | //---------------------------------------- 154 | //-----------VER PEG2BENCH fields---------- 155 | ReferenceContractID int64 156 | PeggedChangeAmount float64 157 | IsPeggedChangeAmountDecrease bool 158 | ReferenceChangeAmount float64 159 | ReferenceExchangeID string 160 | AdjustedOrderType string 161 | TriggerPrice float64 `default:"UNSETFLOAT"` 162 | AdjustedStopPrice float64 `default:"UNSETFLOAT"` 163 | AdjustedStopLimitPrice float64 `default:"UNSETFLOAT"` 164 | AdjustedTrailingAmount float64 `default:"UNSETFLOAT"` 165 | AdjustableTrailingUnit int64 166 | LimitPriceOffset float64 `default:"UNSETFLOAT"` 167 | 168 | Conditions []OrderConditioner 169 | ConditionsCancelOrder bool 170 | ConditionsIgnoreRth bool 171 | 172 | //------ext operator-------------- 173 | ExtOperator string 174 | 175 | //-----native cash quantity -------- 176 | CashQty float64 `default:"UNSETFLOAT"` 177 | 178 | //-------------------------------- 179 | Mifid2DecisionMaker string 180 | Mifid2DecisionAlgo string 181 | Mifid2ExecutionTrader string 182 | Mifid2ExecutionAlgo string 183 | 184 | //------------- 185 | DontUseAutoPriceForHedge bool 186 | 187 | IsOmsContainer bool 188 | 189 | DiscretionaryUpToLimitPrice bool 190 | 191 | AutoCancelDate string 192 | FilledQuantity float64 `default:"UNSETFLOAT"` 193 | RefFuturesConID int64 194 | AutoCancelParent bool 195 | Shareholder string 196 | ImbalanceOnly bool 197 | RouteMarketableToBbo bool 198 | ParentPermID int64 199 | 200 | UsePriceMgmtAlgo bool 201 | Duration int64 `default:"UNSETINT"` 202 | PostToAts int64 `default:"UNSETINT"` 203 | 204 | SoftDollarTier SoftDollarTier 205 | } 206 | 207 | func (o Order) String() string { 208 | s := fmt.Sprintf("Order -- <%s %s %.2f@%f %s> --", 209 | o.OrderID, 210 | o.ClientID, 211 | o.PermID, 212 | o.OrderType, 213 | o.Action, 214 | o.TotalQuantity, 215 | o.LimitPrice, 216 | o.TIF) 217 | 218 | for i, leg := range o.OrderComboLegs { 219 | s += fmt.Sprintf(" CMB<%d>-%s", i, leg) 220 | } 221 | 222 | for i, cond := range o.Conditions { 223 | s += fmt.Sprintf(" COND<%d>-%s", i, cond) 224 | } 225 | 226 | return s 227 | } 228 | 229 | // OrderState is the state of Order 230 | type OrderState struct { 231 | Status string 232 | InitialMarginBefore string 233 | InitialMarginChange string 234 | InitialMarginAfter string 235 | MaintenanceMarginBefore string 236 | MaintenanceMarginChange string 237 | MaintenanceMarginAfter string 238 | EquityWithLoanBefore string 239 | EquityWithLoanChange string 240 | EquityWithLoanAfter string 241 | Commission float64 `default:"UNSETFLOAT"` 242 | MinCommission float64 `default:"UNSETFLOAT"` 243 | MaxCommission float64 `default:"UNSETFLOAT"` 244 | CommissionCurrency string 245 | WarningText string 246 | CompletedTime string 247 | CompletedStatus string 248 | } 249 | 250 | func (state OrderState) String() string { 251 | return fmt.Sprintf( 252 | "OrderState", 253 | state.Status, 254 | state.Commission, 255 | state.CommissionCurrency, 256 | state.CompletedTime, 257 | state.CompletedStatus, 258 | ) 259 | } 260 | 261 | // SoftDollarTier is a container for storing Soft Dollar Tier information 262 | type SoftDollarTier struct { 263 | Name string 264 | Value string 265 | DisplayName string 266 | } 267 | 268 | func (s SoftDollarTier) String() string { 269 | return fmt.Sprintf("SoftDollarTier", 270 | s.Name, 271 | s.Value, 272 | s.DisplayName) 273 | } 274 | 275 | // NewOrder create a default order 276 | func NewOrder() *Order { 277 | order := &Order{} 278 | order.LimitPrice = UNSETFLOAT 279 | order.AuxPrice = UNSETFLOAT 280 | 281 | order.Transmit = true 282 | 283 | order.MinQty = UNSETINT 284 | order.PercentOffset = UNSETFLOAT 285 | order.TrailStopPrice = UNSETFLOAT 286 | order.TrailingPercent = UNSETFLOAT 287 | 288 | order.OpenClose = "O" 289 | 290 | order.ExemptCode = -1 291 | 292 | order.ETradeOnly = true 293 | order.FirmQuoteOnly = true 294 | order.NBBOPriceCap = UNSETFLOAT 295 | 296 | order.AuctionStrategy = AUCTION_UNSET 297 | order.StartingPrice = UNSETFLOAT 298 | order.StockRefPrice = UNSETFLOAT 299 | order.Delta = UNSETFLOAT 300 | 301 | order.StockRangeLower = UNSETFLOAT 302 | order.StockRangeUpper = UNSETFLOAT 303 | 304 | order.Volatility = UNSETFLOAT 305 | order.VolatilityType = UNSETINT 306 | order.DeltaNeutralAuxPrice = UNSETFLOAT 307 | order.ReferencePriceType = UNSETINT 308 | 309 | order.BasisPoints = UNSETFLOAT 310 | order.BasisPointsType = UNSETINT 311 | 312 | order.ScaleInitLevelSize = UNSETINT 313 | order.ScaleSubsLevelSize = UNSETINT 314 | order.ScalePriceIncrement = UNSETFLOAT 315 | order.ScalePriceAdjustValue = UNSETFLOAT 316 | order.ScalePriceAdjustInterval = UNSETINT 317 | order.ScaleProfitOffset = UNSETFLOAT 318 | order.ScaleInitPosition = UNSETINT 319 | order.ScaleInitFillQty = UNSETINT 320 | 321 | order.TriggerPrice = UNSETFLOAT 322 | order.AdjustedStopPrice = UNSETFLOAT 323 | order.AdjustedStopLimitPrice = UNSETFLOAT 324 | order.AdjustedTrailingAmount = UNSETFLOAT 325 | order.LimitPriceOffset = UNSETFLOAT 326 | 327 | order.CashQty = UNSETFLOAT 328 | order.FilledQuantity = UNSETFLOAT 329 | order.Duration = UNSETINT 330 | order.PostToAts = UNSETINT 331 | 332 | return order 333 | } 334 | 335 | // NewLimitOrder create a limit order with action, limit price and quantity 336 | func NewLimitOrder(action string, lmtPrice float64, quantity float64) *Order { 337 | o := NewOrder() 338 | o.OrderType = "LMT" 339 | o.Action = action 340 | o.LimitPrice = lmtPrice 341 | o.TotalQuantity = quantity 342 | 343 | return o 344 | } 345 | 346 | // NewMarketOrder create a market order with action and quantity 347 | func NewMarketOrder(action string, quantity float64) *Order { 348 | o := NewOrder() 349 | o.OrderType = "MKT" 350 | o.Action = action 351 | o.TotalQuantity = quantity 352 | 353 | return o 354 | } 355 | -------------------------------------------------------------------------------- /orderCondition.go: -------------------------------------------------------------------------------- 1 | /* 2 | ordercondition contains several OrderCondition, such as Price, Time, Margin, Execution, Volume, PercentChange 3 | */ 4 | 5 | package ibapi 6 | 7 | import "go.uber.org/zap" 8 | 9 | type OrderConditioner interface { 10 | CondType() int64 11 | setCondType(condType int64) 12 | decode(*MsgBuffer) 13 | toFields() []interface{} 14 | } 15 | 16 | type OrderCondition struct { 17 | conditionType int64 18 | IsConjunctionConnection bool 19 | 20 | // Price = 1 21 | // Time = 3 22 | // Margin = 4 23 | // Execution = 5 24 | // Volume = 6 25 | // PercentChange = 7 26 | } 27 | 28 | func (oc OrderCondition) decode(msgBuf *MsgBuffer) { 29 | connector := msgBuf.readString() 30 | oc.IsConjunctionConnection = connector == "a" 31 | } 32 | 33 | func (oc OrderCondition) toFields() []interface{} { 34 | if oc.IsConjunctionConnection { 35 | return []interface{}{"a"} 36 | } 37 | return []interface{}{"o"} 38 | } 39 | 40 | func (oc OrderCondition) CondType() int64 { 41 | return oc.conditionType 42 | } 43 | 44 | func (oc OrderCondition) setCondType(condType int64) { 45 | oc.conditionType = condType 46 | } 47 | 48 | type ExecutionCondition struct { 49 | OrderCondition 50 | SecType string 51 | Exchange string 52 | Symbol string 53 | } 54 | 55 | func (ec ExecutionCondition) decode(msgBuf *MsgBuffer) { // 4 fields 56 | ec.OrderCondition.decode(msgBuf) 57 | ec.SecType = msgBuf.readString() 58 | ec.Exchange = msgBuf.readString() 59 | ec.Symbol = msgBuf.readString() 60 | } 61 | 62 | func (ec ExecutionCondition) toFields() []interface{} { 63 | return append(ec.OrderCondition.toFields(), ec.SecType, ec.Exchange, ec.Symbol) 64 | } 65 | 66 | type OperatorCondition struct { 67 | OrderCondition 68 | IsMore bool 69 | } 70 | 71 | func (oc OperatorCondition) decode(msgBuf *MsgBuffer) { // 2 fields 72 | oc.OrderCondition.decode(msgBuf) 73 | oc.IsMore = msgBuf.readBool() 74 | } 75 | 76 | func (oc OperatorCondition) toFields() []interface{} { 77 | return append(oc.OrderCondition.toFields(), oc.IsMore) 78 | } 79 | 80 | type MarginCondition struct { 81 | OperatorCondition 82 | Percent float64 83 | } 84 | 85 | func (mc MarginCondition) decode(msgBuf *MsgBuffer) { // 3 fields 86 | mc.OperatorCondition.decode(msgBuf) 87 | mc.Percent = msgBuf.readFloat() 88 | } 89 | 90 | func (mc MarginCondition) toFields() []interface{} { 91 | return append(mc.OperatorCondition.toFields(), mc.Percent) 92 | } 93 | 94 | type ContractCondition struct { 95 | OperatorCondition 96 | ConID int64 97 | Exchange string 98 | } 99 | 100 | func (cc ContractCondition) decode(msgBuf *MsgBuffer) { // 4 fields 101 | cc.OperatorCondition.decode(msgBuf) 102 | cc.ConID = msgBuf.readInt() 103 | cc.Exchange = msgBuf.readString() 104 | } 105 | 106 | func (cc ContractCondition) toFields() []interface{} { 107 | return append(cc.OperatorCondition.toFields(), cc.ConID, cc.Exchange) 108 | } 109 | 110 | type TimeCondition struct { 111 | OperatorCondition 112 | Time string 113 | } 114 | 115 | func (tc TimeCondition) decode(msgBuf *MsgBuffer) { // 3 fields 116 | tc.OperatorCondition.decode(msgBuf) 117 | // tc.Time = decodeTime(fields[2], "20060102") 118 | tc.Time = msgBuf.readString() 119 | } 120 | 121 | func (tc TimeCondition) toFields() []interface{} { 122 | return append(tc.OperatorCondition.toFields(), tc.Time) 123 | } 124 | 125 | type PriceCondition struct { 126 | ContractCondition 127 | Price float64 128 | TriggerMethod int64 129 | } 130 | 131 | func (pc PriceCondition) decode(msgBuf *MsgBuffer) { // 6 fields 132 | pc.ContractCondition.decode(msgBuf) 133 | pc.Price = msgBuf.readFloat() 134 | pc.TriggerMethod = msgBuf.readInt() 135 | } 136 | 137 | func (pc PriceCondition) toFields() []interface{} { 138 | return append(pc.ContractCondition.toFields(), pc.Price, pc.TriggerMethod) 139 | } 140 | 141 | type PercentChangeCondition struct { 142 | ContractCondition 143 | ChangePercent float64 144 | } 145 | 146 | func (pcc PercentChangeCondition) decode(msgBuf *MsgBuffer) { // 5 fields 147 | pcc.ContractCondition.decode(msgBuf) 148 | pcc.ChangePercent = msgBuf.readFloat() 149 | } 150 | 151 | func (pcc PercentChangeCondition) toFields() []interface{} { 152 | return append(pcc.ContractCondition.toFields(), pcc.ChangePercent) 153 | } 154 | 155 | type VolumeCondition struct { 156 | ContractCondition 157 | Volume int64 158 | } 159 | 160 | func (vc VolumeCondition) decode(msgBuf *MsgBuffer) { // 5 fields 161 | vc.ContractCondition.decode(msgBuf) 162 | vc.Volume = msgBuf.readInt() 163 | } 164 | 165 | func (vc VolumeCondition) toFields() []interface{} { 166 | return append(vc.ContractCondition.toFields(), vc.Volume) 167 | } 168 | 169 | func InitOrderCondition(conType int64) (OrderConditioner, int) { 170 | var cond OrderConditioner 171 | var condSize int 172 | switch conType { 173 | case 1: 174 | cond = PriceCondition{} 175 | cond.setCondType(1) 176 | condSize = 6 177 | case 3: 178 | cond = TimeCondition{} 179 | cond.setCondType(3) 180 | condSize = 3 181 | case 4: 182 | cond = MarginCondition{} 183 | cond.setCondType(4) 184 | condSize = 3 185 | case 5: 186 | cond = ExecutionCondition{} 187 | cond.setCondType(5) 188 | condSize = 4 189 | case 6: 190 | cond = VolumeCondition{} 191 | cond.setCondType(6) 192 | condSize = 5 193 | case 7: 194 | cond = PercentChangeCondition{} 195 | cond.setCondType(7) 196 | condSize = 5 197 | default: 198 | log.Panic("unkonwn conType", 199 | zap.Int64("conType", conType), 200 | ) 201 | } 202 | return cond, condSize 203 | } 204 | -------------------------------------------------------------------------------- /scanner.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import "fmt" 4 | 5 | // ScanData is the data retureed by IB, which matches the ScannerSubscription 6 | type ScanData struct { 7 | ContractDetails ContractDetails 8 | Rank int64 9 | Distance string 10 | Benchmark string 11 | Projection string 12 | Legs string 13 | } 14 | 15 | func (s ScanData) String() string { 16 | return fmt.Sprintf("Rank: %d, ContractDetails: %v, Distance: %s, Benchmark: %s, Projection: %s, Legs String: %s", 17 | s.Rank, 18 | s.ContractDetails, 19 | s.Distance, 20 | s.Benchmark, 21 | s.Projection, 22 | s.Legs) 23 | } 24 | 25 | // ScannerSubscription defines a market scanner request 26 | type ScannerSubscription struct { 27 | NumberOfRows int64 `default:"-1"` 28 | Instrument string 29 | LocationCode string 30 | ScanCode string 31 | AbovePrice float64 `default:"UNSETFLOAT"` 32 | BelowPrice float64 `default:"UNSETFLOAT"` 33 | AboveVolume int64 `default:"UNSETINT"` 34 | MarketCapAbove float64 `default:"UNSETFLOAT"` 35 | MarketCapBelow float64 `default:"UNSETFLOAT"` 36 | MoodyRatingAbove string 37 | MoodyRatingBelow string 38 | SpRatingAbove string 39 | SpRatingBelow string 40 | MaturityDateAbove string 41 | MaturityDateBelow string 42 | CouponRateAbove float64 `default:"UNSETFLOAT"` 43 | CouponRateBelow float64 `default:"UNSETFLOAT"` 44 | ExcludeConvertible bool 45 | AverageOptionVolumeAbove int64 `default:"UNSETINT"` 46 | ScannerSettingPairs string 47 | StockTypeFilter string 48 | } 49 | 50 | func (s ScannerSubscription) String() string { 51 | return fmt.Sprintf("Instrument: %s, LocationCode: %s, ScanCode: %s", 52 | s.Instrument, 53 | s.LocationCode, 54 | s.ScanCode) 55 | } 56 | 57 | // NewScannerSubscription create a default ScannerSubscription 58 | func NewScannerSubscription() *ScannerSubscription { 59 | scannerSubscription := &ScannerSubscription{} 60 | 61 | scannerSubscription.NumberOfRows = -1 62 | scannerSubscription.AbovePrice = UNSETFLOAT 63 | scannerSubscription.BelowPrice = UNSETFLOAT 64 | scannerSubscription.AboveVolume = UNSETINT 65 | scannerSubscription.MarketCapAbove = UNSETFLOAT 66 | scannerSubscription.MarketCapBelow = UNSETFLOAT 67 | 68 | scannerSubscription.CouponRateAbove = UNSETFLOAT 69 | scannerSubscription.CouponRateBelow = UNSETFLOAT 70 | scannerSubscription.AverageOptionVolumeAbove = UNSETINT 71 | 72 | return scannerSubscription 73 | } 74 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/binary" 7 | "io" 8 | "math" 9 | "reflect" 10 | "strconv" 11 | "time" 12 | 13 | "go.uber.org/zap" 14 | // log "github.com/sirupsen/logrus" 15 | ) 16 | 17 | const ( 18 | fieldSplit byte = '\x00' 19 | // UNSETFLOAT represent unset value of float64. 20 | UNSETFLOAT float64 = math.MaxFloat64 21 | // UNSETINT represent unset value of int64. 22 | UNSETINT int64 = math.MaxInt64 23 | // NO_VALID_ID represent that the callback func of wrapper is not attached to any request. 24 | NO_VALID_ID int64 = -1 25 | // MAX_MSG_LEN is the max length that receiver could take. 26 | MAX_MSG_LEN int = 0xFFFFFF 27 | ) 28 | 29 | var log *zap.Logger 30 | 31 | func init() { 32 | log, _ = zap.NewProduction() 33 | } 34 | 35 | // APILogger sets the options of internal logger for API, such as level, encoder, output, see uber.org/zap for more information 36 | func SetAPILogger(cfg zap.Config, opts ...zap.Option) error { 37 | newlogger, err := cfg.Build(opts...) 38 | log = newlogger 39 | return err 40 | } 41 | 42 | // GetLogger gets a clone of the internal logger with the option, see uber.org/zap for more information 43 | func GetLogger() *zap.Logger { 44 | return log 45 | } 46 | 47 | func bytesToTime(b []byte) time.Time { 48 | // format := "20060102 15:04:05 Mountain Standard Time" 49 | // 214 208 185 250 177 234 215 188 202 177 188 228 50 | format := "20060102 15:04:05 MST" 51 | t := string(b) 52 | localtime, err := time.ParseInLocation(format, t, time.Local) 53 | if err != nil { 54 | log.Error("btyes to time error", zap.Error(err)) 55 | } 56 | return localtime 57 | } 58 | 59 | // readMsgBytes try to read the msg based on the message size 60 | func readMsgBytes(reader *bufio.Reader) ([]byte, error) { 61 | sizeBytes := make([]byte, 4) // sync.Pool? 62 | //try to get 4bytes sizeBytes 63 | for n, r := 0, 4; n < r; { 64 | tempMsgBytes := make([]byte, r-n) 65 | tn, err := reader.Read(tempMsgBytes) 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | copy(sizeBytes[n:n+tn], tempMsgBytes) 71 | n += tn 72 | } 73 | 74 | size := int(binary.BigEndian.Uint32(sizeBytes)) 75 | log.Debug("readMsgBytes", zap.Int("size", size)) 76 | 77 | msgBytes := make([]byte, size) 78 | 79 | // XXX: maybe there is a better way to get fixed size of bytes 80 | for n, r := 0, size; n < r; { 81 | tempMsgBytes := make([]byte, r-n) 82 | tn, err := reader.Read(tempMsgBytes) 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | copy(msgBytes[n:n+tn], tempMsgBytes) 88 | n += tn 89 | 90 | } 91 | 92 | log.Debug("readMsgBytes", zap.Binary("msgBytes", msgBytes)) 93 | return msgBytes, nil 94 | 95 | } 96 | 97 | // scanFields defines how to unpack the buf 98 | func scanFields(data []byte, atEOF bool) (advance int, token []byte, err error) { 99 | if atEOF { 100 | return 0, nil, io.EOF 101 | } 102 | 103 | if len(data) < 4 { 104 | return 0, nil, nil 105 | } 106 | 107 | totalSize := int(binary.BigEndian.Uint32(data[:4])) + 4 108 | 109 | if totalSize > len(data) { 110 | return 0, nil, nil 111 | } 112 | 113 | // msgBytes := make([]byte, totalSize-4, totalSize-4) 114 | // copy(msgBytes, data[4:totalSize]) 115 | // not copy here, copied by callee more reasonable 116 | return totalSize, data[4:totalSize], nil 117 | } 118 | 119 | // func field2Bytes(field interface{}) []byte { 120 | // // var bs []byte 121 | // bs := make([]byte, 0, 9) 122 | 123 | // switch v := field.(type) { 124 | 125 | // case int64: 126 | // bs = encodeInt64(v) 127 | // case float64: 128 | // bs = encodeFloat64(v) 129 | // case string: 130 | // bs = encodeString(v) 131 | // case bool: 132 | // bs = encodeBool(v) 133 | // case int: 134 | // bs = encodeInt(v) 135 | // case []byte: 136 | // bs = v 137 | 138 | // // case time.Time: 139 | // // b = encodeTime(msg.(time.Time)) 140 | 141 | // default: 142 | // log.Panic("failed to covert the field", zap.Reflect("field", field)) 143 | // } 144 | 145 | // return append(bs, fieldSplit) 146 | // } 147 | 148 | // makeMsgBytes is a universal way to make the request ,but not an efficient way 149 | // TODO: do some test and improve!!! 150 | func makeMsgBytes(fields ...interface{}) []byte { 151 | 152 | msgBytes := make([]byte, 4, 8*len(fields)+4) // pre alloc memory 153 | 154 | for _, f := range fields { 155 | switch v := f.(type) { 156 | case int64: 157 | msgBytes = strconv.AppendInt(msgBytes, v, 10) 158 | case float64: 159 | msgBytes = strconv.AppendFloat(msgBytes, v, 'g', 10, 64) 160 | case string: 161 | msgBytes = append(msgBytes, []byte(v)...) 162 | case bool: 163 | if v { 164 | msgBytes = append(msgBytes, '1') 165 | } else { 166 | msgBytes = append(msgBytes, '0') 167 | } 168 | case int: 169 | msgBytes = strconv.AppendInt(msgBytes, int64(v), 10) 170 | case []byte: 171 | msgBytes = append(msgBytes, v...) 172 | default: 173 | log.Panic("failed to covert the field", zap.Reflect("field", f)) // never reach here 174 | } 175 | 176 | msgBytes = append(msgBytes, fieldSplit) 177 | } 178 | 179 | // add the size header 180 | binary.BigEndian.PutUint32(msgBytes, uint32(len(msgBytes)-4)) 181 | 182 | return msgBytes 183 | } 184 | 185 | func splitMsgBytes(data []byte) [][]byte { 186 | fields := bytes.Split(data, []byte{fieldSplit}) 187 | return fields[:len(fields)-1] 188 | } 189 | 190 | func decodeInt(field []byte) int64 { 191 | if bytes.Equal(field, []byte{}) { 192 | return 0 193 | } 194 | i, err := strconv.ParseInt(string(field), 10, 64) 195 | if err != nil { 196 | log.Panic("failed to decode int", zap.Error(err)) 197 | } 198 | return i 199 | } 200 | 201 | func decodeString(field []byte) string { 202 | return string(field) 203 | } 204 | 205 | // func encodeInt64(i int64) []byte { 206 | // return []byte(strconv.FormatInt(i, 10)) 207 | // } 208 | 209 | // func encodeInt(i int) []byte { 210 | // return []byte(strconv.Itoa(i)) 211 | // } 212 | 213 | // func encodeFloat64(f float64) []byte { 214 | // return []byte(strconv.FormatFloat(f, 'g', 10, 64)) 215 | // } 216 | 217 | // func encodeString(str string) []byte { 218 | // return []byte(str) 219 | // } 220 | 221 | // func encodeBool(b bool) []byte { 222 | // if b { 223 | // return []byte{'1'} 224 | // } 225 | // return []byte{'0'} 226 | 227 | // } 228 | 229 | func handleEmpty(d interface{}) string { 230 | switch v := d.(type) { 231 | case int64: 232 | if v == UNSETINT { 233 | return "" 234 | } 235 | return strconv.FormatInt(v, 10) 236 | 237 | case float64: 238 | if v == UNSETFLOAT { 239 | return "" 240 | } 241 | return strconv.FormatFloat(v, 'g', 10, 64) 242 | 243 | default: 244 | log.Panic("no handler for such type", zap.Reflect("val", d)) 245 | return "" // never reach here 246 | } 247 | } 248 | 249 | //InitDefault try to init the object with the default tag, that is a common way but not a efficent way 250 | func InitDefault(o interface{}) { 251 | t := reflect.TypeOf(o).Elem() 252 | v := reflect.ValueOf(o).Elem() 253 | 254 | fieldCount := t.NumField() 255 | 256 | for i := 0; i < fieldCount; i++ { 257 | field := t.Field(i) 258 | 259 | if v.Field(i).Kind() == reflect.Struct { 260 | InitDefault(v.Field(i).Addr().Interface()) 261 | continue 262 | } 263 | 264 | if defaultValue, ok := field.Tag.Lookup("default"); ok { 265 | 266 | switch defaultValue { 267 | case "UNSETFLOAT": 268 | v.Field(i).SetFloat(UNSETFLOAT) 269 | case "UNSETINT": 270 | v.Field(i).SetInt(UNSETINT) 271 | case "-1": 272 | v.Field(i).SetInt(-1) 273 | case "true": 274 | v.Field(i).SetBool(true) 275 | default: 276 | log.Panic("Unknown defaultValue", zap.Reflect("default", v)) 277 | } 278 | } 279 | 280 | } 281 | } 282 | 283 | // MsgBuffer is the buffer that contains a whole msg 284 | type MsgBuffer struct { 285 | bytes.Buffer 286 | bs []byte 287 | err error 288 | } 289 | 290 | func (m *MsgBuffer) readInt() int64 { 291 | var i int64 292 | m.bs, m.err = m.ReadBytes(fieldSplit) 293 | if m.err != nil { 294 | log.Panic("decode int64 error", zap.Error(m.err)) 295 | } 296 | 297 | m.bs = m.bs[:len(m.bs)-1] 298 | if bytes.Equal(m.bs, nil) { 299 | return 0 300 | } 301 | 302 | i, m.err = strconv.ParseInt(string(m.bs), 10, 64) 303 | if m.err != nil { 304 | log.Panic("decode int64 error", zap.Error(m.err)) 305 | } 306 | 307 | return i 308 | } 309 | 310 | func (m *MsgBuffer) readIntCheckUnset() int64 { 311 | var i int64 312 | m.bs, m.err = m.ReadBytes(fieldSplit) 313 | if m.err != nil { 314 | log.Panic("decode int64 error", zap.Error(m.err)) 315 | } 316 | 317 | m.bs = m.bs[:len(m.bs)-1] 318 | if bytes.Equal(m.bs, nil) { 319 | return UNSETINT 320 | } 321 | 322 | i, m.err = strconv.ParseInt(string(m.bs), 10, 64) 323 | if m.err != nil { 324 | log.Panic("decode int64 error", zap.Error(m.err)) 325 | } 326 | 327 | return i 328 | } 329 | 330 | func (m *MsgBuffer) readFloat() float64 { 331 | var f float64 332 | m.bs, m.err = m.ReadBytes(fieldSplit) 333 | if m.err != nil { 334 | log.Panic("decode float64 error", zap.Error(m.err)) 335 | } 336 | 337 | m.bs = m.bs[:len(m.bs)-1] 338 | if bytes.Equal(m.bs, nil) { 339 | return 0.0 340 | } 341 | 342 | f, m.err = strconv.ParseFloat(string(m.bs), 64) 343 | if m.err != nil { 344 | log.Panic("decode float64 error", zap.Error(m.err)) 345 | } 346 | 347 | return f 348 | } 349 | 350 | func (m *MsgBuffer) readFloatCheckUnset() float64 { 351 | var f float64 352 | m.bs, m.err = m.ReadBytes(fieldSplit) 353 | if m.err != nil { 354 | log.Panic("decode float64 error", zap.Error(m.err)) 355 | } 356 | 357 | m.bs = m.bs[:len(m.bs)-1] 358 | if bytes.Equal(m.bs, nil) { 359 | return UNSETFLOAT 360 | } 361 | 362 | f, m.err = strconv.ParseFloat(string(m.bs), 64) 363 | if m.err != nil { 364 | log.Panic("decode float64 error", zap.Error(m.err)) 365 | } 366 | 367 | return f 368 | } 369 | 370 | func (m *MsgBuffer) readBool() bool { 371 | m.bs, m.err = m.ReadBytes(fieldSplit) 372 | if m.err != nil { 373 | log.Panic("decode bool error", zap.Error(m.err)) 374 | } 375 | 376 | m.bs = m.bs[:len(m.bs)-1] 377 | 378 | if bytes.Equal(m.bs, []byte{'0'}) || bytes.Equal(m.bs, nil) { 379 | return false 380 | } 381 | return true 382 | } 383 | 384 | func (m *MsgBuffer) readString() string { 385 | m.bs, m.err = m.ReadBytes(fieldSplit) 386 | if m.err != nil { 387 | log.Panic("decode string error", zap.Error(m.err)) 388 | } 389 | 390 | return string(m.bs[:len(m.bs)-1]) 391 | } 392 | 393 | // NewMsgBuffer create a new MsgBuffer 394 | func NewMsgBuffer(bs []byte) *MsgBuffer { 395 | return &MsgBuffer{ 396 | *bytes.NewBuffer(bs), 397 | nil, 398 | nil} 399 | } 400 | 401 | // Reset reset buffer, []byte, err 402 | func (m *MsgBuffer) Reset() { 403 | m.Buffer.Reset() 404 | m.bs = m.bs[:0] 405 | m.err = nil 406 | } 407 | -------------------------------------------------------------------------------- /utils_test.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import ( 4 | _ "net/http/pprof" 5 | "testing" 6 | // "time" 7 | ) 8 | 9 | // func makeMsgBytesOld(fields ...interface{}) []byte { 10 | 11 | // // make the whole the slice of msgBytes 12 | // msgBytesSlice := make([][]byte, 0, len(fields)) 13 | // for _, f := range fields { 14 | // // make the field into msgBytes 15 | // msgBytes := field2Bytes(f) 16 | // msgBytesSlice = append(msgBytesSlice, msgBytes) 17 | // } 18 | // msg := bytes.Join(msgBytesSlice, nil) 19 | 20 | // // add the size header 21 | // sizeBytes := make([]byte, 4, 4+len(msg)) 22 | // binary.BigEndian.PutUint32(sizeBytes, uint32(len(msg))) 23 | 24 | // return append(sizeBytes, msg...) 25 | // } 26 | 27 | // func TestMakeMsgEqual(t *testing.T) { 28 | // var v1 int64 = 19901130 29 | // var v2 float64 = 0.123456 30 | // var v3 string = "bla bla bla" 31 | // var v4 bool = true 32 | // var v5 []byte = []byte("hadrianl") 33 | // var v6 int = 20201130 34 | 35 | // oldBytes := makeMsgBytesOld(v3, v1, v2, v3, v4, v5, v6) 36 | // newBytes := makeMsgBytes(v3, v1, v2, v3, v4, v5, v6) 37 | // t.Log("old:", oldBytes) 38 | // t.Log("new:", newBytes) 39 | 40 | // if !bytes.Equal(oldBytes, newBytes) { 41 | // t.Fatal("bytes not equal!") 42 | // } 43 | // } 44 | 45 | func BenchmarkMakeMsg(b *testing.B) { 46 | // log, _ = zap.NewDevelopment() 47 | var v1 int64 = 19901130 48 | var v2 float64 = 0.123456 49 | var v3 string = "bla bla bla" 50 | var v4 bool = true 51 | var v5 []byte = []byte("hadrianl") 52 | var v6 int = 20201130 53 | // var updateAccountValueMsgBuf = NewMsgBuffer(nil) 54 | for i := 0; i < b.N; i++ { 55 | // updateAccountValueMsgBuf.Write(msgBytes) 56 | makeMsgBytes(v1, v2, v3, v4, v5, v6) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /wrapper.go: -------------------------------------------------------------------------------- 1 | package ibapi 2 | 3 | import ( 4 | "sync/atomic" 5 | "time" 6 | 7 | "go.uber.org/zap" 8 | ) 9 | 10 | // IbWrapper contain the funcs to handle the msg from TWS or Gateway 11 | type IbWrapper interface { 12 | TickPrice(reqID int64, tickType int64, price float64, attrib TickAttrib) 13 | TickSize(reqID int64, tickType int64, size int64) 14 | OrderStatus(orderID int64, status string, filled float64, remaining float64, avgFillPrice float64, permID int64, parentID int64, lastFillPrice float64, clientID int64, whyHeld string, mktCapPrice float64) 15 | Error(reqID int64, errCode int64, errString string) 16 | OpenOrder(orderID int64, contract *Contract, order *Order, orderState *OrderState) 17 | UpdateAccountValue(tag string, val string, currency string, accName string) 18 | UpdatePortfolio(contract *Contract, position float64, marketPrice float64, marketValue float64, averageCost float64, unrealizedPNL float64, realizedPNL float64, accName string) 19 | UpdateAccountTime(accTime time.Time) 20 | NextValidID(reqID int64) 21 | ContractDetails(reqID int64, conDetails *ContractDetails) 22 | ExecDetails(reqID int64, contract *Contract, execution *Execution) 23 | UpdateMktDepth(reqID int64, position int64, operation int64, side int64, price float64, size int64) 24 | UpdateMktDepthL2(reqID int64, position int64, marketMaker string, operation int64, side int64, price float64, size int64, isSmartDepth bool) 25 | UpdateNewsBulletin(msgID int64, msgType int64, newsMessage string, originExchange string) 26 | ManagedAccounts(accountsList []string) 27 | ReceiveFA(faData int64, cxml string) 28 | HistoricalData(reqID int64, bar *BarData) 29 | HistoricalDataEnd(reqID int64, startDateStr string, endDateStr string) 30 | HistoricalDataUpdate(reqID int64, bar *BarData) 31 | BondContractDetails(reqID int64, conDetails *ContractDetails) 32 | ScannerParameters(xml string) 33 | ScannerData(reqID int64, rank int64, conDetails *ContractDetails, distance string, benchmark string, projection string, legs string) 34 | ScannerDataEnd(reqID int64) 35 | TickOptionComputation(reqID int64, tickType int64, tickAttrib int64, impliedVol float64, delta float64, optPrice float64, pvDiviedn float64, gamma float64, vega float64, theta float64, undPrice float64) 36 | TickGeneric(reqID int64, tickType int64, value float64) 37 | TickString(reqID int64, tickType int64, value string) 38 | TickEFP(reqID int64, tickType int64, basisPoints float64, formattedBasisPoints string, totalDividends float64, holdDays int64, futureLastTradeDate string, dividendImpact float64, dividendsToLastTradeDate float64) 39 | CurrentTime(t time.Time) 40 | RealtimeBar(reqID int64, time int64, open float64, high float64, low float64, close float64, volume int64, wap float64, count int64) 41 | FundamentalData(reqID int64, data string) 42 | ContractDetailsEnd(reqID int64) 43 | OpenOrderEnd() 44 | AccountDownloadEnd(accName string) 45 | ExecDetailsEnd(reqID int64) 46 | DeltaNeutralValidation(reqID int64, deltaNeutralContract DeltaNeutralContract) 47 | TickSnapshotEnd(reqID int64) 48 | MarketDataType(reqID int64, marketDataType int64) 49 | Position(account string, contract *Contract, position float64, avgCost float64) 50 | PositionEnd() 51 | AccountSummary(reqID int64, account string, tag string, value string, currency string) 52 | AccountSummaryEnd(reqID int64) 53 | VerifyMessageAPI(apiData string) 54 | VerifyCompleted(isSuccessful bool, err string) 55 | DisplayGroupList(reqID int64, groups string) 56 | DisplayGroupUpdated(reqID int64, contractInfo string) 57 | VerifyAndAuthMessageAPI(apiData string, xyzChallange string) 58 | VerifyAndAuthCompleted(isSuccessful bool, err string) 59 | PositionMulti(reqID int64, account string, modelCode string, contract *Contract, position float64, avgCost float64) 60 | PositionMultiEnd(reqID int64) 61 | AccountUpdateMulti(reqID int64, account string, modleCode string, tag string, value string, currency string) 62 | AccountUpdateMultiEnd(reqID int64) 63 | SecurityDefinitionOptionParameter(reqID int64, exchange string, underlyingContractID int64, tradingClass string, multiplier string, expirations []string, strikes []float64) 64 | SecurityDefinitionOptionParameterEnd(reqID int64) 65 | SoftDollarTiers(reqID int64, tiers []SoftDollarTier) 66 | FamilyCodes(famCodes []FamilyCode) 67 | SymbolSamples(reqID int64, contractDescriptions []ContractDescription) 68 | SmartComponents(reqID int64, smartComps []SmartComponent) 69 | TickReqParams(tickerID int64, minTick float64, bboExchange string, snapshotPermissions int64) 70 | MktDepthExchanges(depthMktDataDescriptions []DepthMktDataDescription) 71 | HeadTimestamp(reqID int64, headTimestamp string) 72 | TickNews(tickerID int64, timeStamp int64, providerCode string, articleID string, headline string, extraData string) 73 | NewsProviders(newsProviders []NewsProvider) 74 | NewsArticle(reqID int64, articleType int64, articleText string) 75 | HistoricalNews(reqID int64, time string, providerCode string, articleID string, headline string) 76 | HistoricalNewsEnd(reqID int64, hasMore bool) 77 | HistogramData(reqID int64, histogram []HistogramData) 78 | RerouteMktDataReq(reqID int64, contractID int64, exchange string) 79 | RerouteMktDepthReq(reqID int64, contractID int64, exchange string) 80 | MarketRule(marketRuleID int64, priceIncrements []PriceIncrement) 81 | Pnl(reqID int64, dailyPnL float64, unrealizedPnL float64, realizedPnL float64) 82 | PnlSingle(reqID int64, position int64, dailyPnL float64, unrealizedPnL float64, realizedPnL float64, value float64) 83 | HistoricalTicks(reqID int64, ticks []HistoricalTick, done bool) 84 | HistoricalTicksBidAsk(reqID int64, ticks []HistoricalTickBidAsk, done bool) 85 | HistoricalTicksLast(reqID int64, ticks []HistoricalTickLast, done bool) 86 | TickByTickAllLast(reqID int64, tickType int64, time int64, price float64, size int64, tickAttribLast TickAttribLast, exchange string, specialConditions string) 87 | TickByTickBidAsk(reqID int64, time int64, bidPrice float64, askPrice float64, bidSize int64, askSize int64, tickAttribBidAsk TickAttribBidAsk) 88 | TickByTickMidPoint(reqID int64, time int64, midPoint float64) 89 | OrderBound(reqID int64, apiClientID int64, apiOrderID int64) 90 | CompletedOrder(contract *Contract, order *Order, orderState *OrderState) 91 | CompletedOrdersEnd() 92 | CommissionReport(commissionReport CommissionReport) 93 | ConnectAck() 94 | ConnectionClosed() 95 | ReplaceFAEnd(reqID int64, text string) 96 | WshMetaData(reqID int64, dataJson string) 97 | WshEventData(reqID int64, dataJson string) 98 | } 99 | 100 | // Wrapper is the default wrapper provided by this golang implement. 101 | type Wrapper struct { 102 | orderID int64 103 | } 104 | 105 | func (w *Wrapper) GetNextOrderID() (i int64) { 106 | i = w.orderID 107 | atomic.AddInt64(&w.orderID, 1) 108 | return 109 | } 110 | 111 | func (w Wrapper) ConnectAck() { 112 | log.Info("...") 113 | } 114 | 115 | func (w Wrapper) ConnectionClosed() { 116 | log.Info("...") 117 | } 118 | 119 | func (w *Wrapper) NextValidID(reqID int64) { 120 | atomic.StoreInt64(&w.orderID, reqID) 121 | log.With(zap.Int64("reqID", reqID)).Info("") 122 | } 123 | 124 | func (w Wrapper) ManagedAccounts(accountsList []string) { 125 | log.Info("", zap.Strings("accountList", accountsList)) 126 | } 127 | 128 | func (w Wrapper) TickPrice(reqID int64, tickType int64, price float64, attrib TickAttrib) { 129 | log.With(zap.Int64("reqID", reqID)).Info("", zap.Int64("tickType", tickType), zap.Float64("price", price)) 130 | } 131 | 132 | func (w Wrapper) UpdateAccountTime(accTime time.Time) { 133 | log.Info("", zap.Time("accountTime", accTime)) 134 | } 135 | 136 | func (w Wrapper) UpdateAccountValue(tag string, value string, currency string, account string) { 137 | log.Info("", zap.String("tag", tag), zap.String("value", value), zap.String("currency", currency), zap.String("account", account)) 138 | } 139 | 140 | func (w Wrapper) AccountDownloadEnd(accName string) { 141 | log.Info("", zap.String("accountName", accName)) 142 | } 143 | 144 | func (w Wrapper) AccountUpdateMulti(reqID int64, account string, modelCode string, tag string, value string, currency string) { 145 | log.With(zap.Int64("reqID", reqID)).Info("", 146 | zap.String("account", account), 147 | zap.String("modelCode", modelCode), 148 | zap.String("tag", tag), 149 | zap.String("value", value), 150 | zap.String("curreny", currency), 151 | ) 152 | } 153 | 154 | func (w Wrapper) AccountUpdateMultiEnd(reqID int64) { 155 | log.With(zap.Int64("reqID", reqID)).Info("") 156 | } 157 | 158 | func (w Wrapper) AccountSummary(reqID int64, account string, tag string, value string, currency string) { 159 | log.With(zap.Int64("reqID", reqID)).Info("", 160 | zap.String("account", account), 161 | zap.String("tag", tag), 162 | zap.String("value", value), 163 | zap.String("curreny", currency), 164 | ) 165 | 166 | } 167 | 168 | func (w Wrapper) AccountSummaryEnd(reqID int64) { 169 | log.With(zap.Int64("reqID", reqID)).Info("") 170 | } 171 | 172 | func (w Wrapper) VerifyMessageAPI(apiData string) { 173 | log.Info("", zap.String("apiData", apiData)) 174 | } 175 | 176 | func (w Wrapper) VerifyCompleted(isSuccessful bool, err string) { 177 | log.Info("", zap.Bool("isSuccessful", isSuccessful), zap.String("error", err)) 178 | } 179 | 180 | func (w Wrapper) VerifyAndAuthMessageAPI(apiData string, xyzChallange string) { 181 | log.Info("", zap.String("apiData", apiData), zap.String("xyzChallange", xyzChallange)) 182 | } 183 | 184 | func (w Wrapper) VerifyAndAuthCompleted(isSuccessful bool, err string) { 185 | log.Info("", zap.Bool("isSuccessful", isSuccessful), zap.String("error", err)) 186 | } 187 | 188 | func (w Wrapper) DisplayGroupList(reqID int64, groups string) { 189 | log.With(zap.Int64("reqID", reqID)).Info("", zap.String("groups", groups)) 190 | } 191 | 192 | func (w Wrapper) DisplayGroupUpdated(reqID int64, contractInfo string) { 193 | log.With(zap.Int64("reqID", reqID)).Info("", zap.String("contractInfo", contractInfo)) 194 | } 195 | 196 | func (w Wrapper) PositionMulti(reqID int64, account string, modelCode string, contract *Contract, position float64, avgCost float64) { 197 | log.With(zap.Int64("reqID", reqID)).Info("", 198 | zap.String("account", account), 199 | zap.String("modelCode", modelCode), 200 | zap.Any("contract", contract), 201 | zap.Float64("position", position), 202 | zap.Float64("avgCost", avgCost), 203 | ) 204 | } 205 | 206 | func (w Wrapper) PositionMultiEnd(reqID int64) { 207 | log.With(zap.Int64("reqID", reqID)).Info("") 208 | } 209 | 210 | func (w Wrapper) UpdatePortfolio(contract *Contract, position float64, marketPrice float64, marketValue float64, averageCost float64, unrealizedPNL float64, realizedPNL float64, accName string) { 211 | log.Info("", 212 | zap.String("localSymbol", contract.LocalSymbol), 213 | zap.Float64("position", position), 214 | zap.Float64("marketPrice", marketPrice), 215 | zap.Float64("averageCost", averageCost), 216 | zap.Float64("unrealizedPNL", unrealizedPNL), 217 | zap.Float64("realizedPNL", realizedPNL), 218 | zap.String("accName", accName), 219 | ) 220 | } 221 | 222 | func (w Wrapper) Position(account string, contract *Contract, position float64, avgCost float64) { 223 | log.Info("", 224 | zap.String("account", account), 225 | zap.Any("contract", contract), 226 | zap.Float64("position", position), 227 | zap.Float64("avgCost", avgCost), 228 | ) 229 | } 230 | 231 | func (w Wrapper) PositionEnd() { 232 | log.Info("") 233 | } 234 | 235 | func (w Wrapper) Pnl(reqID int64, dailyPnL float64, unrealizedPnL float64, realizedPnL float64) { 236 | log.With(zap.Int64("reqID", reqID)).Info("", 237 | zap.Float64("dailyPnL", dailyPnL), 238 | zap.Float64("unrealizedPnL", unrealizedPnL), 239 | zap.Float64("realizedPnL", realizedPnL), 240 | ) 241 | } 242 | 243 | func (w Wrapper) PnlSingle(reqID int64, position int64, dailyPnL float64, unrealizedPnL float64, realizedPnL float64, value float64) { 244 | log.With(zap.Int64("reqID", reqID)).Info("", 245 | zap.Float64("dailyPnL", dailyPnL), 246 | zap.Float64("unrealizedPnL", unrealizedPnL), 247 | zap.Float64("realizedPnL", realizedPnL), 248 | zap.Float64("value", value), 249 | ) 250 | } 251 | 252 | func (w Wrapper) OpenOrder(orderID int64, contract *Contract, order *Order, orderState *OrderState) { 253 | log.With(zap.Int64("orderID", orderID)).Info("", 254 | zap.Any("contract", contract), 255 | zap.Any("order", order), 256 | zap.Any("orderState", orderState), 257 | ) 258 | } 259 | 260 | func (w Wrapper) OpenOrderEnd() { 261 | log.Info("") 262 | 263 | } 264 | 265 | func (w Wrapper) OrderStatus(orderID int64, status string, filled float64, remaining float64, avgFillPrice float64, permID int64, parentID int64, lastFillPrice float64, clientID int64, whyHeld string, mktCapPrice float64) { 266 | log.With(zap.Int64("orderID", orderID)).Info("", 267 | zap.String("status", status), 268 | zap.Float64("filled", filled), 269 | zap.Float64("remaining", remaining), 270 | zap.Float64("avgFillPrice", avgFillPrice), 271 | ) 272 | } 273 | 274 | func (w Wrapper) ExecDetails(reqID int64, contract *Contract, execution *Execution) { 275 | log.With(zap.Int64("reqID", reqID)).Info("", 276 | zap.Any("contract", contract), 277 | zap.Any("execution", execution), 278 | ) 279 | } 280 | 281 | func (w Wrapper) ExecDetailsEnd(reqID int64) { 282 | log.With(zap.Int64("reqID", reqID)).Info("") 283 | } 284 | 285 | func (w Wrapper) DeltaNeutralValidation(reqID int64, deltaNeutralContract DeltaNeutralContract) { 286 | log.With(zap.Int64("reqID", reqID)).Info("", 287 | zap.Any("deltaNeutralContract", deltaNeutralContract), 288 | ) 289 | } 290 | 291 | func (w Wrapper) CommissionReport(commissionReport CommissionReport) { 292 | log.Info("", 293 | zap.Any("commissionReport", commissionReport), 294 | ) 295 | } 296 | 297 | func (w Wrapper) OrderBound(reqID int64, apiClientID int64, apiOrderID int64) { 298 | log.With(zap.Int64("reqID", reqID)).Info("", 299 | zap.Int64("apiClientID", apiClientID), 300 | zap.Int64("apiOrderID", apiOrderID), 301 | ) 302 | } 303 | 304 | func (w Wrapper) ContractDetails(reqID int64, conDetails *ContractDetails) { 305 | log.With(zap.Int64("reqID", reqID)).Info("", 306 | zap.Any("conDetails", conDetails), 307 | ) 308 | } 309 | 310 | func (w Wrapper) ContractDetailsEnd(reqID int64) { 311 | log.With(zap.Int64("reqID", reqID)).Info("") 312 | } 313 | 314 | func (w Wrapper) BondContractDetails(reqID int64, conDetails *ContractDetails) { 315 | log.With(zap.Int64("reqID", reqID)).Info("", 316 | zap.Any("conDetails", conDetails), 317 | ) 318 | } 319 | 320 | func (w Wrapper) SymbolSamples(reqID int64, contractDescriptions []ContractDescription) { 321 | log.With(zap.Int64("reqID", reqID)).Info("", 322 | zap.Any("contractDescriptions", contractDescriptions), 323 | ) 324 | } 325 | 326 | func (w Wrapper) SmartComponents(reqID int64, smartComps []SmartComponent) { 327 | log.With(zap.Int64("reqID", reqID)).Info("", 328 | zap.Any("smartComps", smartComps), 329 | ) 330 | } 331 | 332 | func (w Wrapper) MarketRule(marketRuleID int64, priceIncrements []PriceIncrement) { 333 | log.Info("", 334 | zap.Int64("marketRuleID", marketRuleID), 335 | zap.Any("priceIncrements", priceIncrements), 336 | ) 337 | } 338 | 339 | func (w Wrapper) RealtimeBar(reqID int64, time int64, open float64, high float64, low float64, close float64, volume int64, wap float64, count int64) { 340 | log.With(zap.Int64("reqID", reqID)).Info("", 341 | zap.Int64("time", time), 342 | zap.Float64("open", open), 343 | zap.Float64("high", high), 344 | zap.Float64("low", low), 345 | zap.Float64("close", close), 346 | zap.Int64("volume", volume), 347 | zap.Float64("wap", wap), 348 | zap.Int64("count", count), 349 | ) 350 | } 351 | 352 | func (w Wrapper) HistoricalData(reqID int64, bar *BarData) { 353 | log.With(zap.Int64("reqID", reqID)).Info("", 354 | zap.Any("bar", bar), 355 | ) 356 | } 357 | 358 | func (w Wrapper) HistoricalDataEnd(reqID int64, startDateStr string, endDateStr string) { 359 | log.With(zap.Int64("reqID", reqID)).Info("", 360 | zap.String("startDate", startDateStr), 361 | zap.String("endDate", endDateStr), 362 | ) 363 | } 364 | 365 | func (w Wrapper) HistoricalDataUpdate(reqID int64, bar *BarData) { 366 | log.With(zap.Int64("reqID", reqID)).Info("", 367 | zap.Any("bar", bar), 368 | ) 369 | } 370 | 371 | func (w Wrapper) HeadTimestamp(reqID int64, headTimestamp string) { 372 | log.With(zap.Int64("reqID", reqID)).Info("", 373 | zap.String("headTimestamp", headTimestamp), 374 | ) 375 | } 376 | 377 | func (w Wrapper) HistoricalTicks(reqID int64, ticks []HistoricalTick, done bool) { 378 | log.With(zap.Int64("reqID", reqID)).Info("", 379 | zap.Any("ticks", ticks), 380 | zap.Bool("done", done), 381 | ) 382 | } 383 | 384 | func (w Wrapper) HistoricalTicksBidAsk(reqID int64, ticks []HistoricalTickBidAsk, done bool) { 385 | log.With(zap.Int64("reqID", reqID)).Info("", 386 | zap.Any("ticks", ticks), 387 | zap.Bool("done", done), 388 | ) 389 | } 390 | 391 | func (w Wrapper) HistoricalTicksLast(reqID int64, ticks []HistoricalTickLast, done bool) { 392 | log.With(zap.Int64("reqID", reqID)).Info("", 393 | zap.Any("ticks", ticks), 394 | zap.Bool("done", done), 395 | ) 396 | } 397 | 398 | func (w Wrapper) TickSize(reqID int64, tickType int64, size int64) { 399 | log.With(zap.Int64("reqID", reqID)).Info("", 400 | zap.Int64("tickType", tickType), 401 | zap.Int64("size", size), 402 | ) 403 | } 404 | 405 | func (w Wrapper) TickSnapshotEnd(reqID int64) { 406 | log.With(zap.Int64("reqID", reqID)).Info("") 407 | } 408 | 409 | func (w Wrapper) MarketDataType(reqID int64, marketDataType int64) { 410 | log.With(zap.Int64("reqID", reqID)).Info("", 411 | zap.Int64("marketDataType", marketDataType), 412 | ) 413 | } 414 | 415 | func (w Wrapper) TickByTickAllLast(reqID int64, tickType int64, time int64, price float64, size int64, tickAttribLast TickAttribLast, exchange string, specialConditions string) { 416 | log.With(zap.Int64("reqID", reqID)).Info("", 417 | zap.Int64("tickType", tickType), 418 | zap.Int64("time", time), 419 | zap.Float64("price", price), 420 | zap.Int64("size", size), 421 | ) 422 | } 423 | 424 | func (w Wrapper) TickByTickBidAsk(reqID int64, time int64, bidPrice float64, askPrice float64, bidSize int64, askSize int64, tickAttribBidAsk TickAttribBidAsk) { 425 | log.With(zap.Int64("reqID", reqID)).Info("", 426 | zap.Int64("time", time), 427 | zap.Float64("bidPrice", bidPrice), 428 | zap.Float64("askPrice", askPrice), 429 | zap.Int64("bidPrice", bidSize), 430 | zap.Int64("askPrice", askSize), 431 | ) 432 | } 433 | 434 | func (w Wrapper) TickByTickMidPoint(reqID int64, time int64, midPoint float64) { 435 | log.With(zap.Int64("reqID", reqID)).Info("", 436 | zap.Int64("time", time), 437 | zap.Float64("midPoint", midPoint), 438 | ) 439 | } 440 | 441 | func (w Wrapper) TickString(reqID int64, tickType int64, value string) { 442 | log.With(zap.Int64("reqID", reqID)).Info("", 443 | zap.Int64("tickType", tickType), 444 | zap.String("value", value), 445 | ) 446 | } 447 | 448 | func (w Wrapper) TickGeneric(reqID int64, tickType int64, value float64) { 449 | log.With(zap.Int64("reqID", reqID)).Info("", 450 | zap.Int64("tickType", tickType), 451 | zap.Float64("value", value), 452 | ) 453 | } 454 | 455 | func (w Wrapper) TickEFP(reqID int64, tickType int64, basisPoints float64, formattedBasisPoints string, totalDividends float64, holdDays int64, futureLastTradeDate string, dividendImpact float64, dividendsToLastTradeDate float64) { 456 | log.With(zap.Int64("reqID", reqID)).Info("", 457 | zap.Int64("tickType", tickType), 458 | zap.Float64("basisPoints", basisPoints), 459 | ) 460 | } 461 | 462 | func (w Wrapper) TickReqParams(reqID int64, minTick float64, bboExchange string, snapshotPermissions int64) { 463 | log.With(zap.Int64("reqID", reqID)).Info("", 464 | zap.Float64("minTick", minTick), 465 | zap.String("bboExchange", bboExchange), 466 | zap.Int64("snapshotPermissions", snapshotPermissions), 467 | ) 468 | } 469 | func (w Wrapper) MktDepthExchanges(depthMktDataDescriptions []DepthMktDataDescription) { 470 | log.Info("", 471 | zap.Any("depthMktDataDescriptions", depthMktDataDescriptions), 472 | ) 473 | } 474 | 475 | /*Returns the order book. 476 | 477 | tickerId - the request's identifier 478 | position - the order book's row being updated 479 | operation - how to refresh the row: 480 | 0 = insert (insert this new order into the row identified by 'position') 481 | 1 = update (update the existing order in the row identified by 'position') 482 | 2 = delete (delete the existing order at the row identified by 'position'). 483 | side - 0 for ask, 1 for bid 484 | price - the order's price 485 | size - the order's size*/ 486 | func (w Wrapper) UpdateMktDepth(reqID int64, position int64, operation int64, side int64, price float64, size int64) { 487 | log.With(zap.Int64("reqID", reqID)).Info("", 488 | zap.Int64("position", position), 489 | zap.Int64("operation", operation), 490 | zap.Int64("side", side), 491 | zap.Float64("price", price), 492 | zap.Int64("size", size), 493 | ) 494 | } 495 | 496 | func (w Wrapper) UpdateMktDepthL2(reqID int64, position int64, marketMaker string, operation int64, side int64, price float64, size int64, isSmartDepth bool) { 497 | log.With(zap.Int64("reqID", reqID)).Info("", 498 | zap.Int64("position", position), 499 | zap.String("marketMaker", marketMaker), 500 | zap.Int64("operation", operation), 501 | zap.Int64("side", side), 502 | zap.Float64("price", price), 503 | zap.Int64("size", size), 504 | zap.Bool("isSmartDepth", isSmartDepth), 505 | ) 506 | } 507 | 508 | func (w Wrapper) TickOptionComputation(reqID int64, tickType int64, tickAttrib int64, impliedVol float64, delta float64, optPrice float64, pvDiviedn float64, gamma float64, vega float64, theta float64, undPrice float64) { 509 | log.With(zap.Int64("reqID", reqID)).Info("", 510 | zap.Int64("tickType", tickType), 511 | zap.Int64("tickAttrib", tickAttrib), 512 | zap.Float64("impliedVol", impliedVol), 513 | zap.Float64("delta", delta), 514 | zap.Float64("optPrice", optPrice), 515 | zap.Float64("pvDiviedn", pvDiviedn), 516 | zap.Float64("gamma", gamma), 517 | zap.Float64("vega", vega), 518 | zap.Float64("theta", theta), 519 | zap.Float64("undPrice", undPrice), 520 | ) 521 | } 522 | 523 | func (w Wrapper) FundamentalData(reqID int64, data string) { 524 | log.With(zap.Int64("reqID", reqID)).Info("", 525 | zap.String("data", data), 526 | ) 527 | } 528 | 529 | func (w Wrapper) ScannerParameters(xml string) { 530 | log.Info("", 531 | zap.String("xml", xml), 532 | ) 533 | 534 | } 535 | 536 | func (w Wrapper) ScannerData(reqID int64, rank int64, conDetails *ContractDetails, distance string, benchmark string, projection string, legs string) { 537 | log.With(zap.Int64("reqID", reqID)).Info("", 538 | zap.Int64("rank", rank), 539 | zap.Any("conDetails", conDetails), 540 | zap.String("distance", distance), 541 | zap.String("benchmark", benchmark), 542 | zap.String("projection", projection), 543 | zap.String("legs", legs), 544 | ) 545 | } 546 | 547 | func (w Wrapper) ScannerDataEnd(reqID int64) { 548 | log.With(zap.Int64("reqID", reqID)).Info("") 549 | } 550 | 551 | func (w Wrapper) HistogramData(reqID int64, histogram []HistogramData) { 552 | log.With(zap.Int64("reqID", reqID)).Info("", 553 | zap.Any("histogram", histogram), 554 | ) 555 | } 556 | 557 | func (w Wrapper) RerouteMktDataReq(reqID int64, contractID int64, exchange string) { 558 | log.With(zap.Int64("reqID", reqID)).Info("", 559 | zap.Int64("contractID", contractID), 560 | zap.String("exchange", exchange), 561 | ) 562 | } 563 | 564 | func (w Wrapper) RerouteMktDepthReq(reqID int64, contractID int64, exchange string) { 565 | log.With(zap.Int64("reqID", reqID)).Info("", 566 | zap.Int64("contractID", contractID), 567 | zap.String("exchange", exchange), 568 | ) 569 | } 570 | 571 | func (w Wrapper) SecurityDefinitionOptionParameter(reqID int64, exchange string, underlyingContractID int64, tradingClass string, multiplier string, expirations []string, strikes []float64) { 572 | log.With(zap.Int64("reqID", reqID)).Info("", 573 | zap.String("exchange", exchange), 574 | zap.Int64("underlyingContractID", underlyingContractID), 575 | zap.String("tradingClass", tradingClass), 576 | zap.String("multiplier", multiplier), 577 | zap.Strings("expirations", expirations), 578 | zap.Float64s("strikes", strikes), 579 | ) 580 | } 581 | 582 | func (w Wrapper) SecurityDefinitionOptionParameterEnd(reqID int64) { 583 | log.With(zap.Int64("reqID", reqID)).Info("") 584 | } 585 | 586 | func (w Wrapper) SoftDollarTiers(reqID int64, tiers []SoftDollarTier) { 587 | log.With(zap.Int64("reqID", reqID)).Info("", 588 | zap.Any("tiers", tiers), 589 | ) 590 | } 591 | 592 | func (w Wrapper) FamilyCodes(famCodes []FamilyCode) { 593 | log.Info("", 594 | zap.Any("famCodes", famCodes), 595 | ) 596 | } 597 | 598 | func (w Wrapper) NewsProviders(newsProviders []NewsProvider) { 599 | log.Info("", 600 | zap.Any("newsProviders", newsProviders), 601 | ) 602 | } 603 | 604 | func (w Wrapper) TickNews(tickerID int64, timeStamp int64, providerCode string, articleID string, headline string, extraData string) { 605 | log.With(zap.Int64("tickerID", tickerID)).Info("", 606 | zap.Int64("timeStamp", timeStamp), 607 | zap.String("providerCode", providerCode), 608 | zap.String("articleID", articleID), 609 | zap.String("headline", headline), 610 | zap.String("extraData", extraData), 611 | ) 612 | } 613 | 614 | func (w Wrapper) NewsArticle(reqID int64, articleType int64, articleText string) { 615 | log.With(zap.Int64("reqID", reqID)).Info("", 616 | zap.Int64("articleType", articleType), 617 | zap.String("articleText", articleText), 618 | ) 619 | } 620 | 621 | func (w Wrapper) HistoricalNews(reqID int64, time string, providerCode string, articleID string, headline string) { 622 | log.With(zap.Int64("reqID", reqID)).Info("", 623 | zap.String("time", time), 624 | zap.String("providerCode", providerCode), 625 | zap.String("articleID", articleID), 626 | zap.String("headline", headline), 627 | ) 628 | } 629 | 630 | func (w Wrapper) HistoricalNewsEnd(reqID int64, hasMore bool) { 631 | log.With(zap.Int64("reqID", reqID)).Info("", 632 | zap.Bool("hasMore", hasMore), 633 | ) 634 | } 635 | 636 | func (w Wrapper) UpdateNewsBulletin(msgID int64, msgType int64, newsMessage string, originExch string) { 637 | log.With(zap.Int64("msgID", msgID)).Info("", 638 | zap.Int64("msgType", msgType), 639 | zap.String("newsMessage", newsMessage), 640 | zap.String("originExch", originExch), 641 | ) 642 | } 643 | 644 | func (w Wrapper) ReceiveFA(faData int64, cxml string) { 645 | log.Info("", 646 | zap.Int64("faData", faData), 647 | zap.String("cxml", cxml), 648 | ) 649 | } 650 | 651 | func (w Wrapper) CurrentTime(t time.Time) { 652 | log.Info("", 653 | zap.Time("time", t), 654 | ) 655 | } 656 | 657 | func (w Wrapper) Error(reqID int64, errCode int64, errString string) { 658 | log.With(zap.Int64("reqID", reqID)).Info("", 659 | zap.Int64("errCode", errCode), 660 | zap.String("errString", errString), 661 | ) 662 | } 663 | 664 | func (w Wrapper) CompletedOrder(contract *Contract, order *Order, orderState *OrderState) { 665 | log.Info("", 666 | zap.Any("contract", contract), 667 | zap.Any("order", order), 668 | zap.Any("orderState", orderState), 669 | ) 670 | } 671 | 672 | func (w Wrapper) CompletedOrdersEnd() { 673 | log.Info(":") 674 | } 675 | 676 | func (w Wrapper) ReplaceFAEnd(reqID int64, text string) { 677 | log.With(zap.Int64("reqID", reqID)).Info("", zap.String("text", text)) 678 | } 679 | 680 | func (w Wrapper) WshMetaData(reqID int64, dataJson string) { 681 | log.With(zap.Int64("reqID", reqID)).Info("", zap.String("dataJson", dataJson)) 682 | } 683 | func (w Wrapper) WshEventData(reqID int64, dataJson string) { 684 | log.With(zap.Int64("reqID", reqID)).Info("", zap.String("dataJson", dataJson)) 685 | } 686 | --------------------------------------------------------------------------------