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.
- checksums.yaml +4 -4
- data/Gemfile.lock +36 -37
- data/README.md +48 -3
- data/VERSION +1 -1
- data/api.gemspec +1 -1
- data/bin/console +4 -8
- data/changelog.md +12 -0
- data/lib/ib/base_properties.rb +3 -4
- data/lib/ib/connection.rb +18 -16
- data/lib/ib/logger.rb +1 -1
- data/lib/ib/messages/incoming.rb +1 -1
- data/lib/ib/messages/incoming/abstract_message.rb +7 -7
- data/lib/ib/messages/incoming/account_value.rb +5 -1
- data/lib/ib/messages/incoming/contract_data.rb +0 -2
- data/lib/ib/messages/incoming/historical_data.rb +25 -5
- data/lib/ib/messages/incoming/ticks.rb +21 -60
- data/lib/ib/messages/outgoing.rb +4 -2
- data/lib/ib/messages/outgoing/abstract_message.rb +3 -3
- data/lib/ib/messages/outgoing/bar_requests.rb +4 -5
- data/lib/ib/messages/outgoing/request_marketdata.rb +10 -7
- data/lib/ib/models.rb +1 -1
- data/lib/ib/support.rb +32 -15
- data/lib/logging.rb +45 -0
- data/lib/models/ib/account.rb +0 -13
- data/lib/models/ib/bag.rb +7 -1
- data/lib/models/ib/bar.rb +1 -1
- data/lib/models/ib/combo_leg.rb +22 -0
- data/lib/models/ib/contract.rb +44 -32
- data/lib/models/ib/contract_detail.rb +13 -7
- data/lib/models/ib/index.rb +1 -1
- data/lib/models/ib/option.rb +8 -2
- data/lib/models/ib/option_detail.rb +19 -3
- data/lib/models/ib/order.rb +5 -0
- data/lib/models/ib/spread.rb +168 -0
- data/lib/models/ib/stock.rb +9 -3
- data/lib/models/ib/underlying.rb +4 -1
- data/lib/requires.rb +10 -3
- metadata +9 -20
- data/example/README.md +0 -76
- data/example/account_info +0 -54
- data/example/account_positions +0 -30
- data/example/account_summary +0 -88
- data/example/cancel_orders +0 -74
- data/example/fa_accounts +0 -25
- data/example/fundamental_data +0 -40
- data/example/historic_data_cli +0 -186
- data/example/list_orders +0 -45
- data/example/portfolio_csv +0 -81
- data/example/scanner_data +0 -62
- data/example/template +0 -19
- 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].
|
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
|
@@ -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.
|
45
|
-
:high => buffer.
|
46
|
-
:low => buffer.
|
47
|
-
:close => buffer.
|
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.
|
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}
|
133
|
-
"option @ #{option_price}, IV #{implied_volatility
|
134
|
-
|
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
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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
|
-
|
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 ]
|
data/lib/ib/messages/outgoing.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
23
|
-
|
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 - (
|
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,
|
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
|
-
|
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
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
+
|
data/lib/models/ib/account.rb
CHANGED
@@ -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
|
-
|
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
|
|