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
@@ -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