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.
- checksums.yaml +7 -0
- data/.gitignore +52 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CLAUDE.md +131 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +120 -0
- data/Guardfile +24 -0
- data/LICENSE +674 -0
- data/LLM_GUIDE.md +388 -0
- data/README.md +114 -0
- data/Rakefile +11 -0
- data/VERSION +1 -0
- data/api.gemspec +50 -0
- data/bin/console +96 -0
- data/bin/console.yml +3 -0
- data/bin/setup +8 -0
- data/bin/simple +91 -0
- data/changelog.md +32 -0
- data/conditions/ib/execution_condition.rb +31 -0
- data/conditions/ib/margin_condition.rb +28 -0
- data/conditions/ib/order_condition.rb +29 -0
- data/conditions/ib/percent_change_condition.rb +34 -0
- data/conditions/ib/price_condition.rb +44 -0
- data/conditions/ib/time_condition.rb +42 -0
- data/conditions/ib/volume_condition.rb +36 -0
- data/lib/class_extensions.rb +167 -0
- data/lib/ib/base.rb +109 -0
- data/lib/ib/base_properties.rb +178 -0
- data/lib/ib/connection.rb +573 -0
- data/lib/ib/constants.rb +402 -0
- data/lib/ib/contract.rb +30 -0
- data/lib/ib/errors.rb +52 -0
- data/lib/ib/messages/abstract_message.rb +68 -0
- data/lib/ib/messages/incoming/abstract_message.rb +116 -0
- data/lib/ib/messages/incoming/abstract_tick.rb +25 -0
- data/lib/ib/messages/incoming/account_message.rb +26 -0
- data/lib/ib/messages/incoming/alert.rb +34 -0
- data/lib/ib/messages/incoming/contract_data.rb +105 -0
- data/lib/ib/messages/incoming/contract_message.rb +13 -0
- data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
- data/lib/ib/messages/incoming/execution_data.rb +50 -0
- data/lib/ib/messages/incoming/histogram_data.rb +30 -0
- data/lib/ib/messages/incoming/historical_data.rb +65 -0
- data/lib/ib/messages/incoming/historical_data_update.rb +50 -0
- data/lib/ib/messages/incoming/managed_accounts.rb +21 -0
- data/lib/ib/messages/incoming/market_depth.rb +34 -0
- data/lib/ib/messages/incoming/market_depth_l2.rb +15 -0
- data/lib/ib/messages/incoming/next_valid_id.rb +19 -0
- data/lib/ib/messages/incoming/open_order.rb +290 -0
- data/lib/ib/messages/incoming/order_status.rb +85 -0
- data/lib/ib/messages/incoming/portfolio_value.rb +47 -0
- data/lib/ib/messages/incoming/position_data.rb +21 -0
- data/lib/ib/messages/incoming/positions_multi.rb +15 -0
- data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
- data/lib/ib/messages/incoming/receive_fa.rb +30 -0
- data/lib/ib/messages/incoming/scanner_data.rb +54 -0
- data/lib/ib/messages/incoming/tick_by_tick.rb +77 -0
- data/lib/ib/messages/incoming/tick_efp.rb +18 -0
- data/lib/ib/messages/incoming/tick_generic.rb +12 -0
- data/lib/ib/messages/incoming/tick_option.rb +60 -0
- data/lib/ib/messages/incoming/tick_price.rb +60 -0
- data/lib/ib/messages/incoming/tick_size.rb +55 -0
- data/lib/ib/messages/incoming/tick_string.rb +13 -0
- data/lib/ib/messages/incoming.rb +292 -0
- data/lib/ib/messages/outgoing/abstract_message.rb +84 -0
- data/lib/ib/messages/outgoing/bar_request_message.rb +247 -0
- data/lib/ib/messages/outgoing/new-place-order.rb +193 -0
- data/lib/ib/messages/outgoing/old-place-order.rb +147 -0
- data/lib/ib/messages/outgoing/place_order.rb +149 -0
- data/lib/ib/messages/outgoing/request_account_summary.rb +79 -0
- data/lib/ib/messages/outgoing/request_historical_data.rb +182 -0
- data/lib/ib/messages/outgoing/request_market_data.rb +102 -0
- data/lib/ib/messages/outgoing/request_market_depth.rb +57 -0
- data/lib/ib/messages/outgoing/request_real_time_bars.rb +48 -0
- data/lib/ib/messages/outgoing/request_scanner_subscription.rb +73 -0
- data/lib/ib/messages/outgoing/request_tick_by_tick_data.rb +21 -0
- data/lib/ib/messages/outgoing.rb +410 -0
- data/lib/ib/messages.rb +139 -0
- data/lib/ib/order_condition.rb +26 -0
- data/lib/ib/plugins.rb +27 -0
- data/lib/ib/prepare_data.rb +61 -0
- data/lib/ib/raw_message_parser.rb +99 -0
- data/lib/ib/socket.rb +83 -0
- data/lib/ib/support.rb +236 -0
- data/lib/ib/version.rb +6 -0
- data/lib/ib-api.rb +44 -0
- data/lib/server_versions.rb +145 -0
- data/lib/support/array_function.rb +28 -0
- data/lib/support/logging.rb +45 -0
- data/models/ib/account.rb +72 -0
- data/models/ib/account_value.rb +33 -0
- data/models/ib/bag.rb +55 -0
- data/models/ib/bar.rb +31 -0
- data/models/ib/combo_leg.rb +127 -0
- data/models/ib/contract.rb +411 -0
- data/models/ib/contract_detail.rb +118 -0
- data/models/ib/execution.rb +67 -0
- data/models/ib/forex.rb +12 -0
- data/models/ib/future.rb +64 -0
- data/models/ib/index.rb +14 -0
- data/models/ib/option.rb +149 -0
- data/models/ib/option_detail.rb +84 -0
- data/models/ib/order.rb +720 -0
- data/models/ib/order_state.rb +155 -0
- data/models/ib/portfolio_value.rb +86 -0
- data/models/ib/spread.rb +176 -0
- data/models/ib/stock.rb +25 -0
- data/models/ib/underlying.rb +32 -0
- data/plugins/ib/advanced-account.rb +442 -0
- data/plugins/ib/alerts/base-alert.rb +125 -0
- data/plugins/ib/alerts/gateway-alerts.rb +15 -0
- data/plugins/ib/alerts/order-alerts.rb +73 -0
- data/plugins/ib/auto-adjust.rb +0 -0
- data/plugins/ib/connection-tools.rb +122 -0
- data/plugins/ib/eod.rb +326 -0
- data/plugins/ib/greeks.rb +102 -0
- data/plugins/ib/managed-accounts.rb +274 -0
- data/plugins/ib/market-price.rb +150 -0
- data/plugins/ib/option-chain.rb +167 -0
- data/plugins/ib/order-flow.rb +157 -0
- data/plugins/ib/order-prototypes/abstract.rb +67 -0
- data/plugins/ib/order-prototypes/adaptive.rb +40 -0
- data/plugins/ib/order-prototypes/all-in-one.rb +46 -0
- data/plugins/ib/order-prototypes/combo.rb +46 -0
- data/plugins/ib/order-prototypes/forex.rb +40 -0
- data/plugins/ib/order-prototypes/limit.rb +193 -0
- data/plugins/ib/order-prototypes/market.rb +116 -0
- data/plugins/ib/order-prototypes/pegged.rb +169 -0
- data/plugins/ib/order-prototypes/premarket.rb +31 -0
- data/plugins/ib/order-prototypes/stop.rb +202 -0
- data/plugins/ib/order-prototypes/volatility.rb +39 -0
- data/plugins/ib/order-prototypes.rb +118 -0
- data/plugins/ib/probability-of-expiring.rb +109 -0
- data/plugins/ib/process-orders.rb +155 -0
- data/plugins/ib/roll.rb +86 -0
- data/plugins/ib/spread-prototypes/butterfly.rb +77 -0
- data/plugins/ib/spread-prototypes/calendar.rb +97 -0
- data/plugins/ib/spread-prototypes/stock-spread.rb +56 -0
- data/plugins/ib/spread-prototypes/straddle.rb +70 -0
- data/plugins/ib/spread-prototypes/strangle.rb +93 -0
- data/plugins/ib/spread-prototypes/vertical.rb +83 -0
- data/plugins/ib/spread-prototypes.rb +70 -0
- data/plugins/ib/symbols/abstract.rb +136 -0
- data/plugins/ib/symbols/bonds.rb +28 -0
- data/plugins/ib/symbols/cfd.rb +19 -0
- data/plugins/ib/symbols/combo.rb +46 -0
- data/plugins/ib/symbols/commodity.rb +17 -0
- data/plugins/ib/symbols/forex.rb +41 -0
- data/plugins/ib/symbols/futures.rb +127 -0
- data/plugins/ib/symbols/index.rb +43 -0
- data/plugins/ib/symbols/options.rb +99 -0
- data/plugins/ib/symbols/stocks.rb +44 -0
- data/plugins/ib/symbols/version.rb +5 -0
- data/plugins/ib/symbols.rb +118 -0
- data/plugins/ib/verify.rb +226 -0
- data/symbols/w20.yml +210 -0
- data/t.txt +20 -0
- data/update.md +71 -0
- metadata +327 -0
data/lib/ib/constants.rb
ADDED
|
@@ -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
|
data/lib/ib/contract.rb
ADDED
|
@@ -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
|