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,185 @@
1
+ require 'socket'
2
+ module IBSupport
3
+ refine Array do
4
+ def tws
5
+ if blank?
6
+ nil.tws
7
+ else
8
+ self.flatten.map( &:tws ).join # [ "", [] , nil].flatten -> ["", nil]
9
+ # elemets with empty array's are cut
10
+ # this is the desired behavior!
11
+ end
12
+ end
13
+ end
14
+ refine Symbol do
15
+ def tws
16
+ self.to_s.tws
17
+ end
18
+ end
19
+ refine String do
20
+ def tws
21
+ if empty?
22
+ IB::EOL
23
+ else
24
+ self[-1] == IB::EOL ? self : self+IB::EOL
25
+ end
26
+ end
27
+ end
28
+
29
+ refine Numeric do
30
+ def tws
31
+ self.to_s.tws
32
+ end
33
+ end
34
+
35
+ refine TrueClass do
36
+ def tws
37
+ 1.tws
38
+ end
39
+ end
40
+
41
+ refine FalseClass do
42
+ def tws
43
+ 0.tws
44
+ end
45
+ end
46
+
47
+ refine NilClass do
48
+ def tws
49
+ IB::EOL
50
+ end
51
+ end
52
+ end
53
+ module IB
54
+ # includes methods from IBSupport
55
+ # which adds a tws-method to
56
+ # - Array
57
+ # - Symbol
58
+ # - String
59
+ # - Numeric
60
+ # - TrueClass, FalseClass and NilClass
61
+ #
62
+ module PrepareData
63
+ using IBSupport
64
+ # First call the method #tws on the data-object
65
+ #
66
+ # Then transfom into an Array using the #Pack-Method
67
+ #
68
+ # The optional Block introduces a user-defined pattern to pack the data.
69
+ #
70
+ # Default is "Na*"
71
+ def prepare_message data
72
+ data = data.tws unless data.is_a?(String) && data[-1]== EOL
73
+ matrize = [data.size,data]
74
+ if block_given? # A user defined decoding-sequence is accepted via block
75
+ matrize.pack yield
76
+ else
77
+ matrize.pack "Na*"
78
+ end
79
+ end
80
+
81
+ # The received package is decoded. The parameter (msg) is an Array
82
+ #
83
+ # The protocol is simple: Every Element is treated as Character.
84
+ # Exception: The first Element determines the expected length.
85
+ #
86
+ # The decoded raw-message can further modified by the optional block.
87
+ #
88
+ # The default is to instantiate a Hash: message_id becomes the key.
89
+ # The Hash is returned
90
+ #
91
+ # If a block is provided, no Hash is build and the modified raw-message is returned
92
+ def decode_message msg
93
+ m = Hash.new
94
+ while not msg.blank?
95
+ # the first item is the length
96
+ size= msg[0..4].unpack("N").first
97
+ msg = msg[4..-1]
98
+ # followed by a sequence of characters
99
+ message = msg.unpack("A#{size}").first.split("\0")
100
+ if block_given?
101
+ yield message
102
+ else
103
+ m[message.shift.to_i] = message
104
+ end
105
+ msg = msg[size..-1]
106
+ end
107
+ return m unless block_given?
108
+ end
109
+
110
+ end
111
+
112
+ class IBSocket < TCPSocket
113
+ include PrepareData
114
+ using IBSupport
115
+
116
+ def initialising_handshake
117
+ v100_prefix = "API".tws.encode 'ascii'
118
+ v100_version = self.prepare_message Messages::SERVER_VERSION
119
+ write_data v100_prefix+v100_version
120
+ ## start tws-log
121
+ # [QO] INFO [JTS-SocketListener-49] - State: HEADER, IsAPI: UNKNOWN
122
+ # [QO] INFO [JTS-SocketListener-49] - State: STOP, IsAPI: YES
123
+ # [QO] INFO [JTS-SocketListener-49] - ArEServer: Adding 392382055 with id 2147483647
124
+ # [QO] INFO [JTS-SocketListener-49] - eServersChanged: 1
125
+ # [QO] INFO [JTS-EServerSocket-287] - [2147483647:136:136:1:0:0:0:SYS] Starting new conversation with client on 127.0.0.1
126
+ # [QO] INFO [JTS-EServerSocketNotifier-288] - Starting async queue thread
127
+ # [QO] INFO [JTS-EServerSocket-287] - [2147483647:136:136:1:0:0:0:SYS] Server version is 136
128
+ # [QO] INFO [JTS-EServerSocket-287] - [2147483647:136:136:1:0:0:0:SYS] Client version is 136
129
+ # [QO] INFO [JTS-EServerSocket-287] - [2147483647:136:136:1:0:0:0:SYS] is 3rdParty true
130
+ ## end tws-log
131
+ end
132
+
133
+
134
+ def read_string
135
+ string = self.gets(EOL)
136
+
137
+ until string
138
+ # Silently ignores nils
139
+ string = self.gets(EOL)
140
+ sleep 0.1
141
+ end
142
+
143
+ string.chomp
144
+ end
145
+
146
+
147
+ # Sends null terminated data string into socket
148
+ def write_data data
149
+ self.syswrite data.tws
150
+ end
151
+
152
+ # send the message (containing several instructions) to the socket,
153
+ # calls prepare_message to convert data-elements into NULL-terminated strings
154
+ def send_messages *data
155
+ self.syswrite prepare_message(data)
156
+ rescue Errno::ECONNRESET => e
157
+ Connection.logger.error{ "Data not accepted by IB \n
158
+ #{data.inspect} \n
159
+ Backtrace:\n "}
160
+ Connection.logger.error e.backtrace
161
+ end
162
+
163
+ def recieve_messages
164
+ begin
165
+ complete_message_buffer = []
166
+ begin
167
+ # this is the blocking version of recv
168
+ buffer = self.recvfrom(4096)[0]
169
+ # STDOUT.puts "BUFFER:: #{buffer.inspect}"
170
+ complete_message_buffer << buffer
171
+
172
+ end while buffer.size == 4096
173
+ complete_message_buffer.join('')
174
+ rescue Errno::ECONNRESET => e
175
+ Connection.logger.error{ "Data Buffer is not filling \n
176
+ The Buffer: #{buffer.inspect} \n
177
+ Backtrace:\n
178
+ #{e.backtrace.join("\n") } " }
179
+ Kernel.exit
180
+ end
181
+ end
182
+
183
+ end # class IBSocket
184
+
185
+ end # module IB
@@ -0,0 +1,160 @@
1
+
2
+ module IBSupport
3
+ refine Array do
4
+
5
+ def zero?
6
+ false
7
+ end
8
+ # Returns the integer.
9
+ # retuns nil otherwise or if no element is left on the stack
10
+ def read_int
11
+ i= self.shift rescue nil
12
+ i = i.to_i unless i.blank? # this includes conversion of string to zero(0)
13
+ i.is_a?( Integer ) ? i : nil
14
+ end
15
+
16
+ def read_float
17
+ i= self.shift rescue nil
18
+ i = i.to_f unless i.blank?
19
+
20
+ end
21
+ def read_decimal
22
+ i= self.shift rescue nil
23
+ i = i.to_d unless i.blank?
24
+ i.is_a?(Numeric) && i < IB::TWS_MAX ? i : nil # return nil, if a very large number is transmitted
25
+ end
26
+
27
+ alias read_decimal_max read_decimal
28
+
29
+ ## Values -1 and below indicate: Not computed (TickOptionComputation)
30
+ def read_decimal_limit_1
31
+ i= read_decimal
32
+ i <= -1 ? nil : i
33
+ end
34
+
35
+ ## Values -2 and below indicate: Not computed (TickOptionComputation)
36
+ def read_decimal_limit_2
37
+ i= read_decimal
38
+ i <= -2 ? nil : i
39
+ end
40
+
41
+
42
+ def read_string
43
+ self.shift rescue ""
44
+ end
45
+ ## reads a string and proofs if NULL == IB::TWS_MAX is present.
46
+ ## in that case: returns nil. otherwise: returns the string
47
+ def read_string_not_null
48
+ r = read_string
49
+ rd = r.to_d unless r.blank?
50
+ rd.is_a?(Numeric) && rd >= IB::TWS_MAX ? nil : r
51
+ end
52
+
53
+ def read_symbol
54
+ read_string.to_sym
55
+ end
56
+
57
+ # convert xml into a hash
58
+ def read_xml
59
+ Ox.load( read_string(), mode: :hash_no_attrs)
60
+ end
61
+
62
+
63
+ def read_int_date
64
+ t= read_int
65
+ s= Time.at(t)
66
+ # s.year == 1970 --> data is most likely a date-string
67
+ s.year == 1970 ? Date.parse(t.to_s) : s
68
+ end
69
+
70
+ def read_parse_date
71
+ Time.parse read_string
72
+ end
73
+
74
+ def read_boolean
75
+
76
+ v = self.shift rescue nil
77
+ case v
78
+ when "1"
79
+ true
80
+ when "0"
81
+ false
82
+ else nil
83
+ end
84
+ end
85
+
86
+
87
+ def read_datetime
88
+ the_string = read_string
89
+ the_string.blank? ? nil : DateTime.parse(the_string)
90
+ end
91
+
92
+ def read_date
93
+ the_string = read_string
94
+ the_string.blank? ? nil : Date.parse(the_string)
95
+ end
96
+ # def read_array
97
+ # count = read_int
98
+ # end
99
+
100
+ ## originally provided in socket.rb
101
+ # # Returns loaded Array or [] if count was 0#
102
+ #
103
+ # Without providing a Block, the elements are treated as string
104
+ def read_array hashmode:false, &block
105
+ count = read_int
106
+ case count
107
+ when 0
108
+ []
109
+ when nil
110
+ nil
111
+ else
112
+ count= count + count if hashmode
113
+ if block_given?
114
+ Array.new(count, &block)
115
+ else
116
+ Array.new( count ){ read_string }
117
+ end
118
+ end
119
+ end
120
+ #
121
+ # Returns a hash
122
+ # Expected Buffer-Format:
123
+ # count (of Hash-elements)
124
+ # count* key|Value
125
+ # Key's are transformed to symbols, values are treated as string
126
+ def read_hash
127
+ tags = read_array( hashmode: true ) # { |_| [read_string, read_string] }
128
+ result = if tags.nil? || tags.flatten.empty?
129
+ tags
130
+ else
131
+ interim = if tags.size.modulo(2).zero?
132
+ Hash[*tags.flatten]
133
+ else
134
+ Hash[*tags[0..-2].flatten] # omit the last element
135
+ end
136
+ # symbolize Hash
137
+ Hash[interim.map { |k, v| [k.to_sym, v] unless k.nil? }.compact]
138
+ end
139
+ end
140
+ #
141
+
142
+ def read_contract # read a standard contract and return als hash
143
+ { con_id: read_int,
144
+ symbol: read_string,
145
+ sec_type: read_string,
146
+ expiry: read_string,
147
+ strike: read_decimal,
148
+ right: read_string,
149
+ multiplier: read_int,
150
+ exchange: read_string,
151
+ currency: read_string,
152
+ local_symbol: read_string,
153
+ trading_class: read_string } # new Version 8
154
+
155
+ end
156
+
157
+
158
+ alias read_bool read_boolean
159
+ end
160
+ end
@@ -0,0 +1,6 @@
1
+ require 'pathname'
2
+
3
+ module IB
4
+ VERSION_FILE = Pathname.new(__FILE__).dirname + '../../VERSION' # :nodoc:
5
+ VERSION = VERSION_FILE.exist? ? VERSION_FILE.read.strip : nil
6
+ end
@@ -0,0 +1,85 @@
1
+ module IB
2
+ class Account < IB::Model
3
+ include BaseProperties
4
+ # include Redis::Objects
5
+ # attr_accessible :alias, :account, :connected
6
+
7
+ prop :account, # String
8
+ :alias, #
9
+ :type,
10
+ :last_updated,
11
+ :connected => :bool
12
+
13
+ # redis_id_field :account
14
+ # value :my_alias
15
+ # value :the_account
16
+ # value :active
17
+
18
+
19
+ validates_format_of :account, :with => /\A[D]?[UF]{1}\d{5,8}\z/ , :message => 'should be (X)X00000'
20
+
21
+ # in tableless mode the scope is ignored
22
+
23
+ has_many :account_values
24
+ has_many :portfolio_values
25
+ has_many :contracts
26
+ has_many :orders
27
+ has_many :focuses
28
+
29
+ def default_attributes
30
+ super.merge account: 'X000000'
31
+ super.merge alias: ''
32
+ super.merge type: 'Account'
33
+ super.merge connected: false
34
+ end
35
+
36
+ def logger #nodoc#
37
+ Connection.logger
38
+ end
39
+
40
+ # Setze Account connect/disconnect und undate!
41
+ def connect!
42
+ update_attribute :connected , true
43
+ end
44
+ def disconnect!
45
+ update_attribute :connected , false
46
+ end
47
+
48
+ def print_type #nodoc#
49
+ (test_environment? ? "demo_" : "") + ( user? ? "user" : "advisor" )
50
+ end
51
+
52
+ def advisor?
53
+ !!(type =~ /Advisor/ || account =~ /\A[D]?[F]{1}/)
54
+ end
55
+
56
+ def user?
57
+ !!(type =~ /User/ || account =~ /\A[D]?[U]{1}/)
58
+ end
59
+
60
+ def test_environment?
61
+ !!(account =~ /^[D]{1}/)
62
+ end
63
+
64
+ def == other
65
+ super(other) ||
66
+ other.is_a?(self.class) && account == other.account
67
+ end
68
+
69
+ def to_human
70
+ a = if self.alias.present? && self.alias != account
71
+ " alias: "+ self.alias
72
+ else
73
+ ""
74
+ end
75
+ "<#{print_type} #{account}#{a}>"
76
+ end
77
+
78
+ def name #nodoc#
79
+ self.alias.present? ? self.alias : account
80
+ end
81
+
82
+ # alias :id :account
83
+ end # class
84
+
85
+ end # module
@@ -0,0 +1,33 @@
1
+ module IB
2
+ # Instantiate with a Hash of attributes, to be auto-set via initialize in Model.
3
+ class AccountValue < IB::Model
4
+ include BaseProperties
5
+
6
+ belongs_to :account
7
+
8
+ prop :key,
9
+ :value,
10
+ :currency
11
+
12
+
13
+ # comparison
14
+ def == other
15
+ super(other) ||
16
+ other.is_a?(self.class) &&
17
+ key == other.key &&
18
+ currency == other.currency &&
19
+ value == other.value
20
+ end
21
+ def default_attributes
22
+ super.merge key: 'AccountValue',
23
+ value: 0,
24
+ currency: 'USD'
25
+ end
26
+
27
+ def to_human
28
+ "<#{key}=#{value} #{currency}>"
29
+ end
30
+
31
+ alias to_s to_human
32
+ end # class
33
+ end # module IB