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,411 @@
1
+ module IB
2
+ class Contract < IB::Base
3
+ include BaseProperties
4
+
5
+ # Fields are Strings unless noted otherwise
6
+ prop :con_id, # int: The unique contract identifier.
7
+ :currency, # Only needed if there is an ambiguity, e.g. when SMART exchange
8
+ # and IBM is being requested (IBM can trade in GBP or USD).
9
+
10
+ :legs_description, # received in OpenOrder for all combos
11
+ :under_comp, # for delta neutral combos
12
+
13
+ :sec_type, # Security type. Valid values are: SECURITY_TYPES
14
+
15
+ :sec_id => :sup, # Unique identifier of the given secIdType.
16
+
17
+ :sec_id_type => :sup, # Security identifier, when querying contract details or
18
+ # when placing orders. Supported identifiers are:
19
+ # - ISIN (Example: Apple: US0378331005)
20
+ # - CUSIP (Example: Apple: 037833100)
21
+ # - SEDOL (6-AN + check digit. Example: BAE: 0263494)
22
+ # - RIC (exchange-independent RIC Root and exchange-
23
+ # identifying suffix. Ex: AAPL.O for Apple on NASDAQ.)
24
+
25
+ :symbol => :s, # This is the symbol of the underlying asset.
26
+ :local_symbol => :s, # Local exchange symbol of the underlying asset
27
+ :trading_class => :s,
28
+ # Future/option contract multiplier (only needed when multiple possibilities exist)
29
+ :multiplier => :d,
30
+
31
+ :strike => :f, # double: The strike price.
32
+ :expiry => :s, # The expiration date. Use the format YYYYMM or YYYYMMDD
33
+ :last_trading_day => :s, # the tws returns the last trading day in Format YYYYMMMDD hh:mm
34
+ # which may differ from the expiry
35
+ :exchange => :sup, # The order destination, such as Smart.
36
+ :primary_exchange => :sup, # Non-SMART exchange where the contract trades.
37
+ :include_expired => :bool, # When true, contract details requests and historical
38
+ # data queries can be performed pertaining to expired contracts.
39
+ # Note: Historical data queries on expired contracts are
40
+ # limited to the last year of the contracts life, and are
41
+ # only supported for expired futures contracts.
42
+ # This field can NOT be set to true for orders.
43
+
44
+
45
+ # Specifies a Put or Call. Valid input values are: P, PUT, C, CALL
46
+ :right => {
47
+ :set => proc { |val|
48
+ self[:right] =
49
+ case val.to_s.upcase
50
+ when 'NONE', '', '0', '?'
51
+ ''
52
+ when 'PUT', 'P'
53
+ 'P'
54
+ when 'CALL', 'C'
55
+ 'C'
56
+ else
57
+ val
58
+ end
59
+ },
60
+ :validate => {:format => {:with => /\Aput$|^call$|^none\z/,
61
+ :message => "should be put, call or none"}}
62
+ }
63
+
64
+ attr_accessor :description # NB: local to ib, not part of TWS.
65
+
66
+ ### Associations
67
+ has_many :misc # multi purpose association
68
+ has_many :orders # Placed for this Contract
69
+ has_many :portfolio_values
70
+
71
+ has_many :bars # Possibly representing trading history for this Contract
72
+
73
+ has_one :contract_detail # Volatile info about this Contract
74
+
75
+ # For Contracts that are part of BAa ## leg is now a method of contract
76
+ # has_one :leg #, :class_name => 'ComboLeg', :foreign_key => :leg_contract_id
77
+ # has_one :combo, :class_name => 'Contract', :through => :leg
78
+
79
+ # for Combo/BAG Contracts that contain ComboLegs
80
+ has_many :combo_legs#, :foreign_key => :combo_id
81
+ # has_many :leg_contracts, :class_name => 'Contract', :through => :combo_legs
82
+ # alias legs combo_legs
83
+ # alias legs= combo_legs=
84
+
85
+ # alias combo_legs_description legs_description
86
+ # alias combo_legs_description= legs_description=
87
+
88
+ # for Delta-Neutral Combo Contracts
89
+ has_one :underlying
90
+ alias under_comp underlying
91
+ alias under_comp= underlying=
92
+
93
+
94
+ ### Extra validations
95
+ validates_inclusion_of :sec_type, :in => CODES[:sec_type].keys,
96
+ :message => "should be valid security type"
97
+
98
+ validates_format_of :expiry, :with => /\A\d{6}$|^\d{8}$|\A\z/,
99
+ :message => "should be YYYYMM or YYYYMMDD"
100
+
101
+ validates_format_of :primary_exchange, :without => /SMART/,
102
+ :message => "should not be SMART"
103
+
104
+ validates_format_of :sec_id_type, :with => /ISIN|SEDOL|CUSIP|RIC|\A\z/,
105
+ :message => "should be valid security identifier"
106
+
107
+ validates_numericality_of :multiplier, :strike, :allow_nil => true
108
+
109
+ def default_attributes # :nodoc:
110
+ super.merge :con_id => 0,
111
+ :strike => "",
112
+ :right => :none, # Not an option
113
+ # :exchange => 'SMART',
114
+ :include_expired => false
115
+ end
116
+
117
+
118
+ # This returns an Array of data from the given contract and is used to represent
119
+ # contracts in outgoing messages.
120
+ #
121
+ # Different messages serialize contracts differently. Go figure.
122
+ #
123
+ # Note that it does NOT include the combo legs.
124
+ # serialize :option, :con_id, :include_expired, :sec_id
125
+ #
126
+ # 18/1/18: serialise always includes con_id
127
+
128
+ def serialize *fields # :nodoc:
129
+ print_default = ->(field, default="") { field.blank? ? default : field }
130
+ [(con_id.present? && !con_id.is_a?(Symbol) && con_id.to_i > 0 ? con_id : ""),
131
+ print_default[symbol],
132
+ print_default[self[:sec_type]],
133
+ ( fields.include?(:option) ?
134
+ [ print_default[expiry],
135
+ ## a Zero-Strike-Option has to be defined with «strike: -1 »
136
+ strike.present? && ( strike.is_a?(Numeric) && !strike.zero? && strike > 0 ) ? strike : strike<0 ? 0 : "",
137
+ print_default[self[:right]],
138
+ print_default[multiplier]] : nil ),
139
+ print_default[exchange],
140
+ ( fields.include?(:primary_exchange) ? print_default[primary_exchange] : nil ) ,
141
+ print_default[currency],
142
+ print_default[local_symbol],
143
+ ( fields.include?(:trading_class) ? print_default[trading_class] : nil ),
144
+ ( fields.include?(:include_expired) ? print_default[include_expired,0] : nil ),
145
+ ( fields.include?(:sec_id_type) ? [print_default[sec_id_type], print_default[sec_id]] : nil )
146
+ ].flatten.compact
147
+ end
148
+
149
+ # serialize contract
150
+ # con_id. sec_type, expiry, strike, right, multiplier exchange, primary_exchange, currency, local_symbol, include_expired
151
+ # other fields on demand
152
+ def serialize_long *fields # :nodoc:
153
+ serialize :option, :include_expired, :primary_exchange, :trading_class, *fields
154
+ end
155
+
156
+ # serialize contract
157
+ # con_id. sec_type, expiry, strike, right, multiplier, exchange, primary_exchange, currency, local_symbol
158
+ # other fields on demand
159
+ # acutal used by place_order, request_marketdata, request_market_depth, exercise_options
160
+ def serialize_short *fields # :nodoc:
161
+ serialize :option, :trading_class, :primary_exchange, *fields
162
+ end
163
+
164
+ # same as :serialize_short, omitting primary_exchange
165
+ # used by RequestMarketDepth
166
+ def serialize_supershort *fields # :nodoc:
167
+ serialize :option, :trading_class, *fields
168
+ end
169
+
170
+ # Serialize under_comp parameters: EClientSocket.java, line 471
171
+ def serialize_under_comp *args # :nodoc:
172
+ under_comp ? [true] + under_comp.serialize : [false]
173
+ end
174
+
175
+ # Defined in Contract, not BAG subclass to keep code DRY
176
+ def serialize_legs *fields # :nodoc:
177
+ case
178
+ when !bag?
179
+ []
180
+ when combo_legs.empty?
181
+ [0]
182
+ else
183
+ [combo_legs.size, combo_legs.map{|x| x.serialize :extended} ]
184
+ end
185
+ end
186
+
187
+
188
+
189
+ # This produces a string uniquely identifying this contract, in the format used
190
+ # for command line arguments in the IB-Ruby examples. The format is:
191
+ #
192
+ # symbol:sec_type:expiry:strike:right:multiplier:exchange:primary_exchange:currency:local_symbol
193
+ #
194
+ # Fields not needed for a particular security should be left blank
195
+ # (e.g. strike and right are only relevant for options.)
196
+ #
197
+ # For example, to query the British pound futures contract trading on Globex
198
+ # expiring in September, 2008, the string is:
199
+ #
200
+ # GBP:FUT:200809:::62500:GLOBEX::USD:
201
+ def serialize_ib_ruby
202
+ serialize_long.join(":")
203
+ end
204
+
205
+ # extracts essential attributes of the contract,
206
+ # and returns a new contract. Used for comparism of equality of contracts
207
+ #
208
+ # the link to contract-details is __not__ maintained.
209
+ def essential
210
+
211
+ the_attributes = [ :sec_type, :symbol , :con_id, :exchange, :right,
212
+ :currency, :expiry, :strike, :local_symbol, :last_trading_day,
213
+ :multiplier, :primary_exchange, :trading_class, :description ]
214
+ new_contract= self.class.new( invariant_attributes.select{|k,_| the_attributes.include? k }
215
+ .transform_values{|v| v.is_a?(Numeric)? v : v.to_s.upcase } )
216
+ new_contract[:description] = if @description.present?
217
+ @description
218
+ elsif contract_detail.present?
219
+ contract_detail.long_name
220
+ else
221
+ ""
222
+ end
223
+ new_contract # return contract
224
+ end
225
+
226
+
227
+ # creates a new Contract substituting attributes by the provided key-value pairs.
228
+ #
229
+ # for convenience
230
+ # con_id, local_symbol and last_trading_day are resetted,
231
+ # the link to contract-details is savaged
232
+ #
233
+ # Example
234
+ # ge = Stock.new( symbol: :ge).verify.first
235
+ # f = ge.merge symbol: :f
236
+ #
237
+ # c = Contract.new( con_id: 428520002, exchange: 'Globex')
238
+ #puts c.verify.as_table
239
+ #┌────────┬────────┬───────────┬──────────┬──────────┬────────────┬───────────────┬───────┬────────┬──────────┐
240
+ #│ │ symbol │ con_id │ exchange │ expiry │ multiplier │ trading-class │ right │ strike │ currency │
241
+ #╞════════╪════════╪═══════════╪══════════╪══════════╪════════════╪═══════════════╪═══════╪════════╪══════════╡
242
+ #│ Future │ NQ │ 428520002 │ GLOBEX │ 20210917 │ 20 │ NQ │ │ │ USD │
243
+ #└────────┴────────┴───────────┴──────────┴──────────┴────────────┴───────────────┴───────┴────────┴──────────┘
244
+ # d= c.merge symbol: :es, trading_class: '', multiplier: 50
245
+ # puts d.verify.as_table
246
+ #┌────────┬────────┬───────────┬──────────┬──────────┬────────────┬───────────────┬───────┬────────┬──────────┐
247
+ #│ │ symbol │ con_id │ exchange │ expiry │ multiplier │ trading-class │ right │ strike │ currency │
248
+ #╞════════╪════════╪═══════════╪══════════╪══════════╪════════════╪═══════════════╪═══════╪════════╪══════════╡
249
+ #│ Future │ ES │ 428520022 │ GLOBEX │ 20210917 │ 50 │ ES │ │ │ USD │
250
+ #│ Future │ ES │ 446091461 │ GLOBEX │ 20211217 │ 50 │ ES │ │ │ USD │
251
+ #│ Future │ ES │ 461318816 │ GLOBEX │ 20220318 │ 50 │ ES │ │ │ USD │
252
+ #│ Future │ ES │ 477836957 │ GLOBEX │ 20220617 │ 50 │ ES │ │ │ USD │
253
+ #│ Future │ ES │ 495512551 │ GLOBEX │ 20221216 │ 50 │ ES │ │ │ USD │
254
+ #│ Future │ ES │ 495512552 │ GLOBEX │ 20231215 │ 50 │ ES │ │ │ USD │
255
+ #│ Future │ ES │ 495512557 │ GLOBEX │ 20241220 │ 50 │ ES │ │ │ USD │
256
+ #│ Future │ ES │ 495512563 │ GLOBEX │ 20251219 │ 50 │ ES │ │ │ USD │
257
+ #│ Future │ ES │ 495512566 │ GLOBEX │ 20220916 │ 50 │ ES │ │ │ USD │
258
+ #│ Future │ ES │ 495512569 │ GLOBEX │ 20230616 │ 50 │ ES │ │ │ USD │
259
+ #│ Future │ ES │ 495512572 │ GLOBEX │ 20230317 │ 50 │ ES │ │ │ USD │
260
+ #│ Future │ ES │ 497222760 │ GLOBEX │ 20230915 │ 50 │ ES │ │ │ USD │
261
+ #└────────┴────────┴───────────┴──────────┴──────────┴────────────┴───────────────┴───────┴────────┴──────────┘
262
+
263
+ def merge **new_attributes
264
+
265
+ resetted_attributes = [:con_id, :local_symbol, :contract_detail]
266
+ ## last_trading_day / expiry needs special treatment
267
+ resetted_attributes << :last_trading_day if new_attributes.keys.include? :expiry
268
+ self.class.new attributes.reject{|k,_| resetted_attributes.include? k}.merge(new_attributes)
269
+ end
270
+
271
+ # Contract comparison
272
+
273
+ def == other # :nodoc:
274
+ # a = ->(e){ e.essential.invariant_attributes.select{|y,_| ![:description, :include_expired, :con_id, :trading_class, :primary_exchange].include? y} }
275
+ return true if self.con_id == other.con_id
276
+ # a.call(self) == a.call(other)
277
+ common_keys = self.invariant_attributes.keys & other.invariant_attributes.keys
278
+ common_keys.all? do |key|
279
+ value1 = attributes[key]
280
+ value2 = other.attributes[key]
281
+ next true if value1 == value2
282
+ value1.to_i.zero? || value2.to_i.zero? rescue true
283
+ end
284
+ end
285
+
286
+ # def to_s
287
+ # "<Contract: " + instance_variables.map do |key|
288
+ # value = send(key[1..-1])
289
+ # " #{key}=#{value} (#{value.class}) " unless value.blank?
290
+ # end.compact.join(',') + " >"
291
+ # end
292
+
293
+ def to_human
294
+ "<Contract: " +
295
+ [symbol,
296
+ sec_type,
297
+ (expiry == '' ? nil : expiry),
298
+ (right == :none ? nil : right),
299
+ (strike == 0 ? nil : strike),
300
+ exchange,
301
+ currency
302
+ ].compact.join(" ") + ">"
303
+ end
304
+
305
+ alias to_s to_human
306
+
307
+ # Testing for type of contract:
308
+ # depreciated : use is_a?(IB::Stock, IB::Bond, IB::Bag etc) instead
309
+ def bag? # :nodoc:
310
+ self[:sec_type] == 'BAG'
311
+ end
312
+
313
+ def bond? # :nodoc:
314
+
315
+ self[:sec_type] == 'BOND'
316
+ end
317
+
318
+ def stock? # :nodoc:
319
+
320
+ self[:sec_type] == 'STK'
321
+ end
322
+
323
+ def option? # :nodoc:
324
+
325
+ self[:sec_type] == 'OPT'
326
+ end
327
+
328
+ def index? # :nodoc:
329
+
330
+ self[:sec_type] == 'IND'
331
+ end
332
+
333
+ def crypto? # :nodoc:
334
+
335
+ self[:sec_type] == 'CRYPTO'
336
+ end
337
+
338
+
339
+ def time_zone
340
+ if contract_detail.present?
341
+ contract_detail.time_zone
342
+ else
343
+ case currency
344
+ when "EUR"
345
+ "MET"
346
+ when "AUD"
347
+ "Australia/NSW"
348
+ when "USD"
349
+ "US/Eastern"
350
+ else
351
+ "UTC"
352
+ end
353
+ end
354
+ end
355
+ =begin
356
+ From the release notes of TWS 9.50
357
+
358
+ Within TWS and Mosaic, we use the last trading day and not the actual expiration date for futures, options and futures options contracts. To be more accurate, all fields and selectors throughout TWS that were labeled Expiry or Expiration have been changed to Last Trading Day. Note that the last trading day and the expiration date may be the same or different dates.
359
+
360
+ In many places, such as the OptionTrader, Probability Lab and other options/futures tools, this is a simple case of changing the name of a field to Last Trading Day. In other cases the change is wider-reaching. For example, basket files that include derivatives were previously saved using the Expiry header. When you try to import these legacy .csv files, you will now receive a message requiring that you change this column title to LastTradingDayorContractMonth before the import will be accepted. New basket files that include derivatives will use this correct header. Additionally, this new field serves two functions. If you use the format YYYYMMDD, we understand you are identifying the last trading day for a contract. If you use the format YYYYMM, we understand you are identifying the contract month.
361
+
362
+ In places where these terms are used to indicate a concept, we have left them as Expiry or Expiration. For example in the Option Chain settings where we allow you to "Load the nearest N expiries" we have left the word expiries. Additionally, the Contract Description window will show both the Last Trading Date and the Expiration Date. Also in cases where it's appropriate, we have replaced Expiry or Expiration with Contract Month.
363
+
364
+ =end
365
+
366
+
367
+ # IB-ruby uses expiry to query Contracts.
368
+ #
369
+ # The response from the TWS is stored in 'last_trading_day' (Contract) and 'real_expiration_data' (ContractDetails)
370
+ #
371
+ # However, after querying a contract, 'expiry' ist overwritten by 'last_trading_day'. The original 'expiry'
372
+ # is still available through 'attributes[:expiry]'
373
+
374
+ def expiry
375
+ if self.last_trading_day.present?
376
+ last_trading_day.gsub(/-/,'')
377
+ else
378
+ @attributes[:expiry]
379
+ end
380
+ end
381
+
382
+
383
+ # is read by Account#PlaceOrder to set requirements for contract-types, as NonGuaranteed for stock-spreads
384
+ def order_requirements
385
+ Hash.new
386
+ end
387
+
388
+
389
+ def table_header( &b )
390
+ if block_given?
391
+ [ yield(self) , 'symbol', 'con_id', 'exchange', 'expiry','multiplier', 'trading-class' , 'right', 'strike', 'currency' ]
392
+ else
393
+ [ '', 'symbol', 'con_id', 'exchange', 'expiry','multiplier', 'trading-class' , 'right', 'strike', 'currency' ]
394
+ end
395
+ end
396
+
397
+ def table_row
398
+ [ self.class.to_s.demodulize, symbol,
399
+ { value: con_id.zero? ? '' : con_id , alignment: :right},
400
+ { value: exchange, alignment: :center},
401
+ expiry,
402
+ { value: multiplier.nil? || multiplier.zero?? "" : multiplier, alignment: :center},
403
+ { value: trading_class.nil? ? "" : trading_class, alignment: :center},
404
+ { value: right.nil? || right == :none ? "": right, alignment: :center },
405
+ { value: strike.nil? || strike.zero? ? "": strike, alignment: :right},
406
+ { value: currency, alignment: :center} ]
407
+
408
+ end
409
+ end # class Contract
410
+ end # module IB
411
+
@@ -0,0 +1,118 @@
1
+ module IB
2
+
3
+ # Additional Contract properties (volatile, therefore extracted)
4
+ class ContractDetail < IB::Base
5
+ include BaseProperties
6
+
7
+ # All fields Strings, unless specified otherwise:
8
+ prop :market_name, # The market name for this contract.
9
+ :trading_class, # The trading class name for this contract.
10
+ :min_tick, # double: The minimum price tick.
11
+ :price_magnifier, # int: Allows execution and strike prices to be reported
12
+ # consistently with market data, historical data and the
13
+ # order price: Z on LIFFE is reported in index points, not GBP.
14
+
15
+ :order_types, # The list of valid order types for this contract.
16
+ :valid_exchanges, # The list of exchanges this contract is traded on.
17
+ :under_con_id, # int: The underlying contract ID.
18
+ :long_name, # Descriptive name of the asset.
19
+ :contract_month, # The contract month of the underlying futures contract.
20
+
21
+ :agg_group,
22
+ :under_symbol,
23
+ :under_sec_type,
24
+ :market_rule_ids,
25
+ :real_expiration_date,
26
+
27
+
28
+ # For Bonds only
29
+ :valid_next_option_date,
30
+ :valid_next_option_type,
31
+ :valid_next_option_partial,
32
+
33
+ # The industry classification of the underlying/product:
34
+ :industry, # Wide industry. For example, Financial.
35
+ :category, # Industry category. For example, InvestmentSvc.
36
+ :subcategory, # Subcategory. For example, Brokerage.
37
+ [:time_zone, :time_zone_id], # Time zone for the trading hours (e.g. EST)
38
+ :trading_hours, # The trading hours of the product. For example:
39
+ # 20090507:0700-1830,1830-2330;20090508:CLOSED.
40
+ :liquid_hours, # The liquid trading hours of the product. For example,
41
+ # 20090507:0930-1600;20090508:CLOSED.
42
+
43
+ # To support products in Australia which trade in non-currency units, the following
44
+ # attributes have been added to Execution and Contract Details objects:
45
+ :ev_rule, # evRule - String contains the Economic Value Rule name and optional argument,
46
+ # separated by a colon. Examle: aussieBond:YearsToExpiration=3.
47
+ # When the optional argument not present, the value will be followed by a colon.
48
+ [:ev_multiplier, :ev_multipler], # evMultiplier - double, tells you approximately
49
+ # how much the market value of a contract would change if the price were
50
+ # to change by 1. It cannot be used to get market value by multiplying
51
+ # the price by the approximate multiplier.
52
+
53
+ :sec_id_list, # Array with multiple Security ids
54
+ # MD Size Multiplier. Returns the size multiplier for values returned to tickSize from a market data request. Generally 100 for US stocks and 1 for other instruments.
55
+ :md_size_multiplier,
56
+ #
57
+ # BOND values:
58
+ :cusip, # The nine-character bond CUSIP or the 12-character SEDOL.
59
+ :ratings, # Credit rating of the issuer. Higher rating is less risky investment.
60
+ # Bond ratings are from Moody's and S&P respectively.
61
+ :desc_append, # Additional descriptive information about the bond.
62
+ :bond_type, # The type of bond, such as "CORP."
63
+ :coupon_type, # The type of bond coupon.
64
+ :coupon, # double: The interest rate used to calculate the amount you
65
+ # will receive in interest payments over the year. default 0
66
+ :maturity, # The date on which the issuer must repay bond face value
67
+ :issue_date, # The date the bond was issued.
68
+ :next_option_date, # only if bond has embedded options.
69
+ :next_option_type, # only if bond has embedded options.
70
+ :notes, # Additional notes, if populated for the bond in IB's database
71
+ :stock_type, # new Version 10.12 --> common
72
+ :min_size, # new Version 10.12
73
+ :size_increment, # new Version 10.12
74
+ :suggested_size_increment, # new Version 10.12
75
+ :callable => :bool, # Can be called by the issuer under certain conditions.
76
+ :puttable => :bool, # Can be sold back to the issuer under certain conditions
77
+ :convertible => :bool, # Can be converted to stock under certain conditions.
78
+ :next_option_partial => :bool # # only if bond has embedded options.
79
+
80
+ # Extra validations
81
+ validates_format_of :time_zone, :with => /\A\w{3}\z/, :message => 'should be XXX'
82
+
83
+ serialize :sec_id_list, Hash
84
+
85
+ belongs_to :contract
86
+ alias summary contract
87
+ alias summary= contract=
88
+
89
+ def default_attributes
90
+ super.merge :coupon => 0.0,
91
+ :under_con_id => 0,
92
+ :min_tick => 0,
93
+ :ev_multipler => 0,
94
+ :sec_id_list => Hash.new,
95
+ :callable => false,
96
+ :puttable => false,
97
+ :convertible => false,
98
+ :next_option_partial => false
99
+ end
100
+
101
+ def to_human
102
+ ret = "<ContractDetails #{long_name}, "
103
+ ret << "--> #{market_name}, " if market_name.present?
104
+ ret << "/C/ #{category}, /I/ #{industry} /SC/ #{subcategory}, " if category.present?
105
+ ret << "Underlying:#{under_symbol}[#{under_sec_type}](#{under_con_id}), " unless under_con_id.zero?
106
+ ret << "ev_multiplier:#{ev_multiplier}, " if ev_multiplier.present?
107
+ ret << "convertible:#{convertible}, " if convertible
108
+ ret << "coupon:#{coupon}, " if coupon.present? && coupon > 0
109
+ ret << "md_size_multiplier:#{md_size_multiplier}, min_tick:#{min_tick}, "
110
+ ret << "next_option_partial:#{next_option_partial}, " if next_option_partial.present?
111
+ ret << "price_magnifier:#{price_magnifier}, "
112
+ ret << "puttable:#{puttable}, " if puttable.present?
113
+ ret << "sec_id-list:#{sec_id_list}, " unless sec_id_list.empty?
114
+ ret <<"valid exchanges: #{ valid_exchanges}; order types: #{order_types} >"
115
+ end
116
+
117
+ end # class ContractDetail
118
+ end # module IB
@@ -0,0 +1,67 @@
1
+ module IB
2
+
3
+ # This is IB Order execution report.
4
+ class Execution < IB::Base
5
+ include BaseProperties
6
+
7
+ belongs_to :order
8
+
9
+ prop :local_id, # int: order id. TWS orders have a fixed order id of 0.
10
+ :client_id, # int: client id. TWS orders have a fixed client id of 0.
11
+ :perm_id, # int: TWS id used to identify orders over TWS sessions
12
+ :exec_id, # String: Unique order execution id over TWS sessions.
13
+ :time, # # TODO: convert into Time object?
14
+ # String: The order execution time.
15
+ :exchange, # String: Exchange that executed the order.
16
+ :order_ref, # String: Same order_ref as in corresponding Order
17
+ :price, # double: The order execution price.
18
+ :average_price, # double: Used in regular trades, combo trades and legs of the combo.
19
+ :ev_rule, # String: Australian products only
20
+ :ev_multiplier, # double: Australian products onlyA
21
+ :model_code,
22
+ :last_liquidity,
23
+
24
+ [:quantity, :shares], # int: The number of shares filled.
25
+ :cumulative_quantity, # int: Used in regular trades, combo trades and legs of combo
26
+ :liquidation => :bool, # This position is liquidated last should the need arise.
27
+ [:account_name, :account_number] => :s, # The customer account number.
28
+ [:side, :action] => PROPS[:side] # Was the transaction a buy or a sale: BOT|SLD
29
+
30
+ # Extra validations
31
+ validates_numericality_of :quantity, :cumulative_quantity, :price, :average_price
32
+ validates_numericality_of :local_id, :client_id, :perm_id, :only_integer => true
33
+
34
+ def default_attributes
35
+ super.merge :local_id => 0,
36
+ :client_id => 0,
37
+ :quantity => 0,
38
+ :price => 0,
39
+ :perm_id => 0,
40
+ :liquidation => false
41
+ end
42
+
43
+ # Comparison
44
+ def == other
45
+ super(other) ||
46
+ other.is_a?(self.class) &&
47
+ perm_id == other.perm_id &&
48
+ local_id == other.local_id && # ((p __LINE__)||true) &&
49
+ client_id == other.client_id &&
50
+ exec_id == other.exec_id &&
51
+ time == other.time &&
52
+ exchange == other.exchange &&
53
+ order_ref == other.order_ref &&
54
+ side == other.side
55
+ # TODO: || compare all attributes!
56
+ end
57
+
58
+ def to_human
59
+ "<Execution: #{time} #{side} #{quantity} at #{price} on #{exchange}, " +
60
+ "cumulative #{cumulative_quantity} at #{average_price}, " +
61
+ "ids #{local_id}/#{perm_id}/#{exec_id}>"
62
+ end
63
+
64
+ alias to_s to_human
65
+
66
+ end # Execution
67
+ end # module IB
@@ -0,0 +1,12 @@
1
+ module IB
2
+ class Forex < IB::Contract
3
+ validates_format_of :sec_type, :with => /\Aforex\z/,
4
+ :message => "should be a Currency-Pair"
5
+ def default_attributes
6
+ # Base-currency: USD
7
+ super.merge :sec_type => :forex, currency:'USD', exchange:'IDEALPRO'
8
+ end
9
+
10
+ end
11
+ end
12
+
@@ -0,0 +1,64 @@
1
+ module IB
2
+ class Future < Contract
3
+ validates_format_of :sec_type, :with => /\Afuture\z/,
4
+ :message => "should be a Future"
5
+ def default_attributes
6
+ super.merge :sec_type => :future, currency:'USD'
7
+ end
8
+ def to_human
9
+ "<Future: " + [symbol, expiry, currency].join(" ") + ">"
10
+ end
11
+
12
+
13
+
14
+ # get the next (regular) expiry of the contract
15
+ #
16
+ # fetches for real contracts if verify is available
17
+ #
18
+ def next_expiry d = Date.today
19
+ exp = self.class.next_expiry d
20
+ if IB::Connection.current.plugins.include? 'verify'
21
+ self.expiry = exp[0..-3]
22
+ verify.sort_by{| x | x.last_trading_day}
23
+ .find_all{| y | y.expiry <= exp }
24
+ .first
25
+ else
26
+ exp
27
+ end
28
+ end
29
+ class << self
30
+
31
+
32
+ # This returns the next
33
+ # quarterly expiration month after the current month.
34
+ #
35
+ # IB::Future.next_expiry returns the next quaterly expiration
36
+ # IB::Option.next_expiry returns the next monthly expiration
37
+ #
38
+ #
39
+ #
40
+ def next_expiry d=Date.today, type: :quarter
41
+ next_quarter_day = ->(year, month) do
42
+ base_date = Date.new(year, month)
43
+ base_wday = base_date.wday
44
+ base_date + ( 5 > base_wday ? 5 - base_wday : 7 - base_wday + 5 ) + 14
45
+ end
46
+ next_quarter_day[ next_quarter_year(d), next_quarter_month(d) ].strftime("%Y%m%d")
47
+ # /retired/ "#{ next_quarter_year(time) }#{ sprintf("%02d", next_quarter_month(time)) }"
48
+ end
49
+
50
+ private
51
+ # Find the next front month of quarterly futures.
52
+ # N.B. This will not work as expected during the front month before expiration, as
53
+ # it will point to the next quarter even though the current month is still valid!
54
+ def next_quarter_month d
55
+ [3, 6, 9, 12].find { |month| month > d.month } || 3 # for December, next March
56
+ end
57
+
58
+ def next_quarter_year d
59
+ next_quarter_month(d) < d.month ? d.year + 1 : d.year
60
+ end
61
+ end
62
+ end
63
+ end
64
+