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
data/example/fa_accounts DELETED
@@ -1,25 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # This script receives Financial Adviser and Managed Accounts info
4
-
5
- require 'bundler/setup'
6
- require 'ib-api'
7
-
8
- # First, connect to IB TWS.
9
- ib = IB::Connection.new client_id: 1114, port: 4002 #, :port => 7496 # TWS
10
-
11
- # Subscribe to TWS alerts/errors and FA/managed account info
12
- ib.subscribe(:Alert, :ManagedAccounts, :ReceiveFA) { |msg| puts msg.to_human }
13
-
14
- ##
15
- ## type of Financial Advisor configuration data
16
- # being received from TWS. Valid values include:
17
- # 1 = GROUPS, 2 = PROFILE, 3 = ACCOUNT ALIASES
18
-
19
- ib.send_message :RequestFA, fa_data_type: 3
20
- ib.send_message :RequestManagedAccounts
21
-
22
- ib.wait_for :ReceiveFA
23
-
24
-
25
-
@@ -1,40 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # This script downloads Fundamental data for specific symbols from IB
4
- # This only works IF you have Reuters data subscription!
5
-
6
- require 'bundler/setup'
7
-
8
- require 'ib-api'
9
-
10
- # allocate two variables
11
- request_id, snapshot_data = nil
12
-
13
- ib = IB::Connection.new port: 7496, :client_id => 1112 do | gw | #, :port => 7496 # TWS
14
- gw.subscribe(:Alert) { |msg| puts msg.to_human }
15
-
16
- # Fundamental Data will arrive in XML format, we need to parse it
17
- gw.subscribe(:FundamentalData) { |msg| snapshot_data = msg.xml if msg.request_id == request_id }
18
- end
19
-
20
- # Request Fundamental Data for IBM. Possible report types:
21
- # 'estimates' - Estimates
22
- # 'finstat' - Financial statements
23
- # 'snapshot' - Summary
24
- request_id = ib.send_message :RequestFundamentalData,
25
- :contract => IB::Stock.new( symbol: 'ibm' ),
26
- :report_type => 'snapshot'
27
-
28
- # Needs some time to receive and parse XML. Standard timeout of 1 sec is just too low.
29
- ib.wait_for :FundamentalData
30
-
31
- # Now just extract and use all the fundamental data you needed
32
- puts snapshot_data[:ReportSnapshot][:TextInfo][:Text]
33
-
34
- pp snapshot_data[:ReportSnapshot][:Ratios]
35
- STDIN.gets
36
-
37
- puts "Displaying the complete transmitted data:"
38
- puts ""
39
-
40
- pp snapshot_data
@@ -1,186 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # This script connects to IB API, and downloads historic data
3
-
4
- require 'rubygems'
5
- require 'time'
6
- require 'getopt/long'
7
- require 'bundler/setup'
8
- require 'ib-api'
9
-
10
- include Getopt
11
-
12
- opt = Getopt::Long.getopts(
13
- ["--help", BOOLEAN],
14
- ["--end", REQUIRED],
15
- ["--port", REQUIRED],
16
- ["--security", REQUIRED],
17
- ["--duration", REQUIRED],
18
- ["--barsize", REQUIRED],
19
- ["--csv", BOOLEAN],
20
- ["--dateformat", REQUIRED],
21
- ["--nonregularhours", BOOLEAN],
22
- ["--what", OPTIONAL]
23
- )
24
-
25
- if opt["help"] || opt["security"].nil? || opt["security"].empty?
26
- puts <<ENDHELP
27
-
28
- This program requires a TWS or Gateway running on localhost.
29
-
30
- ----------
31
-
32
- One argument is required: --security, the security specification you want, in
33
- "long serialized IB-Ruby" format. This is a colon-separated string of the format:
34
-
35
- symbol:security_type:expiry:strike:right:multiplier:exchange:primary_exchange:currency:local_symbol
36
-
37
- Fields not needed for a particular security should be left blank (e.g. strike and right are only relevant for options.)
38
-
39
- For example, to query the Apple 500 Strike Call expiring in January 2013, use:
40
-
41
- $ ./historic_data_cli --security AAPL:OPT:202101:200:CALL::SMART::USD:
42
-
43
- Consult contract.rb for allowed values, and see also the examples in the symbols/ directory
44
- (load them in irb and run security#serialize_ib_ruby to see the appropriate string.)
45
-
46
- ----------
47
-
48
- Options:
49
-
50
- --end is is the last time we want data for. The default is now.
51
- This is eval'ed by Ruby, so you can use a Ruby expression, which must return a Time object.
52
-
53
- --duration is time period before --end, in seconds. The default is 86400 sec (1 day).
54
- TWS imposes limit of 86400 sec (1 day) worth of historic data per request.
55
-
56
- --what determines what the data will be comprised of. This can be
57
- "trades", "midpoint", "bid", or "ask". The default is "trades".
58
-
59
- --barsize determines how long each bar will be.
60
-
61
- Possible bar values (from the IB documentation):
62
- Values less than 30 sec do not appear to work for some securities.
63
-
64
- sec1 = 1 sec
65
- sec5 = 5 sec
66
- sec15 = 15 sec
67
- sec30 = 30 sec
68
- min1 = 1 minute
69
- min2 = 2 minutes
70
- min5 = 5 minutes
71
- min15 = 15 minutes (default)
72
- min30 = 30 minutes
73
- hour1 = 1 hour
74
- day1 = 1 day
75
-
76
- --nonregularhours : Normally, only data from the instrument's regular trading
77
- hours is returned. If --nonregularhours is given, all data available during the time
78
- span requested is returned, even for time intervals when the market was illiquid.
79
-
80
- --dateformat : 1 (default) human-readable time/date format, like "20050307 11:32:16".
81
- If you set it to 2 instead, you will get UNIX epoch offsets (seconds since Jan 1, 1970).
82
-
83
- --csv : print out the historic data in CSV format, with header.
84
-
85
- --port : 4001 for Gateway (default), 7496 for TWS, or your custom port
86
-
87
- ENDHELP
88
- exit
89
- end
90
-
91
- ### Parameters
92
-
93
- # DURATION is how much historic data we want, in seconds, before END_DATE_TIME.
94
- DURATION = (opt["duration"] && opt["duration"].to_i) || 86400
95
-
96
- if DURATION > 86400
97
- STDERR.puts("\nTWS rejects --duration longer than 86400 seconds (1 day).\n")
98
- exit(1)
99
- end
100
-
101
- # This is the last time we want data for.
102
- END_DATE_TIME = (opt["end"] && eval(opt["end"]).to_ib) || Time.now.to_ib
103
-
104
- # This can be :trades, :midpoint, :bid, or :asked
105
- WHAT = (opt["what"] && opt["what"].to_sym) || :trades
106
-
107
-
108
- # Values less than 4 do not appear to actually work; they are rejected by the server.
109
- #
110
- BAR_SIZE = (opt["barsize"] && opt["barsize"].to_sym) || :min15
111
-
112
- # If REGULAR_HOURS_ONLY is set to 0, all data available during the time
113
- # span requested is returned, even data bars covering time
114
- # intervals where the market in question was illiquid. If useRTH
115
- # has a non-zero value, only data within the "Regular Trading
116
- # Hours" of the product in question is returned, even if the time
117
- # span requested falls partially or completely outside of them.
118
-
119
- REGULAR_HOURS_ONLY = opt["nonregularhours"] ? 0 : 1
120
-
121
- # Using a DATE_FORMAT of 1 will cause the dates in the returned
122
- # messages with the historic data to be in a text format, like
123
- # "20050307 11:32:16". If you set :format_date to 2 instead, you
124
- # will get an offset in seconds from the beginning of 1970, which
125
- # is the same format as the UNIX epoch time.
126
-
127
- DATE_FORMAT = (opt["dateformat"] && opt["dateformat"].to_i) || 1
128
-
129
- PORT = (opt["port"] && opt["port"]) || '4002'
130
-
131
- lastMessageTime = Queue.new # for communicating with the reader thread.
132
-
133
- # First, connect to IB TWS.
134
- ib = IB::Connection.new( :client_id => 1112, :port => PORT) do | gw|
135
-
136
- # Subscribe to TWS alerts/errors
137
- gw.subscribe(:Alert) { |msg| puts msg.to_human }
138
-
139
-
140
- # Subscribe to incoming HistoricalData events. The code passed in the
141
- # block will be executed when a message of the subscribed type is
142
- # received, with the received message as its argument. In this case,
143
- # we just print out the data.
144
- #
145
- # Note that we have to look the ticker id of each incoming message
146
- # up in local memory to figure out what security it relates to.
147
- # The incoming message packet from TWS just identifies it by ticker id.
148
- #
149
- gw.subscribe(:HistoricalData) do |msg|
150
- if opt["csv"]
151
- puts "date,time,open,high,low,close,volume,wap,has_gaps"
152
- msg.results.each do |datum|
153
- puts "#{datum.time},#{datum.open},#{datum.high},#{datum.low}," +
154
- "#{datum.close},#{datum.volume},#{datum.wap},#{datum.has_gaps}"
155
- end
156
- n
157
- STDERR.puts "Received #{msg.count} items:"
158
- msg.results.each { |datum| puts datum.to_s }
159
- end
160
- lastMessageTime.push(Time.now)
161
- end
162
- end
163
- # Now we actually request historical data for the symbols we're
164
- # interested in. TWS will respond with a HistoricalData message,
165
- # which will be received by the code above.
166
- ib.send_message :RequestHistoricalData,
167
- :request_id => 123,
168
- :contract => opt["security"],
169
- :end_date_time => END_DATE_TIME,
170
- :duration => DURATION, # seconds == 1 hour
171
- :bar_size => BAR_SIZE, # 1 minute bars
172
- :what_to_show => WHAT,
173
- :use_RTH => REGULAR_HOURS_ONLY,
174
- :keep_up_todate => 0,
175
- :format_date => DATE_FORMAT
176
-
177
- # A complication here is that IB does not send any indication when all historic data
178
- # is done being delivered. So we have to guess - when there is no more new data for
179
- # some period, we interpret that as "end of data" and exit.
180
- sleep 2
181
- while true
182
- lastTime = lastMessageTime.pop # blocks until a message is ready on the queue
183
- sleep 0.1 # .. wait ..
184
- exit if lastMessageTime.empty? # if still no more messages after 2 more seconds, exit.
185
- end
186
-
data/example/list_orders DELETED
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # This script retrieves list of all Orders from TWS
4
-
5
- require 'bundler/setup'
6
- require 'ib-api'
7
-
8
- # Connect to IB as 0 (TWS) to retrieve all Orders, including TWS-generated ones
9
- ib = IB::Connection.new :client_id => 0 do |gw| # , :port => 7497 # TWS
10
-
11
- # Subscribe to TWS alerts/errors and order-related messages
12
- counter = 0
13
-
14
- gw.subscribe(:Alert, :ManagedAccounts, :OrderStatus, :OpenOrderEnd) { |msg| puts msg.to_human }
15
-
16
- gw.subscribe(:OpenOrder) do |msg|
17
- counter += 1
18
- puts "#{counter}: #{msg.to_human}"
19
-
20
- # Get rid of logging verbosity
21
- gw.logger.level = Logger::FATAL
22
- end
23
- end
24
-
25
- ## fire request
26
- ib.send_message :RequestAllOpenOrders
27
-
28
- # Wait for IB to respond to our request
29
- ib.wait_for :OpenOrderEnd
30
- sleep 1 # Let printer do the job
31
-
32
- =begin
33
- ---> expected output
34
- 1: <OpenOrder: <Bag: GOOG SMART USD legs: 269330045|1,269562569|1,301964119|-2 > <Order: LMT GTC buy 10.0 0.06 PreSubmitted #9/561364885 from 1112/DU167349 fee 0.0>>
35
- <OrderStatus: <OrderState: PreSubmitted #9/561364885 from 1112 filled 0.0/10.0 at 0.0/0.0 why_held locate>>
36
- 2:<OpenOrder: <Stock: WFC USD> <Order: LMT DAY sell 100.0 34.05 PreSubmitted #19/561364895 from 1112/DU167349 fee 0.0>>
37
- <OrderStatus: <OrderState: PreSubmitted #19/561364895 from 1112 filled 0.0/100.0 at 0.0/0.0 why_held child,locate>>
38
- 3: <OpenOrder: <Stock: WFC USD> <Order: STP DAY sell 100.0 0.0 PreSubmitted /33.55#18/561364894 from 1112/DU167349 fee 0.0>>
39
- <OrderStatus: <OrderState: PreSubmitted #18/561364894 from 1112 filled 0.0/100.0 at 0.0/0.0 why_held child,locate,trigger>>
40
- 4: <OpenOrder: <Bag: GOOG SMART USD legs: 269330045|1,269562569|1,301964119|-2 > <Order: LMT GTC buy 10.0 0.06 PreSubmitted #8/1562725198 from 1112/DU167349 fee 0.0>>
41
- <OrderStatus: <OrderState: PreSubmitted #8/1562725198 from 1112 filled 0.0/10.0 at 0.0/0.0 why_held locate>>
42
- 5: <OpenOrder: <Stock: WFC USD> <Order: LMT GTC buy 100.0 1.72 Submitted #3/1562725193 from 1112/DU167349 fee 0.0>>
43
- <OrderStatus: <OrderState: Submitted #3/1562725193 from 1112 filled 0.0/100.0 at 0.0/0.0 why_held >>
44
- <OpenOrderEnd: >
45
- =end
@@ -1,81 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # This script exports your IB portfolio in a CSV format. Usage:
4
- # $ example/portfolio_csv [account] > portfolio.csv
5
-
6
- require 'bundler/setup'
7
- require 'ib-ruby'
8
-
9
- # Only for Advisors: you need to provide account id such as U666777
10
- account = ARGV[0] || ''
11
- accounts = []
12
-
13
- # Connect to IB TWS and subscribe to events
14
- ib = IB::Connection.new( :client_id => 1112 , port: 4002 ) do |gw| # , :port => 7496 # TWS
15
-
16
- # Redirect TWS alerts/errors to STDERR to keep output file clean
17
- #gw.subscribe(:Alert) { |msg| STDERR.puts msg.to_human }
18
-
19
- # Subscribe to TWS alerts/errors and account-related messages
20
- # that TWS sends in response to account data request
21
- gw.subscribe(:Alert) do |msg|
22
- ## if an account is not given. but required, (Error 321 indicates this)
23
- ## fetch data from the last account detected. (The first is most probably the Advisor-Account)
24
- if msg.code == 321
25
- account = accounts.last
26
- gw.send_message :RequestAccountData, :subscribe => true, :account_code => account
27
- else
28
- puts msg.to_human
29
- end
30
- end
31
- # Silently ignore account-related messages other than :PortfolioValue
32
- gw.subscribe(:AccountUpdateTime, :AccountValue, :ManagedAccounts, :AccountDownloadEnd) {}
33
- # Get rid of logging verbosity
34
- gw.logger.level = Logger::FATAL
35
-
36
- ## Just in case: put account-names into accounts-array
37
- gw.subscribe(:ManagedAccounts){ |msg| accounts = msg.accounts_list.split ',' }
38
- end
39
-
40
- # Output CSV headers
41
- puts %w[Symbol
42
- SecType
43
- Expiry
44
- Strike
45
- Right
46
- Currency
47
- LocalSymbol
48
- Position
49
- MarketPrice
50
- MarketValue
51
- AvgCost
52
- UnrealizedPNL
53
- RealizedPNL
54
- Account].map {|val| "\"#{val}\""}.join(",")
55
-
56
- # Output each portfolio position as a single line in CSV format
57
- ib.subscribe(:PortfolioValue) do |msg|
58
- contract = msg.contract
59
- csv = [ contract.symbol,
60
- IB::CODES[:sec_type][contract.sec_type],
61
- contract.expiry,
62
- contract.strike == 0 ? "" : contract.strike,
63
- contract.right == :none ? "" : contract.right,
64
- contract.currency,
65
- contract.local_symbol,
66
- msg.position,
67
- msg.market_price,
68
- msg.market_value,
69
- msg.average_cost,
70
- msg.unrealized_pnl,
71
- msg.realized_pnl,
72
- msg.account_name
73
- ].map {|val| "\"#{val}\""}.join(",")
74
- puts csv
75
- end
76
-
77
- # Request account data, wait for its end, unsubscribe
78
- ib.send_message :RequestAccountData, :subscribe => true, :account_code => account
79
- ib.wait_for :AccountDownloadEnd, 30
80
- ib.send_message :RequestAccountData, :subscribe => false, :account_code => account
81
- sleep 0.5
data/example/scanner_data DELETED
@@ -1,62 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # This Script needs testing. remove this line after sucessfully using the scanner facility
3
- #
4
- # This script setups a scan request and retreives the results.
5
-
6
- require 'rubygems'
7
- require 'bundler/setup'
8
- require 'ib-api'
9
-
10
-
11
-
12
- # Connect to IB TWS.
13
- ib = IB::Connection.new :client_id => 1112 , port: 7496 do | gw | #, :port => 7497 # TWS
14
-
15
-
16
- # Subscribe to TWS alerts/errors
17
- gw.subscribe(:Alert) { |msg| puts msg.to_human }
18
-
19
-
20
- # Subscribe to ScannerData incoming events. The code passed in the block
21
- # will be executed when a message of that type is received, with the received
22
- # message as its argument. In this case, we just print out the data.
23
- #
24
- gw.subscribe(IB::Messages::Incoming::ScannerData) do |msg|
25
- puts "ID: #{msg.request_id} : #{msg.count} items:"
26
- msg.results.each { |entry| puts " #{entry}" }
27
- end
28
- end
29
- id = 42
30
- # Now we actually request scanner data for the type of scan we are interested.
31
- # Some scan types can be found here: http://www.interactivebrokers.com/php/apiUsersGuide/apiguide/tables/available_market_scanners.htm
32
- mess = IB::Messages::Outgoing::RequestScannerSubscription.new(
33
- :request_id => id,
34
- :number_of_rows => 20,
35
- :instrument => "STK",
36
- :location_code => 'STK.US.MAJOR',
37
- :scan_code => 'TOP_PERC_GAIN',
38
- :above_price => 4.0,
39
- :below_price => Float::MAX,
40
- :above_volume => 5000,
41
- :market_cap_above => 100000000,
42
- :market_cap_below => Float::MAX,
43
- :moody_rating_above => "",
44
- :moody_rating_below => "",
45
- :sp_rating_above => "",
46
- :sp_rating_below => "",
47
- :maturity_date_above => "",
48
- :maturity_date_below => "",
49
- :coupon_rate_above => Float::MAX,
50
- :coupon_rate_below => Float::MAX,
51
- :exclude_convertible => "",
52
- :average_option_volume_above => "", # ?
53
- :scanner_setting_pairs => "Annual,true",
54
- :stock_type_filter => "Stock"
55
- )
56
-
57
- ib.send_message( mess )
58
-
59
- # IB does not send any indication when all data is done being delivered.
60
- # So we need to interrupt manually when our request is answered.
61
- puts "\n******** Press <Enter> to exit... *********\n\n"
62
- STDIN.gets
data/example/template DELETED
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # Your script description here...
4
-
5
- require 'bundler/setup'
6
- require 'ib-api'
7
-
8
- # First, connect to IB TWS and subscribe for events.
9
- ib = IB::Connection.new :client_id => 1112 do | gw | #, :port => 7497 # TWS
10
-
11
- # Subscribe to TWS alerts/errors
12
- gw.subscribe(:Alert, :ManagedAccounts) { |msg| puts msg.to_human }
13
- # Set log level
14
- gw.logger.level = Logger::FATAL # DEBUG -- INFO -- WARN -- ERROR -- FATAL
15
-
16
- end
17
- # Put your code here
18
- # ...
19
-