ib-api 10.33.4 → 972.0

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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/Gemfile +2 -3
  4. data/Gemfile.lock +52 -67
  5. data/README.md +8 -60
  6. data/VERSION +1 -1
  7. data/api.gemspec +3 -10
  8. data/bin/console +38 -39
  9. data/bin/console.yml +2 -2
  10. data/changelog.md +0 -25
  11. data/example/README.md +76 -0
  12. data/example/account_info +54 -0
  13. data/example/account_positions +30 -0
  14. data/example/account_summary +88 -0
  15. data/example/cancel_orders +74 -0
  16. data/example/fa_accounts +25 -0
  17. data/example/fundamental_data +40 -0
  18. data/example/historic_data_cli +186 -0
  19. data/example/list_orders +45 -0
  20. data/example/portfolio_csv +81 -0
  21. data/example/scanner_data +62 -0
  22. data/example/template +19 -0
  23. data/example/tick_data +28 -0
  24. data/lib/extensions/class-extensions.rb +87 -0
  25. data/lib/ib/base.rb +1 -7
  26. data/lib/ib/base_properties.rb +12 -30
  27. data/lib/ib/connection.rb +269 -418
  28. data/lib/ib/constants.rb +22 -31
  29. data/lib/ib/errors.rb +1 -9
  30. data/lib/ib/logger.rb +26 -0
  31. data/lib/ib/messages/abstract_message.rb +43 -10
  32. data/lib/ib/messages/incoming/abstract_message.rb +98 -98
  33. data/lib/ib/messages/incoming/account_value.rb +78 -0
  34. data/lib/ib/messages/incoming/alert.rb +4 -4
  35. data/lib/ib/messages/incoming/contract_data.rb +29 -32
  36. data/lib/ib/messages/incoming/execution_data.rb +3 -3
  37. data/lib/ib/messages/incoming/historical_data.rb +33 -14
  38. data/lib/ib/messages/incoming/{market_depth.rb → market_depths.rb} +10 -0
  39. data/lib/ib/messages/incoming/next_valid_id.rb +0 -1
  40. data/lib/ib/messages/incoming/open_order.rb +201 -214
  41. data/lib/ib/messages/incoming/order_status.rb +1 -1
  42. data/lib/ib/messages/incoming/portfolio_value.rb +58 -27
  43. data/lib/ib/messages/incoming/scanner_data.rb +1 -1
  44. data/lib/ib/messages/incoming/ticks.rb +268 -0
  45. data/lib/ib/messages/incoming.rb +95 -136
  46. data/lib/ib/messages/outgoing/abstract_message.rb +22 -18
  47. data/lib/ib/messages/outgoing/{request_account_summary.rb → account_requests.rb} +40 -7
  48. data/lib/ib/messages/outgoing/{bar_request_message.rb → bar_requests.rb} +16 -13
  49. data/lib/ib/messages/outgoing/place_order.rb +198 -138
  50. data/lib/ib/messages/outgoing/{request_market_data.rb → request_marketdata.rb} +29 -32
  51. data/lib/ib/messages/outgoing/request_tick_data.rb +21 -0
  52. data/lib/ib/messages/outgoing.rb +160 -133
  53. data/lib/ib/messages.rb +7 -47
  54. data/lib/ib/model.rb +4 -0
  55. data/lib/ib/models.rb +14 -0
  56. data/lib/{server_versions.rb → ib/server_versions.rb} +9 -40
  57. data/lib/ib/socket.rb +147 -45
  58. data/lib/ib/support.rb +158 -240
  59. data/lib/ib/version.rb +1 -1
  60. data/lib/ib-api.rb +5 -42
  61. data/lib/models/ib/account.rb +85 -0
  62. data/{models → lib/models}/ib/account_value.rb +5 -5
  63. data/{models → lib/models}/ib/bag.rb +7 -7
  64. data/{models → lib/models}/ib/bar.rb +2 -2
  65. data/{models → lib/models}/ib/combo_leg.rb +4 -26
  66. data/lib/models/ib/condition.rb +245 -0
  67. data/{models → lib/models}/ib/contract.rb +170 -166
  68. data/{models → lib/models}/ib/contract_detail.rb +15 -25
  69. data/{models → lib/models}/ib/execution.rb +1 -1
  70. data/{models → lib/models}/ib/forex.rb +2 -1
  71. data/lib/models/ib/future.rb +15 -0
  72. data/{models → lib/models}/ib/index.rb +3 -2
  73. data/lib/models/ib/option.rb +78 -0
  74. data/lib/models/ib/option_detail.rb +55 -0
  75. data/{models → lib/models}/ib/order.rb +165 -366
  76. data/{models → lib/models}/ib/order_state.rb +22 -25
  77. data/lib/models/ib/portfolio_value.rb +64 -0
  78. data/lib/models/ib/stock.rb +16 -0
  79. data/{models → lib/models}/ib/underlying.rb +5 -3
  80. data/lib/models/ib/vertical.rb +96 -0
  81. data/lib/requires.rb +12 -0
  82. metadata +64 -188
  83. data/CLAUDE.md +0 -131
  84. data/LLM_GUIDE.md +0 -388
  85. data/bin/simple +0 -91
  86. data/conditions/ib/execution_condition.rb +0 -31
  87. data/conditions/ib/margin_condition.rb +0 -28
  88. data/conditions/ib/order_condition.rb +0 -29
  89. data/conditions/ib/percent_change_condition.rb +0 -34
  90. data/conditions/ib/price_condition.rb +0 -44
  91. data/conditions/ib/time_condition.rb +0 -42
  92. data/conditions/ib/volume_condition.rb +0 -36
  93. data/lib/class_extensions.rb +0 -167
  94. data/lib/ib/contract.rb +0 -30
  95. data/lib/ib/messages/incoming/abstract_tick.rb +0 -25
  96. data/lib/ib/messages/incoming/account_message.rb +0 -26
  97. data/lib/ib/messages/incoming/contract_message.rb +0 -13
  98. data/lib/ib/messages/incoming/histogram_data.rb +0 -30
  99. data/lib/ib/messages/incoming/historical_data_update.rb +0 -50
  100. data/lib/ib/messages/incoming/managed_accounts.rb +0 -21
  101. data/lib/ib/messages/incoming/market_depth_l2.rb +0 -15
  102. data/lib/ib/messages/incoming/position_data.rb +0 -21
  103. data/lib/ib/messages/incoming/positions_multi.rb +0 -15
  104. data/lib/ib/messages/incoming/receive_fa.rb +0 -30
  105. data/lib/ib/messages/incoming/tick_by_tick.rb +0 -77
  106. data/lib/ib/messages/incoming/tick_efp.rb +0 -18
  107. data/lib/ib/messages/incoming/tick_generic.rb +0 -12
  108. data/lib/ib/messages/incoming/tick_option.rb +0 -60
  109. data/lib/ib/messages/incoming/tick_price.rb +0 -60
  110. data/lib/ib/messages/incoming/tick_size.rb +0 -55
  111. data/lib/ib/messages/incoming/tick_string.rb +0 -13
  112. data/lib/ib/messages/outgoing/new-place-order.rb +0 -193
  113. data/lib/ib/messages/outgoing/old-place-order.rb +0 -147
  114. data/lib/ib/messages/outgoing/request_historical_data.rb +0 -182
  115. data/lib/ib/messages/outgoing/request_market_depth.rb +0 -57
  116. data/lib/ib/messages/outgoing/request_real_time_bars.rb +0 -48
  117. data/lib/ib/messages/outgoing/request_scanner_subscription.rb +0 -73
  118. data/lib/ib/messages/outgoing/request_tick_by_tick_data.rb +0 -21
  119. data/lib/ib/order_condition.rb +0 -26
  120. data/lib/ib/plugins.rb +0 -27
  121. data/lib/ib/prepare_data.rb +0 -61
  122. data/lib/ib/raw_message_parser.rb +0 -99
  123. data/lib/support/array_function.rb +0 -28
  124. data/lib/support/logging.rb +0 -45
  125. data/models/ib/account.rb +0 -72
  126. data/models/ib/future.rb +0 -64
  127. data/models/ib/option.rb +0 -149
  128. data/models/ib/option_detail.rb +0 -84
  129. data/models/ib/portfolio_value.rb +0 -86
  130. data/models/ib/spread.rb +0 -176
  131. data/models/ib/stock.rb +0 -25
  132. data/plugins/ib/advanced-account.rb +0 -442
  133. data/plugins/ib/alerts/base-alert.rb +0 -125
  134. data/plugins/ib/alerts/gateway-alerts.rb +0 -15
  135. data/plugins/ib/alerts/order-alerts.rb +0 -73
  136. data/plugins/ib/auto-adjust.rb +0 -0
  137. data/plugins/ib/connection-tools.rb +0 -122
  138. data/plugins/ib/eod.rb +0 -326
  139. data/plugins/ib/greeks.rb +0 -102
  140. data/plugins/ib/managed-accounts.rb +0 -274
  141. data/plugins/ib/market-price.rb +0 -150
  142. data/plugins/ib/option-chain.rb +0 -167
  143. data/plugins/ib/order-flow.rb +0 -157
  144. data/plugins/ib/order-prototypes/abstract.rb +0 -67
  145. data/plugins/ib/order-prototypes/adaptive.rb +0 -40
  146. data/plugins/ib/order-prototypes/all-in-one.rb +0 -46
  147. data/plugins/ib/order-prototypes/combo.rb +0 -46
  148. data/plugins/ib/order-prototypes/forex.rb +0 -40
  149. data/plugins/ib/order-prototypes/limit.rb +0 -193
  150. data/plugins/ib/order-prototypes/market.rb +0 -116
  151. data/plugins/ib/order-prototypes/pegged.rb +0 -169
  152. data/plugins/ib/order-prototypes/premarket.rb +0 -31
  153. data/plugins/ib/order-prototypes/stop.rb +0 -202
  154. data/plugins/ib/order-prototypes/volatility.rb +0 -39
  155. data/plugins/ib/order-prototypes.rb +0 -118
  156. data/plugins/ib/probability-of-expiring.rb +0 -109
  157. data/plugins/ib/process-orders.rb +0 -155
  158. data/plugins/ib/roll.rb +0 -86
  159. data/plugins/ib/spread-prototypes/butterfly.rb +0 -77
  160. data/plugins/ib/spread-prototypes/calendar.rb +0 -97
  161. data/plugins/ib/spread-prototypes/stock-spread.rb +0 -56
  162. data/plugins/ib/spread-prototypes/straddle.rb +0 -70
  163. data/plugins/ib/spread-prototypes/strangle.rb +0 -93
  164. data/plugins/ib/spread-prototypes/vertical.rb +0 -83
  165. data/plugins/ib/spread-prototypes.rb +0 -70
  166. data/plugins/ib/symbols/abstract.rb +0 -136
  167. data/plugins/ib/symbols/bonds.rb +0 -28
  168. data/plugins/ib/symbols/cfd.rb +0 -19
  169. data/plugins/ib/symbols/combo.rb +0 -46
  170. data/plugins/ib/symbols/commodity.rb +0 -17
  171. data/plugins/ib/symbols/forex.rb +0 -41
  172. data/plugins/ib/symbols/futures.rb +0 -127
  173. data/plugins/ib/symbols/index.rb +0 -43
  174. data/plugins/ib/symbols/options.rb +0 -99
  175. data/plugins/ib/symbols/stocks.rb +0 -44
  176. data/plugins/ib/symbols/version.rb +0 -5
  177. data/plugins/ib/symbols.rb +0 -118
  178. data/plugins/ib/verify.rb +0 -226
  179. data/symbols/w20.yml +0 -210
  180. data/t.txt +0 -20
  181. data/update.md +0 -71
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This script connects to IB API and subscribes to Account Summary Updates for certain tags
4
+
5
+ require 'bundler/setup'
6
+ require 'ib-api'
7
+
8
+ # Definition of what we want listed
9
+ # possible tags
10
+ # NetLiquidation,
11
+ # TotalCashValue - Total cash including futures pnl
12
+ # SettledCash - For cash accounts, this is the same as
13
+ # TotalCashValue
14
+ # AccruedCash - Net accrued interest
15
+ # BuyingPower - The maximum amount of marginable US stocks the
16
+ # account can buy
17
+ # EquityWithLoanValue - Cash + stocks + bonds + mutual funds
18
+ # PreviousDayEquityWithLoanValue,
19
+ # GrossPositionValue - The sum of the absolute value of all stock
20
+ # and equity option positions
21
+ # RegTEquity,
22
+ # RegTMargin,
23
+ # SMA - Special Memorandum Account
24
+ # InitMarginReq,
25
+ # MaintMarginReq,
26
+ # AvailableFunds,
27
+ # ExcessLiquidity,
28
+ # Cushion - Excess liquidity as a percentage of net liquidation value
29
+ # FullInitMarginReq,
30
+ # FullMaintMarginReq,
31
+ # FullAvailableFunds,
32
+ # FullExcessLiquidity,
33
+ # LookAheadNextChange - Time when look-ahead values take effect
34
+ # LookAheadInitMarginReq,
35
+ # LookAheadMaintMarginReq,
36
+ # LookAheadAvailableFunds,
37
+ # LookAheadExcessLiquidity,
38
+ # HighestSeverity - A measure of how close the account is to liquidation
39
+ # DayTradesRemaining - The Number of Open/Close trades a user
40
+ # could put on before Pattern Day Trading is detected. A value of "-1"
41
+ # means that the user can put on unlimited day trades.
42
+ # Leverage - GrossPositionValue / NetLiquidation
43
+ # $LEDGER - Single flag to relay all cash balance tags*, only in base
44
+ # currency.
45
+ # $LEDGER:CURRENCY - Single flag to relay all cash balance tags*, only in
46
+ # the specified currency.
47
+ # $LEDGER:ALL - Single flag to relay all cash balance tags* in all
48
+ #
49
+ tags = %w( NetLiquidation InitMarginReq DayTradesRemaining )
50
+ # connect to IB TWS.
51
+
52
+
53
+ ib = IB::Connection.new( :client_id => 1112) do | gw | #, :port => 7496 # TWS)
54
+ ## Subcribe to forseable events before the connection is completed
55
+ ## Subscribe to TWS alerts/errors
56
+ gw.subscribe(:Alert, :AccountSummary){ |msg| puts msg.to_human }
57
+ gw.logger.level = Logger::FATAL # DEBUG -- INFO -- WARN -- ERROR -- FATAL
58
+ end
59
+
60
+ Thread.new do
61
+ sleep 1
62
+ puts "\n******** Press <Enter> to quit *********\n\n"
63
+ end
64
+ # request the AccountSummary
65
+ request_id = ib.send_message :RequestAccountSummary, tags: tags.join(',')
66
+
67
+ STDIN.gets
68
+ puts "\n *** canceling Request ..."
69
+ ib.send_message :CancelAccountSummary, id: request_id
70
+ sleep 1
71
+ puts "done."
72
+
73
+
74
+ =begin
75
+ Expected Output
76
+
77
+ delta:~/workspace/ib-ruby/example$ ./account_summary
78
+ <AccountSummary: request_id 8111, account DU167349, tag DayTradesRemaining, value -1.0, currency >
79
+ <AccountSummary: request_id 8111, account DU167349, tag InitMarginReq, value 93402.16, currency EUR >
80
+ <AccountSummary: request_id 8111, account DU167349, tag NetLiquidation, value 990842.34, currency EUR >
81
+ <AccountSummary: request_id 8111, account DF167347, tag InitMarginReq, value 0.0, currency EUR >
82
+ <AccountSummary: request_id 8111, account DF167347, tag NetLiquidation, value 0.0, currency EUR >
83
+ <AccountSummary: request_id 8111, account DU167348, tag DayTradesRemaining, value -1.0, currency >
84
+ <AccountSummary: request_id 8111, account DU167348, tag InitMarginReq, value 135317.73, currency EUR >
85
+ <AccountSummary: request_id 8111, account DU167348, tag NetLiquidation, value 949599.26, currency EUR >
86
+
87
+ ******** Press <Enter> to quit *********
88
+ =end
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This script allows you to cancel either a set of open Orders by their ids,
4
+ # or ALL Orders opened via IB API at once. The latter is useful when your
5
+ # robot goes crazy and opens gazillions of wrong limit orders.
6
+ #
7
+ # Accepts the port to connect to as first command-parameter, T)ws or G)ateway, which is the default
8
+ #
9
+ # Numeric Parameter are interpreted as keys for specific orders to kill
10
+
11
+ require 'bundler/setup'
12
+ require 'ib-api'
13
+ specified_port = ARGV[0] || 'Gateway'
14
+ port = case specified_port
15
+ when /^[gG]/
16
+ ARGV.shift # consume the first argument
17
+ 4002
18
+ when /^[Tt]/
19
+ ARGV.shift # consume the first argument
20
+ 7497
21
+ end
22
+
23
+ # First, connect to IB TWS.
24
+ ib = IB::Connection.new client_id: 1111, port: port do | gw |
25
+
26
+ # Subscribe to TWS alerts/errors and order-related messages
27
+ gw.subscribe(:Alert, :ManagedAccounts, :OpenOrder, :OrderStatus, :OpenOrderEnd) { |msg| puts msg.to_human }
28
+ # Set log level
29
+ gw.logger.level = Logger::FATAL # DEBUG -- INFO -- WARN -- ERROR -- FATAL
30
+ end
31
+
32
+ if ARGV.empty?
33
+ ib.send_message :RequestGlobalCancel
34
+ else
35
+ puts "ARGV: #{ARGV}"
36
+ # Will only work for Orders placed under the same :client_id
37
+ ib.cancel_order *ARGV
38
+ puts '-'*55
39
+ puts "Remaining Orders"
40
+ puts '-'*55
41
+ end
42
+
43
+ puts '-'*55
44
+ puts "Remaining Orders"
45
+ ib.send_message :RequestAllOpenOrders
46
+ puts '-'*55
47
+
48
+ sleep 3
49
+
50
+
51
+ ## Expected output
52
+ #12:20:25.154 Connected to server, version: 137,
53
+ # connection time: 2018-02-27 12:20:25 +0000 local, 2018-02-27T12:20:25+00:00 remote.
54
+ #12:20:25.156 Got message 5 (IB::Messages::Incoming::OpenOrder)
55
+ #<OpenOrder: <Stock: WFC USD> <Order: LMT GTC buy 100.0 1.13 Submitted #1/1562725191 from 1112/DU167348 fee 0.0>>
56
+ #12:20:25.158 Got message 3 (IB::Messages::Incoming::OrderStatus)
57
+ #<OrderStatus: <OrderState: Submitted #1/1562725191 from 1112 filled 0.0/100.0 at 0.0/0.0 why_held >>
58
+ #12:20:25.197 Got message 53 (IB::Messages::Incoming::OpenOrderEnd)
59
+ #<OpenOrderEnd: >
60
+ #12:20:25.197 Got message 15 (IB::Messages::Incoming::ManagedAccounts)
61
+ #12:20:25.197 No subscribers for message IB::Messages::Incoming::ManagedAccounts!
62
+ #12:20:25.197 Got message 9 (IB::Messages::Incoming::NextValidId)
63
+ #12:20:25.197 Got next valid order id: 2.
64
+ #12:20:25.254 Got message 5 (IB::Messages::Incoming::OpenOrder)
65
+ #<OpenOrder: <Stock: WFC USD> <Order: LMT GTC buy 100.0 1.13 PendingCancel #1/1562725191 from 1112/DU167348 fee 0.0>>
66
+ #12:20:25.256 Got message 3 (IB::Messages::Incoming::OrderStatus)
67
+ #<OrderStatus: <OrderState: PendingCancel #1/1562725191 from 1112 filled 0.0/100.0 at 0.0/0.0 why_held >>
68
+ #12:20:25.297 Got message 53 (IB::Messages::Incoming::OpenOrderEnd)
69
+ #<OpenOrderEnd: >
70
+ #12:20:25.342 Got message 3 (IB::Messages::Incoming::OrderStatus)
71
+ #<OrderStatus: <OrderState: Cancelled #1/1562725191 from 1112 filled 0.0/100.0 at 0.0/0.0 why_held >>
72
+ #12:20:25.343 Got message 4 (IB::Messages::Incoming::Alert)
73
+ #TWS Error 202: Order Canceled - reason:
74
+ ##
@@ -0,0 +1,25 @@
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
+
@@ -0,0 +1,40 @@
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
@@ -0,0 +1,186 @@
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
+
@@ -0,0 +1,45 @@
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
@@ -0,0 +1,81 @@
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
@@ -0,0 +1,62 @@
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 ADDED
@@ -0,0 +1,19 @@
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
+
data/example/tick_data ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This script reproduces https://github.com/ib-ruby/ib-ruby/issues/12
4
+
5
+ require 'bundler/setup'
6
+ require 'ib-api'
7
+
8
+ contract = IB::Stock.new :symbol=> 'AAPL'
9
+
10
+ # First, connect to IB Gateway.
11
+ ib = IB::Connection.new :client_id => 1112 # id to identify your script
12
+ # :port => 7497 # TWS connection (instead of default Gateway)
13
+ # :received => false # Do not keep all received messages in memory
14
+
15
+ ib.subscribe(:Alert) { |msg| puts msg.to_human }
16
+ ib.subscribe(:MarketDataType) { |msg| puts msg.to_human }
17
+ ib.subscribe(:TickGeneric, :TickString, :TickPrice, :TickSize) { |msg| puts msg.inspect }
18
+ ib.send_message :RequestMarketDataType, :market_data_type => :delayed
19
+ ib.send_message :RequestMarketData, id: 123, contract: contract
20
+
21
+ puts "\nSubscribed to market data"
22
+ puts "\n******** Press <Enter> to cancel... *********\n\n"
23
+ gets
24
+ puts "Cancelling market data subscription.."
25
+
26
+ ib.send_message :CancelMarketData, id: 123
27
+ sleep 1
28
+ puts "Done."