ib-api 972.0 → 972.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|