ib-api 10.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +52 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CLAUDE.md +131 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +17 -0
  8. data/Gemfile.lock +120 -0
  9. data/Guardfile +24 -0
  10. data/LICENSE +674 -0
  11. data/LLM_GUIDE.md +388 -0
  12. data/README.md +114 -0
  13. data/Rakefile +11 -0
  14. data/VERSION +1 -0
  15. data/api.gemspec +50 -0
  16. data/bin/console +96 -0
  17. data/bin/console.yml +3 -0
  18. data/bin/setup +8 -0
  19. data/bin/simple +91 -0
  20. data/changelog.md +32 -0
  21. data/conditions/ib/execution_condition.rb +31 -0
  22. data/conditions/ib/margin_condition.rb +28 -0
  23. data/conditions/ib/order_condition.rb +29 -0
  24. data/conditions/ib/percent_change_condition.rb +34 -0
  25. data/conditions/ib/price_condition.rb +44 -0
  26. data/conditions/ib/time_condition.rb +42 -0
  27. data/conditions/ib/volume_condition.rb +36 -0
  28. data/lib/class_extensions.rb +167 -0
  29. data/lib/ib/base.rb +109 -0
  30. data/lib/ib/base_properties.rb +178 -0
  31. data/lib/ib/connection.rb +573 -0
  32. data/lib/ib/constants.rb +402 -0
  33. data/lib/ib/contract.rb +30 -0
  34. data/lib/ib/errors.rb +52 -0
  35. data/lib/ib/messages/abstract_message.rb +68 -0
  36. data/lib/ib/messages/incoming/abstract_message.rb +116 -0
  37. data/lib/ib/messages/incoming/abstract_tick.rb +25 -0
  38. data/lib/ib/messages/incoming/account_message.rb +26 -0
  39. data/lib/ib/messages/incoming/alert.rb +34 -0
  40. data/lib/ib/messages/incoming/contract_data.rb +105 -0
  41. data/lib/ib/messages/incoming/contract_message.rb +13 -0
  42. data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
  43. data/lib/ib/messages/incoming/execution_data.rb +50 -0
  44. data/lib/ib/messages/incoming/histogram_data.rb +30 -0
  45. data/lib/ib/messages/incoming/historical_data.rb +65 -0
  46. data/lib/ib/messages/incoming/historical_data_update.rb +50 -0
  47. data/lib/ib/messages/incoming/managed_accounts.rb +21 -0
  48. data/lib/ib/messages/incoming/market_depth.rb +34 -0
  49. data/lib/ib/messages/incoming/market_depth_l2.rb +15 -0
  50. data/lib/ib/messages/incoming/next_valid_id.rb +19 -0
  51. data/lib/ib/messages/incoming/open_order.rb +290 -0
  52. data/lib/ib/messages/incoming/order_status.rb +85 -0
  53. data/lib/ib/messages/incoming/portfolio_value.rb +47 -0
  54. data/lib/ib/messages/incoming/position_data.rb +21 -0
  55. data/lib/ib/messages/incoming/positions_multi.rb +15 -0
  56. data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
  57. data/lib/ib/messages/incoming/receive_fa.rb +30 -0
  58. data/lib/ib/messages/incoming/scanner_data.rb +54 -0
  59. data/lib/ib/messages/incoming/tick_by_tick.rb +77 -0
  60. data/lib/ib/messages/incoming/tick_efp.rb +18 -0
  61. data/lib/ib/messages/incoming/tick_generic.rb +12 -0
  62. data/lib/ib/messages/incoming/tick_option.rb +60 -0
  63. data/lib/ib/messages/incoming/tick_price.rb +60 -0
  64. data/lib/ib/messages/incoming/tick_size.rb +55 -0
  65. data/lib/ib/messages/incoming/tick_string.rb +13 -0
  66. data/lib/ib/messages/incoming.rb +292 -0
  67. data/lib/ib/messages/outgoing/abstract_message.rb +84 -0
  68. data/lib/ib/messages/outgoing/bar_request_message.rb +247 -0
  69. data/lib/ib/messages/outgoing/new-place-order.rb +193 -0
  70. data/lib/ib/messages/outgoing/old-place-order.rb +147 -0
  71. data/lib/ib/messages/outgoing/place_order.rb +149 -0
  72. data/lib/ib/messages/outgoing/request_account_summary.rb +79 -0
  73. data/lib/ib/messages/outgoing/request_historical_data.rb +182 -0
  74. data/lib/ib/messages/outgoing/request_market_data.rb +102 -0
  75. data/lib/ib/messages/outgoing/request_market_depth.rb +57 -0
  76. data/lib/ib/messages/outgoing/request_real_time_bars.rb +48 -0
  77. data/lib/ib/messages/outgoing/request_scanner_subscription.rb +73 -0
  78. data/lib/ib/messages/outgoing/request_tick_by_tick_data.rb +21 -0
  79. data/lib/ib/messages/outgoing.rb +410 -0
  80. data/lib/ib/messages.rb +139 -0
  81. data/lib/ib/order_condition.rb +26 -0
  82. data/lib/ib/plugins.rb +27 -0
  83. data/lib/ib/prepare_data.rb +61 -0
  84. data/lib/ib/raw_message_parser.rb +99 -0
  85. data/lib/ib/socket.rb +83 -0
  86. data/lib/ib/support.rb +236 -0
  87. data/lib/ib/version.rb +6 -0
  88. data/lib/ib-api.rb +44 -0
  89. data/lib/server_versions.rb +145 -0
  90. data/lib/support/array_function.rb +28 -0
  91. data/lib/support/logging.rb +45 -0
  92. data/models/ib/account.rb +72 -0
  93. data/models/ib/account_value.rb +33 -0
  94. data/models/ib/bag.rb +55 -0
  95. data/models/ib/bar.rb +31 -0
  96. data/models/ib/combo_leg.rb +127 -0
  97. data/models/ib/contract.rb +411 -0
  98. data/models/ib/contract_detail.rb +118 -0
  99. data/models/ib/execution.rb +67 -0
  100. data/models/ib/forex.rb +12 -0
  101. data/models/ib/future.rb +64 -0
  102. data/models/ib/index.rb +14 -0
  103. data/models/ib/option.rb +149 -0
  104. data/models/ib/option_detail.rb +84 -0
  105. data/models/ib/order.rb +720 -0
  106. data/models/ib/order_state.rb +155 -0
  107. data/models/ib/portfolio_value.rb +86 -0
  108. data/models/ib/spread.rb +176 -0
  109. data/models/ib/stock.rb +25 -0
  110. data/models/ib/underlying.rb +32 -0
  111. data/plugins/ib/advanced-account.rb +442 -0
  112. data/plugins/ib/alerts/base-alert.rb +125 -0
  113. data/plugins/ib/alerts/gateway-alerts.rb +15 -0
  114. data/plugins/ib/alerts/order-alerts.rb +73 -0
  115. data/plugins/ib/auto-adjust.rb +0 -0
  116. data/plugins/ib/connection-tools.rb +122 -0
  117. data/plugins/ib/eod.rb +326 -0
  118. data/plugins/ib/greeks.rb +102 -0
  119. data/plugins/ib/managed-accounts.rb +274 -0
  120. data/plugins/ib/market-price.rb +150 -0
  121. data/plugins/ib/option-chain.rb +167 -0
  122. data/plugins/ib/order-flow.rb +157 -0
  123. data/plugins/ib/order-prototypes/abstract.rb +67 -0
  124. data/plugins/ib/order-prototypes/adaptive.rb +40 -0
  125. data/plugins/ib/order-prototypes/all-in-one.rb +46 -0
  126. data/plugins/ib/order-prototypes/combo.rb +46 -0
  127. data/plugins/ib/order-prototypes/forex.rb +40 -0
  128. data/plugins/ib/order-prototypes/limit.rb +193 -0
  129. data/plugins/ib/order-prototypes/market.rb +116 -0
  130. data/plugins/ib/order-prototypes/pegged.rb +169 -0
  131. data/plugins/ib/order-prototypes/premarket.rb +31 -0
  132. data/plugins/ib/order-prototypes/stop.rb +202 -0
  133. data/plugins/ib/order-prototypes/volatility.rb +39 -0
  134. data/plugins/ib/order-prototypes.rb +118 -0
  135. data/plugins/ib/probability-of-expiring.rb +109 -0
  136. data/plugins/ib/process-orders.rb +155 -0
  137. data/plugins/ib/roll.rb +86 -0
  138. data/plugins/ib/spread-prototypes/butterfly.rb +77 -0
  139. data/plugins/ib/spread-prototypes/calendar.rb +97 -0
  140. data/plugins/ib/spread-prototypes/stock-spread.rb +56 -0
  141. data/plugins/ib/spread-prototypes/straddle.rb +70 -0
  142. data/plugins/ib/spread-prototypes/strangle.rb +93 -0
  143. data/plugins/ib/spread-prototypes/vertical.rb +83 -0
  144. data/plugins/ib/spread-prototypes.rb +70 -0
  145. data/plugins/ib/symbols/abstract.rb +136 -0
  146. data/plugins/ib/symbols/bonds.rb +28 -0
  147. data/plugins/ib/symbols/cfd.rb +19 -0
  148. data/plugins/ib/symbols/combo.rb +46 -0
  149. data/plugins/ib/symbols/commodity.rb +17 -0
  150. data/plugins/ib/symbols/forex.rb +41 -0
  151. data/plugins/ib/symbols/futures.rb +127 -0
  152. data/plugins/ib/symbols/index.rb +43 -0
  153. data/plugins/ib/symbols/options.rb +99 -0
  154. data/plugins/ib/symbols/stocks.rb +44 -0
  155. data/plugins/ib/symbols/version.rb +5 -0
  156. data/plugins/ib/symbols.rb +118 -0
  157. data/plugins/ib/verify.rb +226 -0
  158. data/symbols/w20.yml +210 -0
  159. data/t.txt +20 -0
  160. data/update.md +71 -0
  161. metadata +327 -0
@@ -0,0 +1,402 @@
1
+ module IB
2
+
3
+ ### Widely used TWS constants:
4
+
5
+ EOL = "\0"
6
+ # TWS_MAX is TWSMAX (transmitted from the TWS) minus the first digit (1)
7
+ # Anything bigger then TWS_MAX is considered as nil (in read_decimal @ messages/incomming/abstract_message.rb)
8
+ TWS_MAX = 79769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0
9
+ TWSMAX = 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0
10
+
11
+
12
+ # Enumeration of bar size types for convenience.
13
+ # Bar sizes less than 30 seconds do not work for some securities.
14
+ BAR_SIZES = {'1 sec' => :sec1,
15
+ '5 secs' => :sec5,
16
+ '15 secs' =>:sec15,
17
+ '30 secs' =>:sec30,
18
+ '1 min' => :min1,
19
+ '2 mins' => :min2,
20
+ '3 mins' => :min3,
21
+ '5 mins' => :min5,
22
+ '15 mins' =>:min15,
23
+ '30 mins' =>:min30,
24
+ '1 hour' =>:hour1,
25
+ '4 hours' =>:hour4,
26
+ '8 hours' =>:hour8,
27
+ '1 day' => :day1,
28
+ '1 week' => :week1,
29
+ '1 month' => :month1,
30
+ }.freeze
31
+
32
+ # Enumeration of data types.
33
+ # Determines the nature of data being extracted. Valid values:
34
+ DATA_TYPES = {'TRADES' => :trades,
35
+ 'MIDPOINT' => :midpoint,
36
+ 'BID' => :bid,
37
+ 'ASK' => :ask,
38
+ 'BID_ASK' => :bid_ask,
39
+ 'HISTORICAL_VOLATILITY' => :historical_volatility,
40
+ 'OPTION_IMPLIED_VOLATILITY' => :option_implied_volatility,
41
+ 'OPTION_VOLUME' => :option_volume,
42
+ 'OPTION_OPEN_INTEREST' => :option_open_interest
43
+ }.freeze
44
+
45
+ ### These values are typically received from TWS in incoming messages
46
+
47
+ # Tick types as received in TickPrice and TickSize messages (enumeration)
48
+ TICK_TYPES = {
49
+ # int id => :Description # Corresponding API Event/Function/Method
50
+ 0 => :bid_size, # tickSize()
51
+ 1 => :bid_price, # tickPrice()
52
+ 2 => :ask_price, # tickPrice()
53
+ 3 => :ask_size, # tickSize()
54
+ 4 => :last_price, # tickPrice()
55
+ 5 => :last_size, # tickSize()
56
+ 6 => :high, # tickPrice()
57
+ 7 => :low, # tickPrice()
58
+ 8 => :volume, # tickSize()
59
+ 9 => :close_price, # tickPrice()
60
+ 10 => :bid_option, # tickOptionComputation() See Note 1 below
61
+ 11 => :ask_option, # tickOptionComputation() See => :Note 1 below
62
+ 12 => :last_option, # tickOptionComputation() See Note 1 below
63
+ 13 => :model_option, # tickOptionComputation() See Note 1 below
64
+ 14 => :open_tick, # tickPrice()
65
+ 15 => :low_13_week, # tickPrice()
66
+ 16 => :high_13_week, # tickPrice()
67
+ 17 => :low_26_week, # tickPrice()
68
+ 18 => :high_26_week, # tickPrice()
69
+ 19 => :low_52_week, # tickPrice()
70
+ 20 => :high_52_week, # tickPrice()
71
+ 21 => :avg_volume, # tickSize()
72
+ 22 => :open_interest, # tickSize()
73
+ 23 => :option_historical_vol, # tickGeneric()
74
+ 24 => :option_implied_vol, # tickGeneric()
75
+ 25 => :option_bid_exch, # not USED
76
+ 26 => :option_ask_exch, # not USED
77
+ 27 => :option_call_open_interest, # tickSize()
78
+ 28 => :option_put_open_interest, # tickSize()
79
+ 29 => :option_call_volume, # tickSize()
80
+ 30 => :option_put_volume, # tickSize()
81
+ 31 => :index_future_premium, # tickGeneric()
82
+ 32 => :bid_exch, # tickString()
83
+ 33 => :ask_exch, # tickString()
84
+ 34 => :auction_volume, # not USED
85
+ 35 => :auction_price, # not USED
86
+ 36 => :auction_imbalance, # not USED
87
+ 37 => :mark_price, # tickPrice()
88
+ 38 => :bid_efp_computation, # tickEFP()
89
+ 39 => :ask_efp_computation, # tickEFP()
90
+ 40 => :last_efp_computation, # tickEFP()
91
+ 41 => :open_efp_computation, # tickEFP()
92
+ 42 => :high_efp_computation, # tickEFP()
93
+ 43 => :low_efp_computation, # tickEFP()
94
+ 44 => :close_efp_computation, # tickEFP()
95
+ 45 => :last_timestamp, # tickString()
96
+ 46 => :shortable, # tickGeneric()
97
+ 47 => :fundamental_ratios, # tickString()
98
+ 48 => :rt_volume, # tickGeneric()
99
+ 49 => :halted, # see Note 2 below.
100
+ 50 => :bid_yield, # tickPrice() See Note 3 below
101
+ 51 => :ask_yield, # tickPrice() See Note 3 below
102
+ 52 => :last_yield, # tickPrice() See Note 3 below
103
+ 53 => :cust_option_computation, # tickOptionComputation()
104
+ 54 => :trade_count, # tickGeneric()
105
+ 55 => :trade_rate, # tickGeneric()
106
+ 56 => :volume_rate, # tickGeneric()
107
+ 57 => :last_rth_trade, #
108
+ 58 => :rt_historical_vol,
109
+ 59 => :ib_dividends,
110
+ 60 => :bond_factor_multiplier,
111
+ 61 => :regulatory_imbalance,
112
+ 62 => :news_tick,
113
+ 63 => :short_term_volume_3_min,
114
+ 64 => :short_term_volume_5_min,
115
+ 65 => :short_term_volume_10_min,
116
+ 66 => :delayed_bid,
117
+ 67 => :delayed_ask,
118
+ 68 => :delayed_last,
119
+ 69 => :delayed_bid_size,
120
+ 70 => :delayed_ask_size,
121
+ 71 => :delayed_last_size,
122
+ 72 => :delayed_high,
123
+ 73 => :delayed_low,
124
+ 74 => :delayed_volume,
125
+ 75 => :delayed_close,
126
+ 76 => :delayed_open,
127
+ 77 => :rt_trd_volume,
128
+ 78 => :creditman_mark_price,
129
+ 79 => :creditman_slow_mark_price,
130
+ 80 => :delayed_bid_option,
131
+ 81 => :delayed_ask_option,
132
+ 82 => :delayed_last_option,
133
+ 83 => :delayed_model_option,
134
+ 84 => :last_exch,
135
+ 85 => :last_reg_time,
136
+ 86 => :futures_open_interest,
137
+ 87 => :avg_opt_volume,
138
+ 88 => :not_set,
139
+ 105 => :average_option_volume #(for Stocks) tickGeneric()
140
+
141
+
142
+ # Note 1: Tick types BID_OPTION, ASK_OPTION, LAST_OPTION, and MODEL_OPTION return
143
+ # all Greeks (delta, gamma, vega, theta), the underlying price and the
144
+ # stock and option reference price when requested.
145
+ # MODEL_OPTION also returns model implied volatility.
146
+ # Note 2: When trading is halted for a contract, TWS receives a special tick:
147
+ # haltedLast=1. When trading is resumed, TWS receives haltedLast=0.
148
+ # A tick type, HALTED, tick ID= 49, is now available in regular market
149
+ # data via the API to indicate this halted state. Possible values for
150
+ # this new tick type are: 0 = Not halted, 1 = Halted.
151
+ # Note 3: Applies to bond contracts only.
152
+ }
153
+
154
+ # Financial Advisor types (FaMsgTypeName)
155
+ FA_TYPES = {
156
+ 1 => :groups,
157
+ 2 => :profiles,
158
+ 3 => :aliases}.freeze
159
+
160
+ # Received in new MarketDataType (58 incoming) message
161
+ MARKET_DATA_TYPES = {
162
+ 0 => :unknown,
163
+ 1 => :real_time,
164
+ 2 => :frozen,
165
+ 3 => :delayed,
166
+ 4 => :frozen_delayed }.freeze
167
+
168
+ # Market depth messages contain these "operation" codes to tell you what to do with the data.
169
+ # See also http://www.interactivebrokers.com/php/apiUsersGuide/apiguide/java/updatemktdepth.htm
170
+ MARKET_DEPTH_OPERATIONS = {
171
+ 0 => :insert, # New order, insert into the row identified by :position
172
+ 1 => :update, # Update the existing order at the row identified by :position
173
+ 2 => :delete # Delete the existing order at the row identified by :position
174
+ }.freeze
175
+
176
+ MARKET_DEPTH_SIDES = {
177
+ 0 => :ask,
178
+ 1 => :bid
179
+ }.freeze
180
+
181
+ ORDER_TYPES =
182
+ {'LMT' => :limit, # Limit Order
183
+ 'LIT' => :limit_if_touched, # Limit if Touched
184
+ 'LOC' => :limit_on_close, # Limit-on-Close LMTCLS ?
185
+ 'LOO' => :limit_on_open, # Limit-on-Open
186
+ 'MKT' => :market, # Market
187
+ 'MIT' => :market_if_touched, # Market-if-Touched
188
+ 'MOC' => :market_on_close, # Market-on-Close MKTCLSL ?
189
+ 'MOO' => :market_on_open, # Market-on-Open
190
+ 'MTL' => :market_to_limit, # Market-to-Limit
191
+ 'MKT PRT' => :market_protected, # Market with Protection
192
+ 'QUOTE' => :request_for_quote, # Request for Quote
193
+ 'STP' => :stop, # Stop
194
+ 'STP LMT' => :stop_limit, # Stop Limit
195
+ 'STP PRT' => :stop_protected, # Stop with Protection
196
+ 'TRAIL' => :trailing_stop, # Trailing Stop
197
+ 'TRAIL LIMIT' => :trailing_limit, # Trailing Stop Limit
198
+ 'TRAIL LIT' => :trailing_limit_if_touched, # Trailing Limit if Touched
199
+ 'TRAIL MIT' => :trailing_market_if_touched, # Trailing Market If Touched
200
+ 'REL' => :pegged_to_primary , # Relative aka Pegged to Primary
201
+ 'BOX TOP' => :box_top, # Box Top
202
+ 'PEG MKT' => :pegged_to_market, # Pegged-to-Market
203
+ 'PEG STK' => :pegged_to_market, # Pegged-to-Stock
204
+ 'PEG MID' => :pegged_to_midpoint, # Pegged-to-Midpoint
205
+ 'PEG BENCH' => :pegged_to_benchmark, # Pegged-to-Benchmark # Vers. 102
206
+ 'PEG BEST' => :pegged_to_best,
207
+ 'VWAP' => :vwap, # VWAP-Guaranted
208
+ 'VOL' => :volatility, # Volatility
209
+ 'SCALE' => :scale, # Scale
210
+ 'NONE' => :none, # Used to indicate no hedge in :delta_neutral_order_type
211
+ 'None' => :none, # Used to indicate no hedge in :delta_neutral_order_type
212
+ }.freeze
213
+ # Valid security types (sec_type attribute of IB::Contract)
214
+ SECURITY_TYPES =
215
+ { 'BAG' => :bag,
216
+ 'BOND' => :bond,
217
+ 'CASH' => :forex,
218
+ 'CMDTY'=> :commodity,
219
+ 'CFD' => :cfd,
220
+ 'FUT' => :future,
221
+ 'CONTFUT' => :continous_future,
222
+ 'FUT+CONTFUT' => :all_futures,
223
+ 'FOP' => :futures_option,
224
+ 'FUND' => :fund, # ETF?
225
+ 'IND' => :index,
226
+ 'NEWS' => :news,
227
+ 'OPT' => :option,
228
+ 'IOPT' => :dutch_option,
229
+ 'STK' => :stock,
230
+ 'WAR' => :warrant,
231
+ 'ICU' => :icu,
232
+ 'ICS' => :ics,
233
+ 'BILL' => :bill,
234
+ 'BSK' => :basket,
235
+ 'FWD' => :forward,
236
+ 'FIXED' => :fixed ,
237
+ 'CRYPTO' => :crypto,
238
+ "EC" => :event_contract #
239
+ # "Event Contracts are daily-expiring, cash settled, European Style, binary-options on futures contracts, offering short-term trading opportunities for individuals seeking to take a position on daily price moves on futures using smaller-value trades of up to $20 per contract. The Event Contracts allow market participants to trade their view on the price direction of key futures markets at the end of each day’s trading session."
240
+ }.freeze
241
+
242
+ # Obtain symbolic value from given property code:
243
+ # VALUES[:side]['B'] -> :buy
244
+ VALUES = {
245
+ :sec_type => SECURITY_TYPES,
246
+ :order_type => ORDER_TYPES,
247
+ :delta_neutral_order_type => ORDER_TYPES,
248
+
249
+ :origin => {0 => :customer, 1 => :firm},
250
+ :volatility_type => {1 => :daily, 2 => :annual},
251
+ :reference_price_type => {1 => :average, 2 => :bid_or_ask},
252
+
253
+ # This property encodes differently for ComboLeg and Order objects,
254
+ # we use ComboLeg codes and transcode for Order codes as needed
255
+ :open_close =>
256
+ {0 => :same, # Default for Legs, same as the parent (combo) security.
257
+ 1 => :open, # Open. For Legs, this value is only used by institutions.
258
+ 2 => :close, # Close. For Legs, this value is only used by institutions.
259
+ 3 => :unknown}, # WTF
260
+
261
+ :right =>
262
+ {'' => :none, # Not an option
263
+ 'P' => :put,
264
+ 'C' => :call},
265
+
266
+ :side => # AKA action
267
+ {'B' => :buy, # or BOT
268
+ 'S' => :sell, # or SLD
269
+ 'T' => :short, # short
270
+ 'X' => :short_exempt # Short Sale Exempt action. This allows some orders
271
+ # to be exempt from the SEC recent changes to Regulation SHO, which
272
+ # eliminated the old uptick rule and replaced it with a new "circuit breaker"
273
+ # rule, and allows some orders to be exempt from the new rule.
274
+ },
275
+
276
+ :short_sale_slot =>
277
+ {0 => :default, # The only valid option for retail customers
278
+ 1 => :broker, # Shares are at your clearing broker, institutions
279
+ 2 => :third_party}, # Shares will be delivered from elsewhere, institutions
280
+
281
+ :oca_type =>
282
+ {0 => :none, # Not a member of OCA group
283
+ 1 => :cancel_with_block, # Cancel all remaining orders with block
284
+ 2 => :reduce_with_block, # Remaining orders are reduced in size with block
285
+ 3 => :reduce_no_block}, # Remaining orders are reduced in size with no block
286
+
287
+ :auction_strategy =>
288
+ {0 => :none, # Not a BOX order
289
+ 1 => :match,
290
+ 2 => :improvement,
291
+ 3 => :transparent},
292
+
293
+ :trigger_method =>
294
+ {0 => :default, # "double bid/ask" used for OTC/US options, "last" otherswise.
295
+ 1 => :double_bid_ask, # stops are triggered by 2 consecutive bid or ask prices.
296
+ 2 => :last, # stops are triggered based on the last price.
297
+ 3 => :double_last,
298
+ 4 => :bid_ask, # bid >= trigger price for buy orders, ask <= trigger for sell orders
299
+ 7 => :last_or_bid_ask, # bid OR last price >= trigger price for buy orders
300
+ 8 => :mid_point}, # midpoint >= trigger price for buy orders and the
301
+ # spread between the bid and ask must be less than 0.1% of the midpoint
302
+
303
+ :hedge_type =>
304
+ {'D' => :delta, # parent order is an option and the child order is a stock
305
+ 'B' => :beta, # offset market risk by entering into a position with
306
+ # another contract based on the system or user-defined beta
307
+ 'F' => :forex, # offset risk with currency different from your base currency
308
+ 'P' => :pair}, # trade a mis-valued pair of contracts and provide the
309
+ # ratio between the parent and hedging child order
310
+
311
+ :clearing_intent =>
312
+ {'' => :none,
313
+ 'IB' => :ib,
314
+ 'AWAY' => :away,
315
+ 'PTA' => :post_trade_allocation},
316
+
317
+ :delta_neutral_clearing_intent =>
318
+ {'' => :none,
319
+ 'IB' => :ib,
320
+ 'AWAY' => :away,
321
+ 'PTA' => :post_trade_allocation},
322
+
323
+ :tif =>
324
+ {'DAY' => :day,
325
+ 'GAT' => :good_after_time,
326
+ 'GTD' => :good_till_date,
327
+ 'GTC' => :good_till_cancelled,
328
+ 'IOC' => :immediate_or_cancel,
329
+ 'OPG' => :opening_price,
330
+ 'AUC' => :at_auction},
331
+
332
+ :rule_80a =>
333
+ {'I' => :individual,
334
+ 'A' => :agency,
335
+ 'W' => :agent_other_member,
336
+ 'J' => :individual_ptia,
337
+ 'U' => :agency_ptia,
338
+ 'M' => :agent_other_member_ptia,
339
+ 'K' => :individual_pt,
340
+ 'Y' => :agency_pt,
341
+ 'N' => :agent_other_member_pt},
342
+
343
+ :opt? => # TODO: unknown Order property, like OPT_BROKER_DEALER... in Order.java
344
+ {'?' => :unknown,
345
+ 'b' => :broker_dealer,
346
+ 'c' => :customer,
347
+ 'f' => :firm,
348
+ 'm' => :isemm,
349
+ 'n' => :farmm,
350
+ 'y' => :specialist},
351
+ # conditions
352
+ :conjunction_connection => { 'o' => :or, 'a' => :and },
353
+ :operator => { 1 => '>=' , 0 => '<=' }
354
+
355
+ }.freeze
356
+
357
+ # Obtain property code from given symbolic value:
358
+ # CODES[:side][:buy] -> 'B'
359
+ CODES = Hash[VALUES.map { |property, hash| [property, hash.invert] }].freeze
360
+
361
+ # Most common property processors
362
+ PROPS = {:side =>
363
+ {:set => proc { |val| # BUY(BOT)/SELL(SLD)/SSHORT/SSHORTX
364
+ self[:side] = case val.to_s.upcase
365
+ when /SHORT.*X|\AX\z/
366
+ 'X'
367
+ when /SHORT|\AT\z/
368
+ 'T'
369
+ when /\AB/
370
+ 'B'
371
+ when /\AS/
372
+ 'S'
373
+ end },
374
+ :validate =>
375
+ {:format =>
376
+ {:with => /\Abuy\z|\Asell\z|\Ashort\z|\Ashort_exempt\z/,
377
+ :message => "should be buy/sell/short"}
378
+ }
379
+ },
380
+
381
+ :open_close =>
382
+ {:set => proc { |val|
383
+ self[:open_close] = case val.to_s.upcase[0..0]
384
+ when 'S', '0' # SAME
385
+ 0
386
+ when 'O', '1' # OPEN
387
+ 1
388
+ when 'C', '2' # CLOSE
389
+ 2
390
+ when 'U', '3' # Unknown
391
+ 3
392
+ end
393
+ },
394
+ :validate =>
395
+ {:format =>
396
+ {:with => /\Asame\z|\Aopen\z|\Aclose\z|\Aunknown\z/,
397
+ :message => "should be same/open/close/unknown"}
398
+ },
399
+ }
400
+ }.freeze
401
+
402
+ end # module IB
@@ -0,0 +1,30 @@
1
+ module IB
2
+
3
+ # Here we reopen IB::Contract and implenent the dynamic build facility
4
+ # This file is required after zeitwerk processed the basic includes.
5
+ #
6
+ class Contract
7
+ # Contract subclasses representing specialized security types.
8
+ using IB::Support
9
+
10
+ Subclasses = Hash.new(Contract)
11
+ Subclasses[:bag] = IB::Bag
12
+ Subclasses[:option] = IB::Option
13
+ Subclasses[:futures_option] = IB::FutureOption
14
+ Subclasses[:future] = IB::Future
15
+ Subclasses[:stock] = IB::Stock
16
+ Subclasses[:forex] = IB::Forex
17
+ Subclasses[:index] = IB::Index
18
+
19
+
20
+ # This builds an appropriate Contract subclass based on its type
21
+ #
22
+ # the method is also used to copy Contract.values to new instances
23
+ def self.build opts = {}
24
+ subclass =( VALUES[:sec_type][opts[:sec_type]] || opts['sec_type'] || opts[:sec_type]).to_sym
25
+ Contract::Subclasses[subclass].new opts
26
+ end
27
+
28
+
29
+ end # class Contract
30
+ end
data/lib/ib/errors.rb ADDED
@@ -0,0 +1,52 @@
1
+ module IB
2
+
3
+ # Error handling
4
+ class Error < RuntimeError
5
+ end
6
+
7
+ class ConnectionError < ArgumentError
8
+ end
9
+
10
+ class ArgumentError < ArgumentError
11
+ end
12
+
13
+ class SymbolError < ArgumentError
14
+ end
15
+
16
+ class LoadError < LoadError
17
+ end
18
+
19
+ class FlexError < RuntimeError
20
+ end
21
+
22
+ class TransmissionError < RuntimeError
23
+ end
24
+ # define a custom ErrorClass which can be fired if a verification fails
25
+ class VerifyError < StandardError
26
+ end
27
+ end # module IB
28
+
29
+ # Patching Object with universally accessible top level error method.
30
+ # The method is used throughout the lib instead of plainly raising exceptions.
31
+ # This allows lib user to easily inject user-specific error handling into the lib
32
+ # by just replacing Object#error method.
33
+ def error message, type=:standard, backtrace=nil
34
+ e = case type
35
+ when :standard
36
+ IB::Error.new message
37
+ when :args
38
+ IB::ArgumentError.new message
39
+ when :symbol
40
+ IB::SymbolError.new message
41
+ when :load
42
+ IB::LoadError.new message
43
+ when :flex
44
+ IB::FlexError.new message
45
+ when :reader
46
+ IB::TransmissionError.new message
47
+ when :verify
48
+ IB::VerifyError.new message
49
+ end
50
+ e.set_backtrace(caller) if backtrace
51
+ raise e
52
+ end
@@ -0,0 +1,68 @@
1
+ module IB
2
+ module Messages
3
+
4
+ # This is just a basic generic message from the server.
5
+ #
6
+ # Class variables:
7
+ # @message_id - int: message id.
8
+ # @message_type - Symbol: message type (e.g. :OpenOrderEnd)
9
+ #
10
+ # Instance attributes (at least):
11
+ # @version - int: current version of message format.
12
+ # @data - Hash of actual data read from a stream.
13
+ class AbstractMessage
14
+
15
+ # Class methods
16
+ def self.data_map # Map for converting between structured message and raw data
17
+ @data_map ||= []
18
+ end
19
+
20
+ def self.version # Per class, minimum message version supported
21
+ @version || 1
22
+ end
23
+
24
+ # including server-version as method to every message class
25
+ def server_version
26
+ Connection.current &.server_version || 165
27
+ end
28
+ def self.message_id
29
+ @message_id
30
+ end
31
+
32
+ # Returns message type Symbol (e.g. :OpenOrderEnd)
33
+ def self.message_type
34
+ to_s.split(/::/).last.to_sym
35
+ end
36
+
37
+ def message_id
38
+ self.class.message_id
39
+ end
40
+
41
+ def request_id
42
+ @data[:request_id].presence || nil
43
+ end
44
+
45
+ def message_type
46
+ self.class.message_type
47
+ end
48
+
49
+ attr_accessor :created_at, :data
50
+
51
+ def self.properties?
52
+ @given_arguments
53
+ end
54
+
55
+
56
+ def to_human
57
+ "<#{self.message_type}:" +
58
+ @data.map do |key, value|
59
+ unless [:version].include?(key)
60
+ " #{key} #{ value.is_a?(Hash) ? value.inspect : value}"
61
+ end
62
+ end.compact.join(',') + " >"
63
+ end
64
+
65
+ end # class AbstractMessage
66
+
67
+ end # module Messages
68
+ end # module IB
@@ -0,0 +1,116 @@
1
+ #require 'ib/messages/abstract_message'
2
+ #require 'ib/support'
3
+ require 'ox'
4
+ module IB
5
+ module Messages
6
+ module Incoming
7
+ using IB::Support # refine Array-method for decoding of IB-Messages
8
+
9
+
10
+ # Container for specific message classes, keyed by their message_ids
11
+ Classes = {}
12
+
13
+ class AbstractMessage < IB::Messages::AbstractMessage
14
+
15
+ attr_accessor :buffer # is an array
16
+
17
+ def version # Per message, received messages may have the different versions
18
+ @data[:version]
19
+ end
20
+
21
+ def check_version actual, expected
22
+ unless actual == expected || expected.is_a?(Array) && expected.include?(actual)
23
+ puts self.class.name
24
+ error "Unsupported version #{actual} received, expected #{expected}"
25
+ end
26
+ end
27
+
28
+ # Create incoming message from a given source (IB Socket or data Hash)
29
+ def initialize source
30
+ @created_at = Time.now
31
+ if source.is_a?(Hash) # Source is a @data Hash
32
+ @data = source
33
+ @buffer =[] # initialize empty buffer, indicates a successful initializing
34
+ else
35
+ @buffer = source
36
+ ### DEBUG DEBUG DEBUG RAW STREAM ###############
37
+ # if uncommented, the raw-input from the tws is included in the logging
38
+ ## puts "BUFFER :> \n #{buffer.inspect} \n"
39
+ ### DEBUG DEBUG DEBUG RAW STREAM ###############
40
+ @data = Hash.new
41
+ self.load
42
+ end
43
+ end
44
+
45
+ def valid?
46
+ @buffer.empty?
47
+ end
48
+
49
+ ## more recent messages omit the transmission of a version
50
+ ## thus just load the parameter-map
51
+ def simple_load
52
+ load_map *self.class.data_map
53
+ rescue IB::Error => e
54
+ error "Reading #{self.class}: #{e.class}: #{e.message}", :load, e.backtrace
55
+ end
56
+ # Every message loads received message version first
57
+ # Override the load method in your subclass to do actual reading into @data.
58
+ def load
59
+ unless self.class.version.zero?
60
+ @data[:version] = buffer.read_int
61
+ check_version @data[:version], self.class.version
62
+ end
63
+ simple_load
64
+ end
65
+
66
+ # Load @data from the buffer according to the given data map.
67
+ #
68
+ # map is a series of Arrays in the format of
69
+ # [ :name, :type ], [ :group, :name, :type]
70
+ # type identifiers must have a corresponding read_type method on the buffer-class (read_int, etc.).
71
+ # group is used to lump together aggregates, such as Contract or Order fields
72
+ def load_map(*map)
73
+ map.each do |instruction|
74
+ # We determine the function of the first element
75
+ head = instruction.first
76
+ case head
77
+ when Integer # >= Version condition: [ min_version, [map]]
78
+ load_map *instruction.drop(1) if version >= head
79
+
80
+ when Proc # Callable condition: [ condition, [map]]
81
+ load_map *instruction.drop(1) if head.call
82
+
83
+ when true # Pre-condition already succeeded!
84
+ load_map *instruction.drop(1)
85
+
86
+ when nil, false # Pre-condition already failed! Do nothing...
87
+
88
+ when Symbol # Normal map
89
+ group, name, type, block =
90
+ if instruction[2].nil? || instruction[2].is_a?(Proc) # lambda's are Proc's
91
+ [nil] + instruction # No group, [ :name, :type, (:block) ]
92
+ else
93
+ instruction # [ :group, :name, :type, (:block)]
94
+ end
95
+ begin
96
+ data = @buffer.__send__("read_#{type}", &block)
97
+ rescue IB::LoadError, NoMethodError => e
98
+ error "Reading #{self.class}: #{e.class}: #{e.message} --> Instruction: #{name}" , :reader, false
99
+ end
100
+ # debug puts data.inspect
101
+ if group
102
+ @data[group] ||= {}
103
+ @data[group][name] = data
104
+ else
105
+ @data[name] = data
106
+ end
107
+ else
108
+ error "Unrecognized instruction #{instruction}"
109
+ end
110
+ end
111
+ end
112
+
113
+ end # class AbstractMessage
114
+ end # module Incoming
115
+ end # module Messages
116
+ end # module IB