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,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