ib-api 972.0

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