ib-api 972.0 → 972.4

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 (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
@@ -26,7 +26,11 @@ module IB
26
26
 
27
27
  class ReceiveFA
28
28
  def accounts
29
- xml[:ListOfAccountAliases][:AccountAlias].map{|x| Account.new x }
29
+ if( a= xml[:ListOfAccountAliases][:AccountAlias]).is_a? Array
30
+ a.map{|x| Account.new x }
31
+ elsif a.is_a? Hash ## only one account (soley financial advisor)
32
+ [ Account.new( a ) ]
33
+ end
30
34
  end
31
35
 
32
36
  def to_human
@@ -56,8 +56,6 @@ module IB
56
56
  @contract_detail = IB::ContractDetail.new @data[:contract_detail]
57
57
  end
58
58
 
59
- alias contract_details contract_detail
60
-
61
59
  def to_human
62
60
  "<Contract #{contract.to_human} #{contract_detail.to_human}>"
63
61
  end
@@ -41,12 +41,12 @@ module IB
41
41
  IB::Bar.new :time => buffer.read_int_date, # conversion of epoche-time-integer to Dateime
42
42
  # requires format_date in request to be "2"
43
43
  # (outgoing/bar_requests # RequestHistoricalData#Encoding)
44
- :open => buffer.read_decimal,
45
- :high => buffer.read_decimal,
46
- :low => buffer.read_decimal,
47
- :close => buffer.read_decimal,
44
+ :open => buffer.read_float,
45
+ :high => buffer.read_float,
46
+ :low => buffer.read_float,
47
+ :close => buffer.read_float,
48
48
  :volume => buffer.read_int,
49
- :wap => buffer.read_decimal,
49
+ :wap => buffer.read_float,
50
50
  # :has_gaps => buffer.read_string, # only in ServerVersion < 124
51
51
  :trades => buffer.read_int
52
52
  end
@@ -79,6 +79,26 @@ module IB
79
79
  end
80
80
  end
81
81
 
82
+ HistoricalDataUpdate = def_message [90, 0] ,
83
+ [:request_id, :int] ,
84
+ [:count, :int],
85
+ [:bar, :bar] # defined in support.rb
86
+
87
+ class HistoricalDataUpdate
88
+ attr_accessor :results
89
+ using IBSupport # extended Array-Class from abstract_message
90
+
91
+ def bar
92
+ @bar = IB::Bar.new @data[:bar]
93
+ end
94
+
95
+ def to_human
96
+ "<HistDataUpdate #{request_id} #{bar}>"
97
+ end
98
+ end
99
+
100
+
101
+
82
102
  end # module Incoming
83
103
  end # module Messages
84
104
  end # module IB
@@ -13,7 +13,7 @@ module IB
13
13
  "<#{self.message_type} #{type}:" +
14
14
  @data.map do |key, value|
15
15
  " #{key} #{value}" unless [:version, :ticker_id, :tick_type].include?(key)
16
- end.compact.join(',') + " >"
16
+ end.compact.join('",') + " >"
17
17
  end
18
18
 
19
19
  def the_data
@@ -129,71 +129,32 @@ module IB
129
129
  [:theta, :decimal_limit_2], # -2 -"-
130
130
  [:under_price, :decimal_limit_1]) do
131
131
 
132
- "<TickOption #{type} for #{ticker_id}: underlying @ #{under_price}, "+
133
- "option @ #{option_price}, IV #{implied_volatility}%, delta #{delta}, " +
134
- "gamma #{gamma}, vega #{vega}, theta #{theta}, pv_dividend #{pv_dividend}>"
132
+ "<TickOption #{type} " +
133
+ "option @ #{"%8.3f" % (option_price || -1)}, IV: #{"%4.3f" % (implied_volatility || -1)}, " +
134
+ "delta: #{"%5.3f" % (delta || -1)}, " +
135
+ "gamma: #{"%6.4f" % (gamma || -1)}, vega: #{ "%6.5f" % (vega || -1)}, " +
136
+ "theta: #{"%7.6f" % (theta || -1)}, pv_dividend: #{"%5.3f" % (pv_dividend || -1)}, " +
137
+ "underlying @ #{"% 8.3f" % (under_price || -1)} >"
135
138
  end
136
139
 
137
- TickSnapshotEnd = def_message 57, [:ticker_id, :int]
138
- =begin
139
- def processTickByTickMsg(self, fields):
140
- next(fields)
141
- reqId = decode(int, fields)
142
- tickType = decode(int, fields)
143
- time = decode(int, fields)
144
-
145
- if tickType == 0:
146
- # None
147
- pass
148
- elif tickType == 1 or tickType == 2:
149
- # Last or AllLast
150
- price = decode(float, fields)
151
- size = decode(int, fields)
152
- mask = decode(int, fields)
153
- class TickAttribLast(Object):
154
- def __init__(self):
155
- self.pastLimit = False
156
- self.unreported = False
157
-
158
- def __str__(self):
159
- return "PastLimit: %d, Unreported: %d" % (self.pastLimit, self.unreported)
160
-
161
- tickAttribLast = TickAttribLast()
162
- tickAttribLast.pastLimit = mask & 1 != 0
163
- tickAttribLast.unreported = mask & 2 != 0
164
- exchange = decode(str, fields)
165
- specialConditions = decode(str, fields)
140
+ class TickOption
141
+ def greeks
142
+ { delta: delta, gamma: gamma, vega: vega, theta: theta }
143
+ end
166
144
 
167
- self.wrapper.tickByTickAllLast(reqId, tickType, time, price, size, tickAttribLast,
168
- exchange, specialConditions)
169
- elif tickType == 3:
170
- # BidAsk
171
- bidPrice = decode(float, fields)
172
- askPrice = decode(float, fields)
173
- bidSize = decode(int, fields)
174
- askSize = decode(int, fields)
175
- mask = decode(int, fields)
176
- class TickAttribBidAsk(Object):
177
- def __init__(self):
178
- self.bidPastLow = False
179
- self.askPastHigh = False
145
+ def iv
146
+ implied_volatility
147
+ end
148
+
149
+
150
+ def greeks?
151
+ greeks.values.any? &:present?
152
+ end
180
153
 
181
- def __str__(self):
182
- return "BidPastLow: %d, AskPastHigh: %d" % (self.bidPastLow, self.askPastHigh)
154
+ end
183
155
 
156
+ TickSnapshotEnd = def_message 57, [:ticker_id, :int]
184
157
 
185
- tickAttribBidAsk = TickAttribBidAsk()
186
- tickAttribBidAsk.bidPastLow = mask & 1 != 0
187
- tickAttribBidAsk.askPastHigh = mask & 2 != 0
188
-
189
- self.wrapper.tickByTickBidAsk(reqId, time, bidPrice, askPrice, bidSize,
190
- askSize, tickAttribBidAsk)
191
- elif tickType == 4:
192
- # MidPoint
193
- midPoint = decode(float, fields)
194
-
195
- self.wrapper.tickByTickMidPoint(reqId, time, midPoint)
196
- =end
197
158
  TickByTick = def_message [99, 0], [:ticker_id, :int ],
198
159
  [ :tick_type, :int],
199
160
  [ :time, :int_date ]
@@ -249,10 +249,12 @@ module IB
249
249
  CancelHistogramData =
250
250
  def_message [89,0 ] # , :(request_)id required
251
251
 
252
+ ## Attention: If not reasonable data are used, simply nothing is returned.
253
+ ## There is no error message either.
252
254
  RequestCalculateImpliedVolatility = CalculateImpliedVolatility =
253
255
  RequestImpliedVolatility =
254
256
  def_message([ 54,3 ],:request_id, # autogenerated
255
- [:contract, :serialize_short, :primary_exchange],
257
+ [:contract, :serialize_short],
256
258
  :option_price,
257
259
  :under_price,
258
260
  [:implied_volatility_options_count, 0],
@@ -262,7 +264,7 @@ module IB
262
264
  # :volatility => double, :under_price => double }
263
265
  RequestCalculateOptionPrice = CalculateOptionPrice = RequestOptionPrice =
264
266
  def_message([ 55, 3], :request_id, #autogenerated if not specified
265
- [:contract, :serialize_short, :primary_exchange],
267
+ [:contract, :serialize_short],
266
268
  :volatility,
267
269
  :under_price,
268
270
  [:implied_volatility_options_count, 0],
@@ -25,9 +25,9 @@ module IB
25
25
  #
26
26
  def send_to socket
27
27
  ### debugging of outgoing Messages
28
- # puts "------sendto ---------(debugging output in outgoing/abstract_message)"
29
- # puts socket.prepare_message( self.preprocess).inspect.split('\x00')[3..-1].inspect
30
- # puts "------sendto ---------"
28
+ # puts "------sendto ---------(debugging output in outgoing/abstract_message)"
29
+ # puts socket.prepare_message( self.preprocess).inspect.split('\x00')[3..-1].inspect
30
+ # puts "------sendto ---------"
31
31
  socket.send_messages self.preprocess #.each {|data| socket.write_data data}
32
32
  end
33
33
 
@@ -18,11 +18,10 @@ module IB
18
18
  # unless BAR_SIZES.keys.include?(bar_size)
19
19
  # error ":bar_size must be one of #{BAR_SIZES.inspect}", :args
20
20
  # end
21
-
22
- contract = data[:contract].is_a?(IB::Contract) ?
23
- data[:contract] : IB::Contract.from_ib_ruby(data[:contract])
24
-
25
- [data_type, nil, contract]
21
+ unless data[:contract].is_a? IB::Contract
22
+ error "contract must be a valid IB::Contract" , :args
23
+ end
24
+ [data_type, nil, data[:contract]]
26
25
  end
27
26
  end
28
27
 
@@ -4,7 +4,8 @@ module IB
4
4
  module Outgoing
5
5
  extend Messages # def_message macros
6
6
 
7
-
7
+ # ==> details: https://interactivebrokers.github.io/tws-api/tick_types.html
8
+ #
8
9
  # @data={:id => int: ticker_id - Must be a unique value. When the market data
9
10
  # returns, it will be identified by this tag,
10
11
  # if omitted, id-autogeneration process is performed
@@ -32,7 +33,7 @@ module IB
32
33
  # 292 - (Wide News)
33
34
  # 293 - (TradeCount)
34
35
  # 295 - (VolumeRate)
35
- # 318 - (iLastRTHT-Trade)
36
+ # 318 - (LastRTHT-Trade)
36
37
  # 370 - (Participation Monitor)
37
38
  # 375 - RTTrdVolumne
38
39
  # 377 - CttTickTag
@@ -46,7 +47,11 @@ module IB
46
47
  # 411 - Realtime Historical Volatility - 58
47
48
  # 428 - Monetary Close
48
49
  # 439 - MonitorTicTag
49
- # 456/59 - IB Dividends
50
+ # 456/59 - IB Dividends, 4 comma separated values: 12 Month dividend,
51
+ # projected 12 Month dividend,
52
+ # next dividend date,
53
+ # next dividend value
54
+ # (use primary exchange instead of smart)
50
55
  # 459 - RTCLOSE
51
56
  # 460 - Bond Factor Multiplier
52
57
  # 499 - Fee and Rebate Ratge
@@ -79,7 +84,7 @@ module IB
79
84
  # :tick_list values if you use snapshot.
80
85
  #
81
86
  # :regulatory_snapshot => bool - With the US Value Snapshot Bundle for stocks,
82
- # regulatory snapshots are available for 0.01 USD each.
87
+ # regulatory snapshots are available for 0.01 USD each. (applies on demo accounts as well)
83
88
  # :mktDataOptions => (TagValueList) For internal use only.
84
89
  # Use default value XYZ.
85
90
  #
@@ -88,9 +93,7 @@ module IB
88
93
  [:contract, :serialize_short, :primary_exchange], # include primary exchange in request
89
94
  [:contract, :serialize_legs, []],
90
95
  [:contract, :serialize_under_comp, []],
91
- [:tick_list, lambda do |tick_list|
92
- tick_list.is_a?(Array) ? tick_list.join(',') : (tick_list || '')
93
- end, []],
96
+ [:tick_list, ->(tick_list){ tick_list.is_a?(Array) ? tick_list.join(',') : (tick_list || '')}, []],
94
97
  [:snapshot, false],
95
98
  [:regulatory_snapshot, false],
96
99
  [:mkt_data_options, "XYZ"]
data/lib/ib/models.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'ib/model'
2
1
 
3
2
  require 'models/ib/account'
4
3
  require 'models/ib/account_value'
@@ -11,4 +10,5 @@
11
10
  require 'models/ib/combo_leg'
12
11
  require 'models/ib/execution'
13
12
  require 'models/ib/bar'
13
+ require 'models/ib/spread'
14
14
  require 'models/ib/condition'
data/lib/ib/support.rb CHANGED
@@ -62,7 +62,7 @@ module IBSupport
62
62
 
63
63
  def read_int_date
64
64
  t= read_int
65
- s= Time.at(t)
65
+ s= Time.at(t.to_i)
66
66
  # s.year == 1970 --> data is most likely a date-string
67
67
  s.year == 1970 ? Date.parse(t.to_s) : s
68
68
  end
@@ -139,20 +139,37 @@ module IBSupport
139
139
  end
140
140
  #
141
141
 
142
- def read_contract # read a standard contract and return als hash
143
- { con_id: read_int,
144
- symbol: read_string,
145
- sec_type: read_string,
146
- expiry: read_string,
147
- strike: read_decimal,
148
- right: read_string,
149
- multiplier: read_int,
150
- exchange: read_string,
151
- currency: read_string,
152
- local_symbol: read_string,
153
- trading_class: read_string } # new Version 8
154
-
155
- end
142
+ def read_contract # read a standard contract and return als hash
143
+ { con_id: read_int,
144
+ symbol: read_string,
145
+ sec_type: read_string,
146
+ expiry: read_string,
147
+ strike: read_decimal,
148
+ right: read_string,
149
+ multiplier: read_int,
150
+ exchange: read_string,
151
+ currency: read_string,
152
+ local_symbol: read_string,
153
+ trading_class: read_string } # new Version 8
154
+ end
155
+
156
+
157
+ def read_bar # read a Historical data bar
158
+ # ** historicalDataUpdate: time open close high low ** covered hier
159
+ # historicalData time open high low close <- covered in messages/incomming
160
+ { :time => read_int_date, # conversion of epoche-time-integer to Dateime
161
+ # requires format_date in request to be "2"
162
+ # (outgoing/bar_requests # RequestHistoricalData#Encoding)
163
+ :open => read_float,
164
+ :close => read_float,
165
+ :high => read_float,
166
+ :low => read_float,
167
+ :wap => read_float,
168
+ :volume => read_int,
169
+ # :has_gaps => read_string, # only in ServerVersion < 124
170
+ :trades => read_int }
171
+
172
+ end
156
173
 
157
174
 
158
175
  alias read_bool read_boolean
data/lib/logging.rb ADDED
@@ -0,0 +1,45 @@
1
+ #module Kernel
2
+ # private
3
+ # def this_method_name
4
+ # caller[0] =~ /`([^']*)'/ and $1
5
+ # end
6
+ # see also __method__ and __callee__
7
+ #end
8
+
9
+
10
+
11
+ module Support
12
+ module Logging
13
+ def self.included(base)
14
+ base.extend ClassMethods
15
+ base.send :define_method, :logger do
16
+ base.logger
17
+ end
18
+ end
19
+
20
+ module ClassMethods
21
+ def logger
22
+ @logger
23
+ end
24
+
25
+ def logger=(logger)
26
+ @logger = logger
27
+ end
28
+
29
+ def configure_logger(log=nil)
30
+ if log
31
+ @logger = log
32
+ else
33
+ @logger = Logger.new(STDOUT)
34
+ @logger.level = Logger::INFO
35
+ @logger.formatter = proc do |severity, datetime, progname, msg|
36
+ # "#{datetime.strftime("%d.%m.(%X)")}#{"%5s" % severity}->#{msg}\n"
37
+ "#{"%5s" % severity}::#{msg}\n"
38
+ end
39
+ @logger.debug "------------------------------ start logging ----------------------------"
40
+ end # branch
41
+ end # def
42
+ end # module ClassMethods
43
+ end # module Logging
44
+ end # module Support
45
+
@@ -1,7 +1,6 @@
1
1
  module IB
2
2
  class Account < IB::Model
3
3
  include BaseProperties
4
- # include Redis::Objects
5
4
  # attr_accessible :alias, :account, :connected
6
5
 
7
6
  prop :account, # String
@@ -10,10 +9,6 @@ module IB
10
9
  :last_updated,
11
10
  :connected => :bool
12
11
 
13
- # redis_id_field :account
14
- # value :my_alias
15
- # value :the_account
16
- # value :active
17
12
 
18
13
 
19
14
  validates_format_of :account, :with => /\A[D]?[UF]{1}\d{5,8}\z/ , :message => 'should be (X)X00000'
@@ -37,14 +32,6 @@ module IB
37
32
  Connection.logger
38
33
  end
39
34
 
40
- # Setze Account connect/disconnect und undate!
41
- def connect!
42
- update_attribute :connected , true
43
- end
44
- def disconnect!
45
- update_attribute :connected , false
46
- end
47
-
48
35
  def print_type #nodoc#
49
36
  (test_environment? ? "demo_" : "") + ( user? ? "user" : "advisor" )
50
37
  end
data/lib/models/ib/bag.rb CHANGED
@@ -1,7 +1,13 @@
1
- require 'models/ib/contract'
1
+ #require 'models/ib/contract'
2
2
 
3
3
  module IB
4
4
 
5
+ if defined?(Bag)
6
+ puts "Bag already a #{defined?(Bag)}"
7
+
8
+ # puts Bag.ancestors
9
+ IB.send(:remove_const, 'Bag')
10
+ end
5
11
  # "BAG" is not really a contract, but a combination (combo) of securities.
6
12
  # AKA basket or bag of securities. Individual securities in combo are represented
7
13
  # by ComboLeg objects.
data/lib/models/ib/bar.rb CHANGED
@@ -22,7 +22,7 @@ module IB
22
22
  validates_numericality_of :open, :high, :low, :close, :volume
23
23
 
24
24
  def to_human
25
- "<Bar: #{time} wap #{wap} OHLC #{open} #{high} #{low} #{close} " +
25
+ "<Bar: #{time.strftime("(%d.%m.%y)%X")} wap #{wap.round(3)} OHLC #{open} #{high} #{low} #{close} " +
26
26
  (trades ? "trades #{trades}" : "") + " vol #{volume}>"
27
27
  end
28
28