ib-api 972.0 → 972.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +36 -37
  3. data/README.md +48 -3
  4. data/VERSION +1 -1
  5. data/api.gemspec +1 -1
  6. data/bin/console +4 -8
  7. data/changelog.md +12 -0
  8. data/lib/ib/base_properties.rb +3 -4
  9. data/lib/ib/connection.rb +18 -16
  10. data/lib/ib/logger.rb +1 -1
  11. data/lib/ib/messages/incoming.rb +1 -1
  12. data/lib/ib/messages/incoming/abstract_message.rb +7 -7
  13. data/lib/ib/messages/incoming/account_value.rb +5 -1
  14. data/lib/ib/messages/incoming/contract_data.rb +0 -2
  15. data/lib/ib/messages/incoming/historical_data.rb +25 -5
  16. data/lib/ib/messages/incoming/ticks.rb +21 -60
  17. data/lib/ib/messages/outgoing.rb +4 -2
  18. data/lib/ib/messages/outgoing/abstract_message.rb +3 -3
  19. data/lib/ib/messages/outgoing/bar_requests.rb +4 -5
  20. data/lib/ib/messages/outgoing/request_marketdata.rb +10 -7
  21. data/lib/ib/models.rb +1 -1
  22. data/lib/ib/support.rb +32 -15
  23. data/lib/logging.rb +45 -0
  24. data/lib/models/ib/account.rb +0 -13
  25. data/lib/models/ib/bag.rb +7 -1
  26. data/lib/models/ib/bar.rb +1 -1
  27. data/lib/models/ib/combo_leg.rb +22 -0
  28. data/lib/models/ib/contract.rb +44 -32
  29. data/lib/models/ib/contract_detail.rb +13 -7
  30. data/lib/models/ib/index.rb +1 -1
  31. data/lib/models/ib/option.rb +8 -2
  32. data/lib/models/ib/option_detail.rb +19 -3
  33. data/lib/models/ib/order.rb +5 -0
  34. data/lib/models/ib/spread.rb +168 -0
  35. data/lib/models/ib/stock.rb +9 -3
  36. data/lib/models/ib/underlying.rb +4 -1
  37. data/lib/requires.rb +10 -3
  38. metadata +9 -20
  39. data/example/README.md +0 -76
  40. data/example/account_info +0 -54
  41. data/example/account_positions +0 -30
  42. data/example/account_summary +0 -88
  43. data/example/cancel_orders +0 -74
  44. data/example/fa_accounts +0 -25
  45. data/example/fundamental_data +0 -40
  46. data/example/historic_data_cli +0 -186
  47. data/example/list_orders +0 -45
  48. data/example/portfolio_csv +0 -81
  49. data/example/scanner_data +0 -62
  50. data/example/template +0 -19
  51. data/example/tick_data +0 -28
@@ -83,6 +83,28 @@ module IB
83
83
  ].flatten
84
84
  end
85
85
 
86
+
87
+ # fields are generated by serialize(:extended)
88
+ # i.e.
89
+ # z= Strangle.build from: Symbols::Index.stoxx, p: 3700, c: 4000, expiry: 202106
90
+ # zc= z.combo_legs.serialize :extended
91
+ # => [[321584786, 1, "BUY", "DTB", 0, 0, "", -1], [321584637, 1, "BUY", "DTB", 0, 0, "", -1]]
92
+ # nz = zc.map{|o| ComboLeg.build *o }
93
+ # zc.map{|o| ComboLeg.build o } => # is equivalent
94
+ # => [#<IB::ComboLeg:0x0000000001c36bc0 @attributes={:con_id=>321584786, :ratio=>1, :side=>"B", :exchange=>"DTB", ...
95
+ # nz.first == z.combo_legs.first => true
96
+ #
97
+ def self.build *fields
98
+ self.new Hash[[:con_id,
99
+ :ratio,
100
+ :side, # reverse to_sup?
101
+ :exchange,
102
+ :open_close,
103
+ :short_sale_slot,
104
+ :designated_location,
105
+ :exempt_code].zip fields]
106
+ end
107
+
86
108
  def to_human
87
109
  "<ComboLeg: #{side} #{ratio} con_id #{con_id} at #{exchange}>"
88
110
  end
@@ -4,7 +4,14 @@ require 'models/ib/underlying'
4
4
 
5
5
 
6
6
  module IB
7
- class Contract < IB::Model
7
+
8
+ if defined?(Contract)
9
+ #Connection.current.logger.warn "Contract already a #{defined?(Contract)}"
10
+
11
+ # puts Contract.ancestors
12
+ # IB.send(:remove_const, 'Contract')
13
+ end
14
+ class Contract < IB::Model
8
15
  include BaseProperties
9
16
 
10
17
  # Fields are Strings unless noted otherwise
@@ -113,7 +120,7 @@ module IB
113
120
 
114
121
  def default_attributes # :nodoc:
115
122
  super.merge :con_id => 0,
116
- :strike => 0.0,
123
+ :strike => "",
117
124
  :right => :none, # Not an option
118
125
  # :exchange => 'SMART',
119
126
  :include_expired => false
@@ -130,13 +137,13 @@ module IB
130
137
 
131
138
  def serialize *fields # :nodoc:
132
139
  print_default = ->(field, default="") { field.blank? ? default : field }
133
- print_not_zero = ->(field, default="") { field.to_i.zero? ? default : field }
134
140
  [(con_id.present? && !con_id.is_a?(Symbol) && con_id.to_i > 0 ? con_id : ""),
135
141
  print_default[symbol],
136
142
  print_default[self[:sec_type]],
137
143
  ( fields.include?(:option) ?
138
144
  [ print_default[expiry],
139
- print_not_zero[strike],
145
+ ## a Zero-Strike-Option has to be defined with «strike: -1 »
146
+ strike.present? && ( strike.is_a?(Numeric) && !strike.zero? && strike > 0 ) ? strike : strike<0 ? 0 : "",
140
147
  print_default[self[:right]],
141
148
  print_default[multiplier]] : nil ),
142
149
  print_default[exchange],
@@ -211,17 +218,22 @@ module IB
211
218
  # the link to contract-details is __not__ maintained.
212
219
  def essential
213
220
 
214
- self_attributes = [ :right, :sec_type]
215
- the_attributes = [ :symbol , :con_id, :exchange,
221
+ the_attributes = [ :sec_type, :symbol , :con_id, :exchange, :right,
216
222
  :currency, :expiry, :strike, :local_symbol, :last_trading_day,
217
- :multiplier, :primary_exchange, :trading_class ]
218
- the_hash= the_attributes.map{|x| y= attributes[x]; [x,y] if y.present? }.compact.to_h
219
- the_hash[:description] = @description if @description.present?
220
- self.class.new the_hash.merge( self_attributes.map{|x| y = self.send(x); [x,y] unless y == :none}.compact.to_h )
223
+ :multiplier, :primary_exchange, :trading_class, :description ]
224
+ new_contract= self.class.new invariant_attributes.select{|k,_| the_attributes.include? k }.compact
225
+ new_contract[:description] = if @description.present?
226
+ @description
227
+ elsif contract_detail.present?
228
+ contract_detail.long_name
229
+ else
230
+ ""
231
+ end
232
+ new_contract # return contract
221
233
  end
222
234
 
223
235
 
224
- # creates a new Contract substituting attributes by the provied key-value pairs.
236
+ # creates a new Contract substituting attributes by the provided key-value pairs.
225
237
  #
226
238
  # con_id is resetted
227
239
  def merge **new_attributes
@@ -327,6 +339,10 @@ module IB
327
339
  self[:sec_type] == 'IND'
328
340
  end
329
341
 
342
+
343
+ def verify # :nodoc:
344
+ error "verify must be overloaded. Please require at least `ib/verify` from the `ib-extenstions` gem "
345
+ end
330
346
  =begin
331
347
  From the release notes of TWS 9.50
332
348
 
@@ -364,20 +380,33 @@ In places where these terms are used to indicate a concept, we have left them as
364
380
 
365
381
 
366
382
  ### Now let's deal with Contract subclasses
383
+ begin
367
384
 
368
- require 'models/ib/option'
385
+ require_relative 'option'
369
386
  require 'models/ib/bag'
370
387
  require 'models/ib/forex'
371
388
  require 'models/ib/future'
372
389
  require 'models/ib/stock'
373
390
  require 'models/ib/index'
391
+ ### walkaraound to enable spreads with orientdb
392
+ if IB::const_defined? :Spread
393
+ IB::send(:remove_const, :Spread)
394
+ #puts "Spread already defined"
395
+ #puts "erasing"
396
+ end
397
+ require 'models/ib/spread.rb'
398
+ end
399
+
400
+
374
401
 
375
402
  class Contract
376
403
  # Contract subclasses representing specialized security types.
404
+ using IBSupport
377
405
 
378
406
  Subclasses = Hash.new(Contract)
379
407
  Subclasses[:bag] = IB::Bag
380
408
  Subclasses[:option] = IB::Option
409
+ Subclasses[:futures_option] = IB::FutureOption
381
410
  Subclasses[:future] = IB::Future
382
411
  Subclasses[:stock] = IB::Stock
383
412
  Subclasses[:forex] = IB::Forex
@@ -385,31 +414,14 @@ In places where these terms are used to indicate a concept, we have left them as
385
414
 
386
415
 
387
416
  # This builds an appropriate Contract subclass based on its type
388
- #
389
- # the method is also used to copy Contract.values to new instances
417
+ #
418
+ # the method is also used to copy Contract.values to new instances
390
419
  def self.build opts = {}
391
420
  subclass =( VALUES[:sec_type][opts[:sec_type]] || opts['sec_type'] || opts[:sec_type]).to_sym
392
421
  Contract::Subclasses[subclass].new opts
393
422
  end
394
423
 
395
- # This returns a Contract initialized from the serialize_ib_ruby format string.
396
- def self.from_ib_ruby
397
- keys = [:con_id, :symbol, :sec_type, :expiry, :strike, :right, :multiplier,
398
- :exchange, :primary_exchange, :currency, :local_symbol]
399
- props = Hash[keys.zip(string.split(":"))]
400
- props.delete_if { |k, v| v.nil? || v.empty? }
401
- Contract.build props
402
- end
424
+
403
425
  end # class Contract
404
426
  end # module IB
405
427
 
406
- class String
407
- def to_contract
408
- keys = [:con_id, :symbol, :sec_type, :expiry, :strike, :right, :multiplier,
409
- :exchange, :primary_exchange, :currency, :local_symbol]
410
- props = Hash[keys.zip(split(":"))]
411
- props.delete_if { |k, v| v.nil? || v.empty? }
412
- IB::Contract.build props
413
-
414
- end
415
- end
@@ -95,13 +95,19 @@ module IB
95
95
  end
96
96
 
97
97
  def to_human
98
- ret = "<ContractDetails #{long_name}, market-name:#{market_name}, "
99
- ret << "category:#{category}, industry:#{industry} / #{subcategory}, " if category.present?
100
- ret << "underlying: con_id:#{under_con_id} , sec_type:#{under_sec_type}, symbol:#{under_symbol} " unless under_con_id.zero?
101
- ret << "ev_multiplier:#{ev_multiplier}, convertible:#{convertible}, cupon:#{coupon}, "
102
- ret << "md_size_multiplier:#{md_size_multiplier}, min_tick:#{min_tick}, next_option_partial:#{next_option_partial} "
103
- ret <<"price_magnifier:#{price_magnifier}, puttable:#{puttable}, sec_id-list:#{sec_id_list}, "
104
- ret <<"valid exchanges: #{ valid_exchanges}, order types: #{order_types} >"
98
+ ret = "<ContractDetails #{long_name}, "
99
+ ret << "--> #{market_name}, " if market_name.present?
100
+ ret << "/C/ #{category}, /I/ #{industry} /SC/ #{subcategory}, " if category.present?
101
+ ret << "Underlying:#{under_symbol}[#{under_sec_type}](#{under_con_id}), " unless under_con_id.zero?
102
+ ret << "ev_multiplier:#{ev_multiplier}, " if ev_multiplier.present?
103
+ ret << "convertible:#{convertible}, " if convertible
104
+ ret << "coupon:#{coupon}, " if coupon.present? && coupon > 0
105
+ ret << "md_size_multiplier:#{md_size_multiplier}, min_tick:#{min_tick}, "
106
+ ret << "next_option_partial:#{next_option_partial}, " if next_option_partial.present?
107
+ ret << "price_magnifier:#{price_magnifier}, "
108
+ ret << "puttable:#{puttable}, " if puttable.present?
109
+ ret << "sec_id-list:#{sec_id_list}, " unless sec_id_list.empty?
110
+ ret <<"valid exchanges: #{ valid_exchanges}; order types: #{order_types} >"
105
111
  end
106
112
 
107
113
  end # class ContractDetail
@@ -7,7 +7,7 @@ module IB
7
7
  super.merge :sec_type => :ind
8
8
  end
9
9
  def to_human
10
- "<Index: " + [symbol, currency].join(" ") + ">"
10
+ "<Index: " + [symbol, currency].join(" ") + " (#{description}) >"
11
11
  end
12
12
 
13
13
  end
@@ -1,5 +1,5 @@
1
- require 'models/ib/contract'
2
- require 'models/ib/option_detail'
1
+ #require_relative 'contract'
2
+ require_relative 'option_detail'
3
3
 
4
4
  module IB
5
5
  class Option < Contract
@@ -75,4 +75,10 @@ module IB
75
75
  end
76
76
 
77
77
  end # class Option
78
+
79
+ class FutureOption < Option
80
+ def default_attributes
81
+ super.merge :sec_type => :futures_option
82
+ end
83
+ end
78
84
  end # module IB
@@ -17,7 +17,8 @@ module IB
17
17
  :next_strike,
18
18
  :prev_expiry,
19
19
  :next_expiry,
20
- :option_price
20
+ :option_price,
21
+ :updated_at
21
22
  belongs_to :option
22
23
 
23
24
  # returns true if all datafields are filled with reasonal data
@@ -32,19 +33,34 @@ module IB
32
33
 
33
34
  def greeks?
34
35
  fields= [ :delta, :gamma, :vega, :theta,
35
- :implied_volatility, :pv_dividend]
36
+ :implied_volatility]
36
37
 
37
38
  !fields.detect{|y| self.send(y).nil?}
38
39
 
39
40
  end
40
41
 
42
+ def prices?
43
+ fields = [:implied_volatility, :under_price, :option_price]
44
+ !fields.detect{|y| self.send(y).nil?}
45
+ end
46
+
47
+ def iv
48
+ implied_volatility
49
+ end
50
+
51
+ def spread
52
+ bid_price - ask_price
53
+ end
54
+
41
55
  def to_human
42
56
  outstr= ->( item ) { if item.nil? then "--" else sprintf("%g" , item) end }
43
57
  att = " optionPrice: #{ outstr[ option_price ]}, UnderlyingPrice: #{ outstr[ under_price] } impl.Vola: #{ outstr[ implied_volatility ]} ; dividend: #{ outstr[ pv_dividend ]}; "
44
58
  greeks = "Greeks:: delta: #{ outstr[ delta ] }; gamma: #{ outstr[ gamma ]}, vega: #{ outstr[ vega ] }; theta: #{ outstr[ theta ]}"
45
59
  prices= " close: #{ outstr[ close_price ]}; bid: #{ outstr[ bid_price ]}; ask: #{ outstr[ ask_price ]} "
46
60
  if complete?
47
- "< "+ prices + "\n" + att + "\n" + greeks + " >"
61
+ "< "+ prices + "\n" + att + "\n" + greeks + " >"
62
+ elsif prices?
63
+ "< " + att + greeks + " >"
48
64
  else
49
65
  "< " + greeks + " >"
50
66
  end
@@ -514,6 +514,11 @@ Format of serialisation
514
514
  (account ? "/#{account}" : '') +
515
515
  (commission ? " fee #{commission}" : '') + ">"
516
516
  end
517
+ def serialize_rabbit
518
+ { 'Contract' => contract.present? ? contract.serialize( :option, :trading_class ): '' ,
519
+ 'Order' => self,
520
+ 'OrderState' => order_state}
521
+ end
517
522
 
518
523
  end # class Order
519
524
  end # module IB
@@ -0,0 +1,168 @@
1
+ # require 'ib/verify'
2
+ module IB
3
+ if defined?(Spread)
4
+ puts "Bag already a #{defined?(Spread)}"
5
+
6
+ # puts Spread.ancestors
7
+ IB.send(:remove_const, 'Spread')
8
+ end
9
+ class Spread < Bag
10
+ has_many :legs
11
+
12
+ using IBSupport
13
+
14
+ =begin
15
+ Parameters: front: YYYMM(DD)
16
+ back: {n}w, {n}d or YYYYMM(DD)
17
+
18
+ Adds (or substracts) relative (back) measures to the front month, just passes absolute YYYYMM(DD) value
19
+
20
+ front: 201809 back: 2m (-1m) --> 201811 (201808)
21
+ front: 20180908 back: 1w (-1w) --> 20180918 (20180902)
22
+ =end
23
+
24
+ def transform_distance front, back
25
+ # Check Format of back: 201809 --> > 200.000
26
+ # 20180989 ---> 20.000.000
27
+ start_date = front.to_i < 20000000 ? Date.strptime(front.to_s,"%Y%m") : Date.strptime(front.to_s,"%Y%m%d")
28
+ nb = if back.to_i > 200000
29
+ back.to_i
30
+ elsif back[-1] == "w" && front.to_i > 20000000
31
+ start_date + (back.to_i * 7)
32
+ elsif back[-1] == "m" && front.to_i > 200000
33
+ start_date >> back.to_i
34
+ else
35
+ error "Wrong date #{back} required format YYYMM, YYYYMMDD ord {n}w or {n}m"
36
+ end
37
+ if nb.is_a?(Date)
38
+ if back[-1]=='w'
39
+ nb.strftime("%Y%m%d")
40
+ else
41
+ nb.strftime("%Y%m")
42
+ end
43
+ else
44
+ nb
45
+ end
46
+ end # def
47
+
48
+ def to_human
49
+ self.description
50
+ end
51
+
52
+ def calculate_spread_value( array_of_portfolio_values )
53
+ array_of_portfolio_values.map{|x| x.send yield }.sum if block_given?
54
+ end
55
+
56
+ def fake_portfolio_position( array_of_portfolio_values )
57
+ calculate_spread_value= ->( a_o_p_v, attribute ) do
58
+ a_o_p_v.map{|x| x.send attribute }.sum
59
+ end
60
+ ar=array_of_portfolio_values
61
+ IB::PortfolioValue.new contract: self,
62
+ average_cost: calculate_spread_value[ar, :average_cost],
63
+ market_price: calculate_spread_value[ar, :market_price],
64
+ market_value: calculate_spread_value[ar, :market_value],
65
+ unrealized_pnl: calculate_spread_value[ar, :unrealized_pnl],
66
+ realized_pnl: calculate_spread_value[ar, :realized_pnl],
67
+ position: 0
68
+
69
+ end
70
+
71
+
72
+ # adds a leg to any spread
73
+ #
74
+ # Parameter:
75
+ # contract: Will be verified. Contract.essential is added to legs-array
76
+ # action: :buy or :sell
77
+ # weight:
78
+ # ratio:
79
+ #
80
+ # Default: action: :buy, weight: 1
81
+
82
+ def add_leg contract, **leg_params
83
+ evaluated_contracts = []
84
+ nc = contract.verify.first.essential
85
+ # weigth = 1 --> sets Combo.side to buy and overwrites the action statement
86
+ # leg_params[:weight] = 1 unless leg_params.key?(:weight) || leg_params.key?(:ratio)
87
+ if nc.is_a?( IB::Contract) && nc.con_id.present?
88
+ the_leg= ComboLeg.new( nc.attributes.slice( :con_id, :exchange )
89
+ .merge( leg_params ))
90
+ self.combo_legs << the_leg
91
+ self.description = "#{description.nil? ? "": description} added #{nc.to_human}" rescue "Spread: #{nc.to_human}"
92
+ self.legs << nc
93
+ end
94
+ self # return value to enable chaining
95
+
96
+
97
+ end
98
+
99
+ # removes the contract from the spread definition
100
+ #
101
+ def remove_leg contract
102
+ contract.verify do |c|
103
+ legs.delete_if { |x| x.con_id == c.con_id }
104
+ combo_legs.delete_if { |x| x.con_id == c.con_id }
105
+ self.description = description + " removed #{c.to_human}"
106
+ end
107
+ self
108
+ end
109
+
110
+ # essentail
111
+ # effectivley clones the object
112
+ #
113
+ def essential
114
+ the_es = self.class.new invariant_attributes
115
+ the_es.legs = legs.map{|y| IB::Contract.build y.invariant_attributes}
116
+ the_es.combo_legs = combo_legs.map{|y| IB::ComboLeg.new y.invariant_attributes }
117
+ the_es.description = description
118
+ the_es # return
119
+ end
120
+
121
+ def multiplier
122
+ (legs.map(&:multiplier).sum/legs.size).to_i
123
+ end
124
+
125
+ # provide a negative con_id
126
+ def con_id
127
+ if attributes[:con_id].present? && attributes[] < 0
128
+ attributes[:con_id]
129
+ else
130
+ -legs.map{ |x| x.is_a?(String) ? x.expand.con_id : x.con_id}.sum
131
+ end
132
+ end
133
+
134
+
135
+ def non_guaranteed= x
136
+ super.merge combo_params: [ ['NonGuaranteed', x] ]
137
+ end
138
+
139
+
140
+ def non_guaranteed
141
+ combo_params['NonGuaranteed']
142
+ end
143
+ # optional: specify default order prarmeters for all spreads
144
+ # def order_requirements
145
+ # super.merge symbol: symbol
146
+ # end
147
+
148
+
149
+ def self.build_from_json container
150
+ read_leg = ->(a) do
151
+ IB::ComboLeg.new :con_id => a.read_int,
152
+ :ratio => a.read_int,
153
+ :action => a.read_string,
154
+ :exchange => a.read_string
155
+
156
+ end
157
+ object= self.new container['Spread'].clone.read_contract
158
+ object.legs = container['legs'].map{|x| IB::Contract.build x.clone.read_contract}
159
+ object.combo_legs = container['combo_legs'].map{ |x| read_leg[ x.clone ] }
160
+ object.description = container['misc'].clone.read_string
161
+ object
162
+
163
+ end
164
+
165
+ end
166
+
167
+
168
+ end