ib-api 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 (91) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +50 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +16 -0
  7. data/Gemfile.lock +105 -0
  8. data/Guardfile +24 -0
  9. data/LICENSE +674 -0
  10. data/README.md +65 -0
  11. data/Rakefile +11 -0
  12. data/VERSION +1 -0
  13. data/api.gemspec +43 -0
  14. data/bin/console +95 -0
  15. data/bin/console.yml +3 -0
  16. data/bin/setup +8 -0
  17. data/changelog.md +7 -0
  18. data/example/README.md +76 -0
  19. data/example/account_info +54 -0
  20. data/example/account_positions +30 -0
  21. data/example/account_summary +88 -0
  22. data/example/cancel_orders +74 -0
  23. data/example/fa_accounts +25 -0
  24. data/example/fundamental_data +40 -0
  25. data/example/historic_data_cli +186 -0
  26. data/example/list_orders +45 -0
  27. data/example/portfolio_csv +81 -0
  28. data/example/scanner_data +62 -0
  29. data/example/template +19 -0
  30. data/example/tick_data +28 -0
  31. data/lib/extensions/class-extensions.rb +87 -0
  32. data/lib/ib-api.rb +7 -0
  33. data/lib/ib/base.rb +103 -0
  34. data/lib/ib/base_properties.rb +160 -0
  35. data/lib/ib/connection.rb +450 -0
  36. data/lib/ib/constants.rb +393 -0
  37. data/lib/ib/errors.rb +44 -0
  38. data/lib/ib/logger.rb +26 -0
  39. data/lib/ib/messages.rb +99 -0
  40. data/lib/ib/messages/abstract_message.rb +101 -0
  41. data/lib/ib/messages/incoming.rb +251 -0
  42. data/lib/ib/messages/incoming/abstract_message.rb +116 -0
  43. data/lib/ib/messages/incoming/account_value.rb +78 -0
  44. data/lib/ib/messages/incoming/alert.rb +34 -0
  45. data/lib/ib/messages/incoming/contract_data.rb +102 -0
  46. data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
  47. data/lib/ib/messages/incoming/execution_data.rb +50 -0
  48. data/lib/ib/messages/incoming/historical_data.rb +84 -0
  49. data/lib/ib/messages/incoming/market_depths.rb +44 -0
  50. data/lib/ib/messages/incoming/next_valid_id.rb +18 -0
  51. data/lib/ib/messages/incoming/open_order.rb +277 -0
  52. data/lib/ib/messages/incoming/order_status.rb +85 -0
  53. data/lib/ib/messages/incoming/portfolio_value.rb +78 -0
  54. data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
  55. data/lib/ib/messages/incoming/scanner_data.rb +54 -0
  56. data/lib/ib/messages/incoming/ticks.rb +268 -0
  57. data/lib/ib/messages/outgoing.rb +437 -0
  58. data/lib/ib/messages/outgoing/abstract_message.rb +88 -0
  59. data/lib/ib/messages/outgoing/account_requests.rb +112 -0
  60. data/lib/ib/messages/outgoing/bar_requests.rb +250 -0
  61. data/lib/ib/messages/outgoing/place_order.rb +209 -0
  62. data/lib/ib/messages/outgoing/request_marketdata.rb +99 -0
  63. data/lib/ib/messages/outgoing/request_tick_data.rb +21 -0
  64. data/lib/ib/model.rb +4 -0
  65. data/lib/ib/models.rb +14 -0
  66. data/lib/ib/server_versions.rb +114 -0
  67. data/lib/ib/socket.rb +185 -0
  68. data/lib/ib/support.rb +160 -0
  69. data/lib/ib/version.rb +6 -0
  70. data/lib/models/ib/account.rb +85 -0
  71. data/lib/models/ib/account_value.rb +33 -0
  72. data/lib/models/ib/bag.rb +55 -0
  73. data/lib/models/ib/bar.rb +31 -0
  74. data/lib/models/ib/combo_leg.rb +105 -0
  75. data/lib/models/ib/condition.rb +245 -0
  76. data/lib/models/ib/contract.rb +415 -0
  77. data/lib/models/ib/contract_detail.rb +108 -0
  78. data/lib/models/ib/execution.rb +67 -0
  79. data/lib/models/ib/forex.rb +13 -0
  80. data/lib/models/ib/future.rb +15 -0
  81. data/lib/models/ib/index.rb +15 -0
  82. data/lib/models/ib/option.rb +78 -0
  83. data/lib/models/ib/option_detail.rb +55 -0
  84. data/lib/models/ib/order.rb +519 -0
  85. data/lib/models/ib/order_state.rb +152 -0
  86. data/lib/models/ib/portfolio_value.rb +64 -0
  87. data/lib/models/ib/stock.rb +16 -0
  88. data/lib/models/ib/underlying.rb +34 -0
  89. data/lib/models/ib/vertical.rb +96 -0
  90. data/lib/requires.rb +12 -0
  91. metadata +203 -0
@@ -0,0 +1,116 @@
1
+ require 'ib/messages/abstract_message'
2
+ require 'ib/support'
3
+ require 'ox'
4
+ module IB
5
+ module Messages
6
+ module Incoming
7
+ using IBSupport # refine Array-method for decoding of IB-Messages
8
+
9
+
10
+ # Container for specific message classes, keyed by their message_ids
11
+ Classes = {}
12
+
13
+ class AbstractMessage < IB::Messages::AbstractMessage
14
+
15
+ attr_accessor :buffer # is an array
16
+
17
+ def version # Per message, received messages may have the different versions
18
+ @data[:version]
19
+ end
20
+
21
+ def check_version actual, expected
22
+ unless actual == expected || expected.is_a?(Array) && expected.include?(actual)
23
+ puts self.class.name
24
+ error "Unsupported version #{actual} received, expected #{expected}"
25
+ end
26
+ end
27
+
28
+ # Create incoming message from a given source (IB Socket or data Hash)
29
+ def initialize source
30
+ @created_at = Time.now
31
+ if source.is_a?(Hash) # Source is a @data Hash
32
+ @data = source
33
+ @buffer =[] # initialize empty buffer, indicates a successfull initializing
34
+ else
35
+ @buffer = source
36
+ ### DEBUG DEBUG DEBUG RAW STREAM ###############
37
+ # if uncommented, the raw-input from the tws is included in the logging
38
+ # puts "BUFFER :> #{buffer.inspect} "
39
+ ### DEBUG DEBUG DEBUG RAW STREAM ###############
40
+ @data = Hash.new
41
+ self.load
42
+ end
43
+ end
44
+
45
+ def valid?
46
+ @buffer.empty?
47
+ end
48
+
49
+ ## more recent messages omit the transmission of a version
50
+ ## thus just load the parameter-map
51
+ def simple_load
52
+ load_map *self.class.data_map
53
+ rescue IB::Error => e
54
+ error "Reading #{self.class}: #{e.class}: #{e.message}", :load, e.backtrace
55
+ end
56
+ # Every message loads received message version first
57
+ # Override the load method in your subclass to do actual reading into @data.
58
+ def load
59
+ unless self.class.version.zero?
60
+ @data[:version] = buffer.read_int
61
+ check_version @data[:version], self.class.version
62
+ end
63
+ simple_load
64
+ end
65
+
66
+ # Load @data from the buffer according to the given data map.
67
+ #
68
+ # map is a series of Arrays in the format of
69
+ # [ :name, :type ], [ :group, :name, :type]
70
+ # type identifiers must have a corresponding read_type method on the buffer-class (read_int, etc.).
71
+ # group is used to lump together aggregates, such as Contract or Order fields
72
+ def load_map(*map)
73
+ map.each do |instruction|
74
+ # We determine the function of the first element
75
+ head = instruction.first
76
+ case head
77
+ when Integer # >= Version condition: [ min_version, [map]]
78
+ load_map *instruction.drop(1) if version >= head
79
+
80
+ when Proc # Callable condition: [ condition, [map]]
81
+ load_map *instruction.drop(1) if head.call
82
+
83
+ when true # Pre-condition already succeeded!
84
+ load_map *instruction.drop(1)
85
+
86
+ when nil, false # Pre-condition already failed! Do nothing...
87
+
88
+ when Symbol # Normal map
89
+ group, name, type, block =
90
+ if instruction[2].nil? || instruction[2].is_a?(Proc) # lambda's are Proc's
91
+ [nil] + instruction # No group, [ :name, :type, (:block) ]
92
+ else
93
+ instruction # [ :group, :name, :type, (:block)]
94
+ end
95
+ begin
96
+ data = @buffer.__send__("read_#{type}", &block)
97
+ rescue IB::LoadError, NoMethodError => e
98
+ error "Reading #{self.class}: #{e.class}: #{e.message} --> Instruction: #{name}" , :reader, false
99
+ end
100
+ # debug puts data.inspect
101
+ if group
102
+ @data[group] ||= {}
103
+ @data[group][name] = data
104
+ else
105
+ @data[name] = data
106
+ end
107
+ else
108
+ error "Unrecognized instruction #{instruction}"
109
+ end
110
+ end
111
+ end
112
+
113
+ end # class AbstractMessage
114
+ end # module Incoming
115
+ end # module Messages
116
+ end # module IB
@@ -0,0 +1,78 @@
1
+ module IB
2
+ module Messages
3
+ module Incoming
4
+
5
+ AccountUpdateTime = def_message 8, [:time_stamp, :string]
6
+
7
+ ManagedAccounts =
8
+ def_message 15, [:accounts_list, :string]
9
+
10
+ class ManagedAccounts
11
+ def accounts
12
+ accounts_list.split(',').map{|a| Account.new account: a}
13
+ end
14
+
15
+ def to_human
16
+ "< ManagedAccounts: #{accounts.map(&:account).join(" - ")}>"
17
+ end
18
+ end
19
+
20
+ # Receives previously requested FA configuration information from TWS.
21
+ ReceiveFA =
22
+ def_message 16, [:type, :int], # type of Financial Advisor configuration data
23
+ # being received from TWS. Valid values include:
24
+ # 1 = GROUPS, 2 = PROFILE, 3 = ACCOUNT ALIASES
25
+ [:xml, :xml] # XML string with requested FA configuration information.
26
+
27
+ class ReceiveFA
28
+ def accounts
29
+ xml[:ListOfAccountAliases][:AccountAlias].map{|x| Account.new x }
30
+ end
31
+
32
+ def to_human
33
+ "<FA: #{accounts.map(&:to_human).join(" - ")}>"
34
+ end
35
+ end
36
+
37
+
38
+
39
+ class AccountMessage < AbstractMessage
40
+ def account_value
41
+ @account_value = IB::AccountValue.new @data[:account_value]
42
+ end
43
+ def account_name
44
+ @account_name = @data[:account]
45
+ end
46
+
47
+ def to_human
48
+ "<AccountValue: #{account_name}, #{account_value}"
49
+ end
50
+ end
51
+ AccountSummary = def_message(63, AccountMessage,
52
+ [:request_id, :int],
53
+ [ :account, :string ],
54
+ [:account_value, :key, :symbol],
55
+ [:account_value, :value, :string],
56
+ [:account_value, :currency, :string]
57
+ )
58
+ AccountSummaryEnd = def_message(64)
59
+
60
+ AccountValue = def_message([6, 2], AccountMessage,
61
+ [:account_value, :key, :symbol],
62
+ [:account_value, :value, :string],
63
+ [:account_value, :currency, :string],
64
+ [:account, :string])
65
+
66
+
67
+ AccountUpdatesMulti = def_message( 73,
68
+ [ :request_id, :int ],
69
+ [ :account , :string ],
70
+ [ :model, :string ],
71
+ [ :key , :string ],
72
+ [ :value , :decimal],
73
+ [ :currency, :string ])
74
+
75
+ AccountUpdatesMultiEnd = def_message 74
76
+ end # module Incoming
77
+ end # module Messages
78
+ end # module IB
@@ -0,0 +1,34 @@
1
+ module IB
2
+ module Messages
3
+ module Incoming
4
+
5
+ # Called Error in Java code, but in fact this type of messages also
6
+ # deliver system alerts and additional (non-error) info from TWS.
7
+ ErrorMessage = Error = Alert = def_message([4, 2],
8
+ [:error_id, :int],
9
+ [:code, :int],
10
+ [:message, :string])
11
+ class Alert
12
+ # Is it an Error message?
13
+ def error?
14
+ code < 1000
15
+ end
16
+
17
+ # Is it a System message?
18
+ def system?
19
+ code > 1000 && code < 2000
20
+ end
21
+
22
+ # Is it a Warning message?
23
+ def warning?
24
+ code > 2000
25
+ end
26
+
27
+ def to_human
28
+ "TWS #{ error? ? 'Error' : system? ? 'System' : 'Warning'} #{code}: #{message}"
29
+ end
30
+ end # class Alert
31
+
32
+ end # module Incoming
33
+ end # module Messages
34
+ end # module IB
@@ -0,0 +1,102 @@
1
+ module IB
2
+ module Messages
3
+ module Incoming
4
+ module ContractAccessors
5
+
6
+ end
7
+
8
+ ContractDetails = ContractData =
9
+ def_message([10, [6, 8]],
10
+ [:request_id, :int], ## request id
11
+ [:contract, :symbol, :string], ## next the major contract-fields
12
+ [:contract, :sec_type, :string], ## are transmitted
13
+ [:contract, :last_trading_day, :date], ## difference to the array.get_contract
14
+ [:contract, :strike, :decimal], ## method: con_id is transmitted
15
+ [:contract, :right, :string], ## AFTER the main fields
16
+ [:contract, :exchange, :string], ##
17
+ [:contract, :currency, :string], ## thus we have to read the fields separately
18
+ [:contract, :local_symbol, :string],
19
+ [:contract_detail, :market_name, :string], ## extended
20
+ [:contract, :trading_class, :string], ## new Version 8
21
+ [:contract, :con_id, :int],
22
+ [:contract_detail, :min_tick, :decimal],
23
+ [:contract_detail, :md_size_multiplier, :int],
24
+ [:contract, :multiplier, :int],
25
+ [:contract_detail, :order_types, :string],
26
+ [:contract_detail, :valid_exchanges, :string],
27
+ [:contract_detail, :price_magnifier, :int],
28
+ [:contract_detail, :under_con_id, :int],
29
+ [:contract_detail, :long_name, :string],
30
+ [:contract, :primary_exchange, :string],
31
+ [:contract_detail, :contract_month, :string],
32
+ [:contract_detail, :industry, :string],
33
+ [:contract_detail, :category, :string],
34
+ [:contract_detail, :subcategory, :string],
35
+ [:contract_detail, :time_zone, :string],
36
+ [:contract_detail, :trading_hours, :string],
37
+ [:contract_detail, :liquid_hours, :string],
38
+ [:contract_detail, :ev_rule, :decimal],
39
+ [:contract_detail, :ev_multipler, :string],
40
+ [:contract_detail, :sec_id_list,:hash],
41
+ [:contract_detail, :agg_group, :int ],
42
+ [:contract_detail, :under_symbol, :string ],
43
+ [:contract_detail, :under_sec_type, :string ],
44
+ [:contract_detail, :market_rule_ids, :string ],
45
+ [:contract_detail, :real_expiration_date, :date ]
46
+ )
47
+ #
48
+ #
49
+ class ContractData
50
+ using IBSupport # defines tws-method for Array (socket.rb)
51
+ def contract
52
+ @contract = IB::Contract.build @data[:contract].merge(:contract_detail => contract_detail)
53
+ end
54
+
55
+ def contract_detail
56
+ @contract_detail = IB::ContractDetail.new @data[:contract_detail]
57
+ end
58
+
59
+ alias contract_details contract_detail
60
+
61
+ def to_human
62
+ "<Contract #{contract.to_human} #{contract_detail.to_human}>"
63
+ end
64
+
65
+ end # ContractData
66
+
67
+ BondContractData =
68
+ def_message [18, [4, 6]], ContractData,
69
+ [:request_id, :int],
70
+ [:contract, :symbol, :string],
71
+ [:contract, :sec_type, :string],
72
+ [:contract_detail, :cusip, :string],
73
+ [:contract_detail, :coupon, :decimal],
74
+ [:contract_detail, :maturity, :string],
75
+ [:contract_detail, :issue_date, :string],
76
+ [:contract_detail, :ratings, :string],
77
+ [:contract_detail, :bond_type, :string],
78
+ [:contract_detail, :coupon_type, :string],
79
+ [:contract_detail, :convertible, :boolean],
80
+ [:contract_detail, :callable, :boolean],
81
+ [:contract_detail, :puttable, :boolean],
82
+ [:contract_detail, :desc_append, :string],
83
+ [:contract, :exchange, :string],
84
+ [:contract, :currency, :string],
85
+ [:contract_detail, :market_name, :string], # extended
86
+ [:contract_detail, :trading_class, :string],
87
+ [:contract, :con_id, :int],
88
+ [:contract_detail, :min_tick, :decimal],
89
+ [:contract_detail, :order_types, :string],
90
+ [:contract_detail, :valid_exchanges, :string],
91
+ [:contract_detail, :valid_next_option_date, :string],
92
+ [:contract_detail, :valid_next_option_type, :string],
93
+ [:contract_detail, :valid_next_option_partial, :string],
94
+ [:contract_detail, :notes, :string],
95
+ [:contract_detail, :long_name, :string],
96
+ [:contract_detail, :ev_rule, :decimal],
97
+ [:contract_detail, :ev_multipler, :string],
98
+ [:sec_id_list_count, :int]
99
+
100
+ end # module Incoming
101
+ end # module Messages
102
+ end # module IB
@@ -0,0 +1,23 @@
1
+ module IB
2
+ module Messages
3
+ module Incoming
4
+
5
+ # The server sends this message upon accepting a Delta-Neutral DN RFQ
6
+ # - see API Reference p. 26
7
+ DeltaNeutralValidation = def_message 56,
8
+ [:request_id, :int],
9
+ [:underlying, :con_id, :int],
10
+ [:underlying, :delta, :decimal],
11
+ [:underlying, :price, :decimal]
12
+ class DeltaNeutralValidation
13
+ def underlying
14
+ @underlying = IB::Underlying.new @data[:underlying]
15
+ end
16
+
17
+ alias under_comp underlying
18
+
19
+ end # DeltaNeutralValidation
20
+
21
+ end # module Incoming
22
+ end # module Messages
23
+ end # module IB
@@ -0,0 +1,50 @@
1
+ module IB
2
+ module Messages
3
+ module Incoming
4
+
5
+ ExecutionData =
6
+ def_message [11, 0] , # [8, 9]],
7
+ # The reqID that was specified previously in the call to reqExecution()
8
+ [:request_id, :int],
9
+ [:execution, :local_id, :int],
10
+ [:contract, :contract],
11
+ [:execution, :exec_id, :string], # Weird format
12
+ [:execution, :time, :datetime],
13
+ [:execution, :account_name, :string],
14
+ [:execution, :exchange, :string],
15
+ [:execution, :side, :string],
16
+ [:execution, :quantity, :decimal],
17
+ [:execution, :price, :decimal],
18
+ [:execution, :perm_id, :int],
19
+ [:execution, :client_id, :int],
20
+ [:execution, :liquidation, :int],
21
+ [:execution, :cumulative_quantity, :int],
22
+ [:execution, :average_price, :decimal],
23
+ [:execution, :order_ref, :string],
24
+ [:execution, :ev_rule, :string],
25
+ [:execution, :ev_multiplier, :decimal],
26
+ [:execution, :model_code, :string],
27
+ [:execution, :last_liquidity, :int]
28
+
29
+ class ExecutionData
30
+
31
+ def load
32
+ simple_load
33
+ end
34
+
35
+ def contract
36
+ @contract = IB::Contract.build @data[:contract]
37
+ end
38
+
39
+ def execution
40
+ @execution = IB::Execution.new @data[:execution]
41
+ end
42
+
43
+ def to_human
44
+ "<ExecutionData #{request_id}: #{contract.to_human}, #{execution}>"
45
+ end
46
+
47
+ end # ExecutionData
48
+ end # module Incoming
49
+ end # module Messages
50
+ end # module IB
@@ -0,0 +1,84 @@
1
+ module IB
2
+ module Messages
3
+ module Incoming
4
+
5
+ # HistoricalData contains following @data:
6
+ #
7
+ # _General_:
8
+ #
9
+ # - request_id - The ID of the request to which this is responding
10
+ # - count - Number of Historical data points returned (size of :results).
11
+ # - results - an Array of Historical Data Bars
12
+ # - start_date - beginning of returned Historical data period
13
+ # - end_date - end of returned Historical data period
14
+ #
15
+ # Each returned Bar in @data[:results] Array contains this data:
16
+ # - date - The date-time stamp of the start of the bar. The format is set to sec since EPOCHE
17
+ # in outgoing/bar_requests ReqHistoricalData.
18
+ # - open - The bar opening price.
19
+ # - high - The high price during the time covered by the bar.
20
+ # - low - The low price during the time covered by the bar.
21
+ # - close - The bar closing price.
22
+ # - volume - The volume during the time covered by the bar.
23
+ # - trades - When TRADES historical data is returned, represents number of trades
24
+ # that occurred during the time period the bar covers
25
+ # - wap - The weighted average price during the time covered by the bar.
26
+
27
+
28
+ HistoricalData = def_message [17,0],
29
+ [:request_id, :int],
30
+ [:start_date, :datetime],
31
+ [:end_date, :datetime],
32
+ [:count, :int]
33
+ class HistoricalData
34
+ attr_accessor :results
35
+ using IBSupport # extended Array-Class from abstract_message
36
+
37
+ def load
38
+ super
39
+
40
+ @results = Array.new(@data[:count]) do |_|
41
+ IB::Bar.new :time => buffer.read_int_date, # conversion of epoche-time-integer to Dateime
42
+ # requires format_date in request to be "2"
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,
48
+ :volume => buffer.read_int,
49
+ :wap => buffer.read_decimal,
50
+ # :has_gaps => buffer.read_string, # only in ServerVersion < 124
51
+ :trades => buffer.read_int
52
+ end
53
+ end
54
+
55
+ def to_human
56
+ "<HistoricalData: #{request_id}, #{count} items, #{start_date} to #{end_date}>"
57
+ end
58
+ end # HistoricalData
59
+
60
+
61
+ HistogramData = def_message( [89,0],
62
+ [:request_id, :int],
63
+ [ :number_of_points , :int ]) do
64
+ # to human
65
+ "<HistogramData: #{request_id}, #{number_of_points} read>"
66
+ end
67
+
68
+ class HistogramData
69
+ attr_accessor :results
70
+ using IBSupport # extended Array-Class from abstract_message
71
+
72
+ def load
73
+ super
74
+
75
+ @results = Array.new(@data[:number_of_points]) do |_|
76
+ { price: buffer.read_decimal,
77
+ count: buffer.read_int }
78
+ end
79
+ end
80
+ end
81
+
82
+ end # module Incoming
83
+ end # module Messages
84
+ end # module IB