ib-api 10.33.1

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 (161) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +52 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CLAUDE.md +131 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +17 -0
  8. data/Gemfile.lock +120 -0
  9. data/Guardfile +24 -0
  10. data/LICENSE +674 -0
  11. data/LLM_GUIDE.md +388 -0
  12. data/README.md +114 -0
  13. data/Rakefile +11 -0
  14. data/VERSION +1 -0
  15. data/api.gemspec +50 -0
  16. data/bin/console +96 -0
  17. data/bin/console.yml +3 -0
  18. data/bin/setup +8 -0
  19. data/bin/simple +91 -0
  20. data/changelog.md +32 -0
  21. data/conditions/ib/execution_condition.rb +31 -0
  22. data/conditions/ib/margin_condition.rb +28 -0
  23. data/conditions/ib/order_condition.rb +29 -0
  24. data/conditions/ib/percent_change_condition.rb +34 -0
  25. data/conditions/ib/price_condition.rb +44 -0
  26. data/conditions/ib/time_condition.rb +42 -0
  27. data/conditions/ib/volume_condition.rb +36 -0
  28. data/lib/class_extensions.rb +167 -0
  29. data/lib/ib/base.rb +109 -0
  30. data/lib/ib/base_properties.rb +178 -0
  31. data/lib/ib/connection.rb +573 -0
  32. data/lib/ib/constants.rb +402 -0
  33. data/lib/ib/contract.rb +30 -0
  34. data/lib/ib/errors.rb +52 -0
  35. data/lib/ib/messages/abstract_message.rb +68 -0
  36. data/lib/ib/messages/incoming/abstract_message.rb +116 -0
  37. data/lib/ib/messages/incoming/abstract_tick.rb +25 -0
  38. data/lib/ib/messages/incoming/account_message.rb +26 -0
  39. data/lib/ib/messages/incoming/alert.rb +34 -0
  40. data/lib/ib/messages/incoming/contract_data.rb +105 -0
  41. data/lib/ib/messages/incoming/contract_message.rb +13 -0
  42. data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
  43. data/lib/ib/messages/incoming/execution_data.rb +50 -0
  44. data/lib/ib/messages/incoming/histogram_data.rb +30 -0
  45. data/lib/ib/messages/incoming/historical_data.rb +65 -0
  46. data/lib/ib/messages/incoming/historical_data_update.rb +50 -0
  47. data/lib/ib/messages/incoming/managed_accounts.rb +21 -0
  48. data/lib/ib/messages/incoming/market_depth.rb +34 -0
  49. data/lib/ib/messages/incoming/market_depth_l2.rb +15 -0
  50. data/lib/ib/messages/incoming/next_valid_id.rb +19 -0
  51. data/lib/ib/messages/incoming/open_order.rb +290 -0
  52. data/lib/ib/messages/incoming/order_status.rb +85 -0
  53. data/lib/ib/messages/incoming/portfolio_value.rb +47 -0
  54. data/lib/ib/messages/incoming/position_data.rb +21 -0
  55. data/lib/ib/messages/incoming/positions_multi.rb +15 -0
  56. data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
  57. data/lib/ib/messages/incoming/receive_fa.rb +30 -0
  58. data/lib/ib/messages/incoming/scanner_data.rb +54 -0
  59. data/lib/ib/messages/incoming/tick_by_tick.rb +77 -0
  60. data/lib/ib/messages/incoming/tick_efp.rb +18 -0
  61. data/lib/ib/messages/incoming/tick_generic.rb +12 -0
  62. data/lib/ib/messages/incoming/tick_option.rb +60 -0
  63. data/lib/ib/messages/incoming/tick_price.rb +60 -0
  64. data/lib/ib/messages/incoming/tick_size.rb +55 -0
  65. data/lib/ib/messages/incoming/tick_string.rb +13 -0
  66. data/lib/ib/messages/incoming.rb +292 -0
  67. data/lib/ib/messages/outgoing/abstract_message.rb +84 -0
  68. data/lib/ib/messages/outgoing/bar_request_message.rb +247 -0
  69. data/lib/ib/messages/outgoing/new-place-order.rb +193 -0
  70. data/lib/ib/messages/outgoing/old-place-order.rb +147 -0
  71. data/lib/ib/messages/outgoing/place_order.rb +149 -0
  72. data/lib/ib/messages/outgoing/request_account_summary.rb +79 -0
  73. data/lib/ib/messages/outgoing/request_historical_data.rb +182 -0
  74. data/lib/ib/messages/outgoing/request_market_data.rb +102 -0
  75. data/lib/ib/messages/outgoing/request_market_depth.rb +57 -0
  76. data/lib/ib/messages/outgoing/request_real_time_bars.rb +48 -0
  77. data/lib/ib/messages/outgoing/request_scanner_subscription.rb +73 -0
  78. data/lib/ib/messages/outgoing/request_tick_by_tick_data.rb +21 -0
  79. data/lib/ib/messages/outgoing.rb +410 -0
  80. data/lib/ib/messages.rb +139 -0
  81. data/lib/ib/order_condition.rb +26 -0
  82. data/lib/ib/plugins.rb +27 -0
  83. data/lib/ib/prepare_data.rb +61 -0
  84. data/lib/ib/raw_message_parser.rb +99 -0
  85. data/lib/ib/socket.rb +83 -0
  86. data/lib/ib/support.rb +236 -0
  87. data/lib/ib/version.rb +6 -0
  88. data/lib/ib-api.rb +44 -0
  89. data/lib/server_versions.rb +145 -0
  90. data/lib/support/array_function.rb +28 -0
  91. data/lib/support/logging.rb +45 -0
  92. data/models/ib/account.rb +72 -0
  93. data/models/ib/account_value.rb +33 -0
  94. data/models/ib/bag.rb +55 -0
  95. data/models/ib/bar.rb +31 -0
  96. data/models/ib/combo_leg.rb +127 -0
  97. data/models/ib/contract.rb +411 -0
  98. data/models/ib/contract_detail.rb +118 -0
  99. data/models/ib/execution.rb +67 -0
  100. data/models/ib/forex.rb +12 -0
  101. data/models/ib/future.rb +64 -0
  102. data/models/ib/index.rb +14 -0
  103. data/models/ib/option.rb +149 -0
  104. data/models/ib/option_detail.rb +84 -0
  105. data/models/ib/order.rb +720 -0
  106. data/models/ib/order_state.rb +155 -0
  107. data/models/ib/portfolio_value.rb +86 -0
  108. data/models/ib/spread.rb +176 -0
  109. data/models/ib/stock.rb +25 -0
  110. data/models/ib/underlying.rb +32 -0
  111. data/plugins/ib/advanced-account.rb +442 -0
  112. data/plugins/ib/alerts/base-alert.rb +125 -0
  113. data/plugins/ib/alerts/gateway-alerts.rb +15 -0
  114. data/plugins/ib/alerts/order-alerts.rb +73 -0
  115. data/plugins/ib/auto-adjust.rb +0 -0
  116. data/plugins/ib/connection-tools.rb +122 -0
  117. data/plugins/ib/eod.rb +326 -0
  118. data/plugins/ib/greeks.rb +102 -0
  119. data/plugins/ib/managed-accounts.rb +274 -0
  120. data/plugins/ib/market-price.rb +150 -0
  121. data/plugins/ib/option-chain.rb +167 -0
  122. data/plugins/ib/order-flow.rb +157 -0
  123. data/plugins/ib/order-prototypes/abstract.rb +67 -0
  124. data/plugins/ib/order-prototypes/adaptive.rb +40 -0
  125. data/plugins/ib/order-prototypes/all-in-one.rb +46 -0
  126. data/plugins/ib/order-prototypes/combo.rb +46 -0
  127. data/plugins/ib/order-prototypes/forex.rb +40 -0
  128. data/plugins/ib/order-prototypes/limit.rb +193 -0
  129. data/plugins/ib/order-prototypes/market.rb +116 -0
  130. data/plugins/ib/order-prototypes/pegged.rb +169 -0
  131. data/plugins/ib/order-prototypes/premarket.rb +31 -0
  132. data/plugins/ib/order-prototypes/stop.rb +202 -0
  133. data/plugins/ib/order-prototypes/volatility.rb +39 -0
  134. data/plugins/ib/order-prototypes.rb +118 -0
  135. data/plugins/ib/probability-of-expiring.rb +109 -0
  136. data/plugins/ib/process-orders.rb +155 -0
  137. data/plugins/ib/roll.rb +86 -0
  138. data/plugins/ib/spread-prototypes/butterfly.rb +77 -0
  139. data/plugins/ib/spread-prototypes/calendar.rb +97 -0
  140. data/plugins/ib/spread-prototypes/stock-spread.rb +56 -0
  141. data/plugins/ib/spread-prototypes/straddle.rb +70 -0
  142. data/plugins/ib/spread-prototypes/strangle.rb +93 -0
  143. data/plugins/ib/spread-prototypes/vertical.rb +83 -0
  144. data/plugins/ib/spread-prototypes.rb +70 -0
  145. data/plugins/ib/symbols/abstract.rb +136 -0
  146. data/plugins/ib/symbols/bonds.rb +28 -0
  147. data/plugins/ib/symbols/cfd.rb +19 -0
  148. data/plugins/ib/symbols/combo.rb +46 -0
  149. data/plugins/ib/symbols/commodity.rb +17 -0
  150. data/plugins/ib/symbols/forex.rb +41 -0
  151. data/plugins/ib/symbols/futures.rb +127 -0
  152. data/plugins/ib/symbols/index.rb +43 -0
  153. data/plugins/ib/symbols/options.rb +99 -0
  154. data/plugins/ib/symbols/stocks.rb +44 -0
  155. data/plugins/ib/symbols/version.rb +5 -0
  156. data/plugins/ib/symbols.rb +118 -0
  157. data/plugins/ib/verify.rb +226 -0
  158. data/symbols/w20.yml +210 -0
  159. data/t.txt +20 -0
  160. data/update.md +71 -0
  161. metadata +327 -0
data/lib/ib/plugins.rb ADDED
@@ -0,0 +1,27 @@
1
+ module IB
2
+ module Plugins
3
+ def activate_plugin *names
4
+ root= Pathname(__dir__).parent.parent
5
+ names.map{|y| y.to_s.gsub("_","-")}.each do |n|
6
+ unless @plugins.include? n
7
+ # root= base directory of the ib-api source
8
+ # plugins are defined in ib-api/plugins/ib
9
+ filename = root.join( "plugins", "ib", n+".rb" )
10
+ if filename.exist?
11
+ if require filename
12
+ @plugins << n
13
+ true # return value
14
+ else
15
+ error "Could not load Plugin `#{n}` --> #{filename} "
16
+ end
17
+ else
18
+ error "Plugin `#{n}` not found in `plugins/ib/`"
19
+ nil
20
+ end
21
+ else
22
+ IB::Connection.logger.debug "Already activated plugin #{n}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,61 @@
1
+ module IB
2
+ # includes methods from IB:.Support
3
+ # which adds a tws-method to
4
+ # - Array
5
+ # - Symbol
6
+ # - String
7
+ # - Numeric
8
+ # - TrueClass, FalseClass and NilClass
9
+ #
10
+ module PrepareData
11
+ using IB::Support
12
+ # First call the method #tws on the data-object
13
+ #
14
+ # Then transfom into an Array using the #Pack-Method
15
+ #
16
+ # The optional Block introduces a user-defined pattern to pack the data.
17
+ #
18
+ # Default is "Na*"
19
+ def prepare_message data
20
+ data = data.tws unless data.is_a?(String) && data[-1]== EOL
21
+ matrize = [data.size,data]
22
+ if block_given? # A user defined decoding-sequence is accepted via block
23
+ matrize.pack yield
24
+ else
25
+ matrize.pack "Na*"
26
+ end
27
+ end
28
+
29
+ # The received package is decoded. The parameter (msg) is an Array
30
+ #
31
+ # The protocol is simple: Every Element is treated as Character.
32
+ # Exception: The first Element determines the expected length.
33
+ #
34
+ # The decoded raw-message can further modified by the optional block.
35
+ #
36
+ # The default is to instantiate a Hash: message_id becomes the key.
37
+ # The Hash is returned
38
+ #
39
+ # If a block is provided, no Hash is build and the modified raw-message is returned
40
+ def decode_message msg
41
+ m = Hash.new
42
+ while not msg.blank?
43
+ # the first item is the length
44
+ size = msg[ 0 .. 4 ].unpack( "N" ).first
45
+ msg = msg[ 4 .. -1 ]
46
+ # followed by a sequence of characters
47
+ message = msg.unpack( "A#{size}" ).first.split( "\0" )
48
+ # DEBUG display raw decoded message on STDOUT
49
+ # STDOUT::puts "message: #{message}"
50
+ if block_given?
51
+ yield message
52
+ else
53
+ m[ message.shift.to_i ] = message
54
+ end
55
+ msg = msg[ size .. -1 ]
56
+ end
57
+ return m unless m == {}
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IB
4
+ # Convert data passed in from a TCP socket stream, and convert into raw messages. The messages
5
+ class RawMessageParser
6
+ HEADER_LNGTH = 4
7
+ def initialize socket
8
+ @socket = socket
9
+ @data = String.new
10
+ end
11
+
12
+ def each
13
+ append_new_data
14
+
15
+ while valid_data?
16
+ # puts "looping: #{@data.inspect}"
17
+
18
+ length = next_msg_length
19
+ validate_data_header(length)
20
+
21
+ raw = grab_message(length)
22
+ validate_message_footer(raw, length)
23
+ msg = parse_message(raw, length)
24
+ remove_message
25
+ yield msg
26
+ end
27
+ end
28
+
29
+ def valid_data?
30
+ # Make sure message length is available
31
+ return false unless length_data?
32
+
33
+ # Based on the length, do we have
34
+ # enough data to process a full
35
+ # message?
36
+ return false unless enough_data?
37
+
38
+ true
39
+ end
40
+
41
+ # extract message and convert to
42
+ # an array split by null characters.
43
+ def grab_message(length)
44
+ @data.byteslice(HEADER_LNGTH, length)
45
+ end
46
+
47
+ def parse_message(raw, length)
48
+ raw.unpack1("A#{length}").split("\0")
49
+ end
50
+
51
+ def remove_message
52
+ length = next_msg_length
53
+ leftovers = @data.byteslice( length + HEADER_LNGTH..-1 )
54
+ @data = if leftovers.nil?
55
+ String.new
56
+ else
57
+ leftovers
58
+ end
59
+ end
60
+
61
+ def enough_data?
62
+ actual_lngth = next_msg_length + HEADER_LNGTH
63
+ if next_msg_length.nil?
64
+ Connection.current.logger.warn {"too little data --> #{@data} "}
65
+ false
66
+ else
67
+ @data.bytesize >= actual_lngth
68
+ end
69
+ end
70
+
71
+ def length_data?
72
+ @data.bytesize > HEADER_LNGTH
73
+ end
74
+
75
+ def next_msg_length
76
+ # can't check length if first 4 bytes don't exist
77
+ length = @data.byteslice(0..3).unpack1('N')
78
+ length.nil? ? 0 : length
79
+ end
80
+
81
+ def append_new_data
82
+ @data += @socket.recvfrom(4096)[0]
83
+ end
84
+
85
+ def validate_message_footer(msg, _length)
86
+ last = msg.bytesize
87
+ last_byte = msg.byteslice(last - 1, last)
88
+ raise 'Could not validate last byte' if last_byte.nil?
89
+ raise "Message has an invalid last byte. expecting \0, received: #{last_byte}" if last_byte != "\0"
90
+ end
91
+
92
+ def validate_data_header(length) # disabled
93
+ # todo:: verify length according to the expected dataframe
94
+ # RequestHistoryData returns large datastreams
95
+ return true# if length <= 5000
96
+ raise "Message is longer then max length (#{length}/5000)"
97
+ end
98
+ end
99
+ end
data/lib/ib/socket.rb ADDED
@@ -0,0 +1,83 @@
1
+ module IB
2
+ # includes methods from IB:.Support
3
+ # which adds a tws-method to
4
+ # - Array
5
+ # - Symbol
6
+ # - String
7
+ # - Numeric
8
+ # - TrueClass, FalseClass and NilClass
9
+ #
10
+ class Socket < TCPSocket
11
+ include IB::PrepareData
12
+ using IB::Support
13
+
14
+ def initialising_handshake
15
+ v100_prefix = "API".tws.encode 'ascii'
16
+ v100_version = self.prepare_message Messages::SERVER_VERSION
17
+ write_data v100_prefix+v100_version
18
+ ## start tws-log
19
+ # [QO] INFO [JTS-SocketListener-49] - State: HEADER, IsAPI: UNKNOWN
20
+ # [QO] INFO [JTS-SocketListener-49] - State: STOP, IsAPI: YES
21
+ # [QO] INFO [JTS-SocketListener-49] - ArEServer: Adding 392382055 with id 2147483647
22
+ # [QO] INFO [JTS-SocketListener-49] - eServersChanged: 1
23
+ # [QO] INFO [JTS-EServerSocket-287] - [2147483647:136:136:1:0:0:0:SYS] Starting new conversation with client on 127.0.0.1
24
+ # [QO] INFO [JTS-EServerSocketNotifier-288] - Starting async queue thread
25
+ # [QO] INFO [JTS-EServerSocket-287] - [2147483647:136:136:1:0:0:0:SYS] Server version is 136
26
+ # [QO] INFO [JTS-EServerSocket-287] - [2147483647:136:136:1:0:0:0:SYS] Client version is 136
27
+ # [QO] INFO [JTS-EServerSocket-287] - [2147483647:136:136:1:0:0:0:SYS] is 3rdParty true
28
+ ## end tws-log
29
+ end
30
+
31
+
32
+ def read_string
33
+ string = self.gets(EOL)
34
+
35
+ until string
36
+ # Silently ignores nils
37
+ string = self.gets(EOL)
38
+ sleep 0.1
39
+ end
40
+
41
+ string.chomp
42
+ end
43
+
44
+
45
+ # Sends null terminated data string into socket
46
+ def write_data data
47
+ self.syswrite data.tws
48
+ end
49
+
50
+ # send the message (containing several instructions) to the socket,
51
+ # calls prepare_message to convert data-elements into NULL-terminated strings
52
+ def send_messages *data
53
+ self.syswrite prepare_message(data)
54
+ rescue Errno::ECONNRESET => e
55
+ Connection.logger.fatal{ "Data not accepted by IB \n
56
+ #{data.inspect} \n
57
+ Backtrace:\n "}
58
+ Connection.logger.error e.backtrace
59
+ end
60
+
61
+ def receive_messages
62
+ begin
63
+ complete_message_buffer = []
64
+ begin
65
+ # this is the blocking version of recv
66
+ buffer = self.recvfrom(8192)[0]
67
+ # STDOUT.puts "BUFFER:: #{buffer.inspect}"
68
+ complete_message_buffer << buffer
69
+
70
+ end while buffer.size == 8192
71
+ complete_message_buffer.join('')
72
+ rescue Errno::ECONNRESET => e
73
+ Connection.logger.fatal{ "Data Buffer is not filling \n
74
+ The Buffer: #{buffer.inspect} \n
75
+ Backtrace:\n
76
+ #{e.backtrace.join("\n") } " }
77
+ Kernel.exit
78
+ end
79
+ end
80
+
81
+ end # class Socket
82
+
83
+ end # module IB
data/lib/ib/support.rb ADDED
@@ -0,0 +1,236 @@
1
+ # Class-extensions only applied when data are read from the tws
2
+ # Array : read several formats
3
+ # Array, String, Symbol, true, false, nil : apply tws.method
4
+ #
5
+ # Apply through: module aaxx
6
+ # using IB::Support
7
+ #
8
+ module IB
9
+ module Support
10
+
11
+ refine Array do
12
+
13
+ def zero?
14
+ false
15
+ end
16
+ # Returns the integer.
17
+ # retuns nil otherwise or if no element is left on the stack
18
+ def read_int
19
+ i= self.shift rescue nil
20
+ i = i.to_i unless i.blank? # this includes conversion of string to zero(0)
21
+ i.is_a?( Integer ) && i != 2147483647 ? i : nil
22
+
23
+ end
24
+
25
+ def read_float
26
+ i= self.shift rescue nil
27
+ i = i.to_f unless i.blank?
28
+
29
+ end
30
+ def read_decimal
31
+ i= self.shift rescue nil
32
+ i = i.to_d unless i.blank?
33
+ i.is_a?(Numeric) && i < IB::TWS_MAX ? i : nil # return nil, if a very large number is transmitted
34
+ end
35
+
36
+ alias read_decimal_max read_decimal
37
+
38
+ ## Values -1 and below indicate: Not computed (TickOptionComputation)
39
+ def read_decimal_limit_1
40
+ i= read_decimal
41
+ i <= -1 ? nil : i
42
+ end
43
+
44
+ ## Values -2 and below indicate: Not computed (TickOptionComputation)
45
+ def read_decimal_limit_2
46
+ i= read_decimal
47
+ i <= -2 ? nil : i
48
+ end
49
+
50
+
51
+ def read_string
52
+ self.shift rescue ""
53
+ end
54
+ ## reads a string and checks if NULL == IB::TWS_MAX is present.
55
+ ## in that case: returns nil. otherwise: returns the string
56
+ def read_string_not_null
57
+ r = read_string
58
+ rd = r.to_d unless r.blank?
59
+ rd.is_a?(Numeric) && rd >= IB::TWS_MAX ? nil : r
60
+ end
61
+
62
+ def read_symbol
63
+ read_string.to_sym
64
+ end
65
+
66
+ # convert xml into a hash
67
+ def read_xml
68
+ Ox.load( read_string(), mode: :hash_no_attrs)
69
+ end
70
+
71
+
72
+ def read_int_date
73
+ t= read_int
74
+ s= Time.at(t.to_i)
75
+ # s.year == 1970 --> data is most likely a date-string
76
+ s.year == 1970 ? Date.parse(t.to_s) : s
77
+ end
78
+
79
+ def read_parse_date
80
+ Time.parse read_string
81
+ end
82
+
83
+ def read_boolean
84
+
85
+ v = self.shift rescue nil
86
+ case v
87
+ when "1"
88
+ true
89
+ when "0"
90
+ false
91
+ else nil
92
+ end
93
+ end
94
+
95
+
96
+ def read_datetime
97
+ the_string = read_string
98
+ the_string.blank? ? nil : DateTime.parse(the_string)
99
+ end
100
+
101
+ def read_date
102
+ the_string = read_string
103
+ the_string.blank? ? nil : Date.parse(the_string)
104
+ end
105
+ # def read_array
106
+ # count = read_int
107
+ # end
108
+
109
+ ## originally provided in socket.rb
110
+ # # Returns loaded Array or [] if count was 0#
111
+ #
112
+ # Without providing a Block, the elements are treated as string
113
+ def read_array hashmode:false, &block
114
+ count = read_int
115
+ case count
116
+ when 0
117
+ []
118
+ when nil
119
+ nil
120
+ else
121
+ count= count + count if hashmode
122
+ if block_given?
123
+ Array.new(count, &block)
124
+ else
125
+ Array.new( count ){ read_string }
126
+ end
127
+ end
128
+ end
129
+ #
130
+ # Returns a hash
131
+ # Expected Buffer-Format:
132
+ # count (of Hash-elements)
133
+ # count* key|Value
134
+ # Key's are transformed to symbols, values are treated as string
135
+ def read_hash
136
+ tags = read_array( hashmode: true ) # { |_| [read_string, read_string] }
137
+ result = if tags.nil? || tags.flatten.empty?
138
+ tags # {}
139
+ else
140
+ interim = if tags.size.modulo(2).zero?
141
+ Hash[*tags.flatten]
142
+ else
143
+ Hash[*tags[0..-2].flatten] # omit the last element
144
+ end
145
+ # symbolize Hash
146
+ interim.map { |k, v| [k.to_sym, v] unless k.nil? }.compact.to_h
147
+ end
148
+ end
149
+ #
150
+
151
+ def read_contract # read a standard contract and return als hash
152
+ { con_id: read_int,
153
+ symbol: read_string,
154
+ sec_type: read_string,
155
+ expiry: read_string,
156
+ strike: read_decimal,
157
+ right: read_string,
158
+ multiplier: read_decimal,
159
+ exchange: read_string,
160
+ currency: read_string,
161
+ local_symbol: read_string,
162
+ trading_class: read_string }
163
+ end
164
+
165
+
166
+ def read_bar # read a Historical data bar
167
+ # ** historicalDataUpdate: time open close high low ** covered here
168
+ # historicalData time open high low close <- covered in messages/incomming
169
+ { :time => read_int_date, # conversion of epoche-time-integer to Dateime
170
+ # requires format_date in request to be "2"
171
+ # (outgoing/bar_requests # RequestHistoricalData#Encoding)
172
+ :open => read_decimal,
173
+ :close => read_decimal,
174
+ :high => read_decimal,
175
+ :low => read_decimal,
176
+ :wap => read_decimal,
177
+ :volume => read_int,
178
+ # :has_gaps => read_string, # only in ServerVersion < 124
179
+ :trades => read_int }
180
+
181
+ end
182
+
183
+
184
+ alias read_bool read_boolean
185
+
186
+ def tws
187
+ if blank?
188
+ nil.tws
189
+ else
190
+ self.flatten.map( &:tws ).join # [ "", [] , nil].flatten -> ["", nil]
191
+ # elements with empty array's are cut
192
+ # this is the desired behavior!
193
+ end
194
+ end
195
+ end # refining array
196
+ refine Symbol do
197
+ def tws
198
+ self.to_s.tws
199
+ end
200
+ end
201
+ refine String do
202
+ def tws
203
+ if empty?
204
+ IB::EOL
205
+ else
206
+ self[-1] == IB::EOL ? self : self+IB::EOL
207
+ end
208
+ end
209
+ end
210
+
211
+ refine Numeric do
212
+ def tws
213
+ self.to_s.tws
214
+ end
215
+ end
216
+
217
+ refine TrueClass do
218
+ def tws
219
+ 1.tws
220
+ end
221
+ end
222
+
223
+ refine FalseClass do
224
+ def tws
225
+ 0.tws
226
+ end
227
+ end
228
+
229
+ refine NilClass do
230
+ def tws
231
+ IB::EOL
232
+ end
233
+ end
234
+ end
235
+
236
+ end
data/lib/ib/version.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'pathname'
2
+
3
+ module IB
4
+ VERSION_FILE = Pathname.new(__FILE__).dirname + '../../VERSION' # :nodoc:
5
+ Version = VERSION = VERSION_FILE.exist? ? VERSION_FILE.read.strip : nil
6
+ end
data/lib/ib-api.rb ADDED
@@ -0,0 +1,44 @@
1
+
2
+ require "zeitwerk"
3
+ require "active_model"
4
+ require 'active_support/concern'
5
+ require 'active_support/core_ext/module/attribute_accessors.rb'
6
+ require 'bigdecimal/util' # provides .to_d for numeric and string classes
7
+ require 'class_extensions'
8
+ require 'logger'
9
+ require 'terminal-table'
10
+ require 'workflow'
11
+
12
+ #require 'ib/version'
13
+ #require 'ib/connection'
14
+
15
+ require "server_versions"
16
+
17
+ require 'ib/constants'
18
+ require 'ib/errors'
19
+ #loader = Zeitwerk::Loader.new
20
+ loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
21
+ loader.ignore("#{__dir__}/server_versions.rb")
22
+ loader.ignore("#{__dir__}/ib-api.rb")
23
+ loader.ignore("#{__dir__}/ib/contract.rb")
24
+ loader.ignore("#{__dir__}/ib/constants.rb")
25
+ loader.ignore("#{__dir__}/ib/errors.rb")
26
+ loader.ignore("#{__dir__}/ib/order_condition.rb")
27
+ loader.ignore("#{__dir__}/ib/messages/outgoing/old-place-order.rb")
28
+ loader.ignore("#{__dir__}/ib/messages/outgoing/new-place-order.rb")
29
+ #loader.ignore("#{__dir__}/models")
30
+ loader.inflector.inflect(
31
+ "ib" => "IB",
32
+ "receive_fa" => "ReceiveFA",
33
+ "tick_efp" => "TickEFP",
34
+ )
35
+ #loader.push_dir("#{__dir__}")
36
+ loader.push_dir("#{__dir__}/../models/")
37
+ loader.push_dir("#{__dir__}/../conditions/")
38
+ loader.setup
39
+ loader.eager_load
40
+ #require 'requires'
41
+ require 'ib/contract.rb'
42
+ require 'ib/order_condition.rb'
43
+ #IbRuby = Ib
44
+ #IB = Ib
@@ -0,0 +1,145 @@
1
+ =begin
2
+ taken from the python-client code
3
+
4
+ Copyright (C) 2016 Interactive Brokers LLC. All rights reserved. This code is
5
+ subject to the terms and conditions of the IB API Non-Commercial License or the
6
+ IB API Commercial License, as applicable.
7
+
8
+
9
+
10
+ The known server versions.
11
+ =end
12
+
13
+ KNOWN_SERVERS = {
14
+ #min_server_ver_real_time_bars => 34,
15
+ #min_server_ver_scale_orders => 35,
16
+ #min_server_ver_snapshot_mkt_data => 35,
17
+ #min_server_ver_sshort_combo_legs => 35,
18
+ #min_server_ver_what_if_orders => 36,
19
+ #min_server_ver_contract_conid => 37,
20
+ :min_server_ver_pta_orders => 39,
21
+ :min_server_ver_fundamental_data => 40,
22
+ :min_server_ver_delta_neutral => 40,
23
+ :min_server_ver_contract_data_chain => 40,
24
+ :min_server_ver_scale_orders2 => 40,
25
+ :min_server_ver_algo_orders => 41,
26
+ :min_server_ver_execution_data_chain => 42,
27
+ :min_server_ver_not_held => 44,
28
+ :min_server_ver_sec_id_type => 45,
29
+ :min_server_ver_place_order_conid => 46,
30
+ :min_server_ver_req_mkt_data_conid => 47,
31
+ :min_server_ver_req_calc_implied_volat => 49,
32
+ :min_server_ver_req_calc_option_price => 50,
33
+ :min_server_ver_sshortx_old => 51,
34
+ :min_server_ver_sshortx => 52,
35
+ :min_server_ver_req_global_cancel => 53,
36
+ :min_server_ver_hedge_orders => 54,
37
+ :min_server_ver_req_market_data_type => 55,
38
+ :min_server_ver_opt_out_smart_routing => 56,
39
+ :min_server_ver_smart_combo_routing_params => 57,
40
+ :min_server_ver_delta_neutral_conid => 58,
41
+ :min_server_ver_scale_orders3 => 60,
42
+ :min_server_ver_order_combo_legs_price => 61,
43
+ :min_server_ver_trailing_percent => 62,
44
+ :min_server_ver_delta_neutral_open_close => 66,
45
+ :min_server_ver_positions => 67,
46
+ :min_server_ver_account_summary => 67,
47
+ :min_server_ver_trading_class => 68,
48
+ :min_server_ver_scale_table => 69,
49
+ :min_server_ver_linking => 70,
50
+ :min_server_ver_algo_id => 71,
51
+ :min_server_ver_optional_capabilities => 72,
52
+ :min_server_ver_order_solicited => 73,
53
+ :min_server_ver_linking_auth => 74,
54
+ :min_server_ver_primaryexch => 75,
55
+ :min_server_ver_randomize_size_and_price => 76,
56
+ :min_server_ver_fractional_positions => 101,
57
+ :min_server_ver_pegged_to_benchmark => 102,
58
+ :min_server_ver_models_support => 103,
59
+ :min_server_ver_sec_def_opt_params_req => 104,
60
+ :min_server_ver_ext_operator => 105,
61
+ :min_server_ver_soft_dollar_tier => 106,
62
+ :min_server_ver_req_family_codes => 107,
63
+ :min_server_ver_req_matching_symbols => 108,
64
+ :min_server_ver_past_limit => 109,
65
+ :min_server_ver_md_size_multiplier => 110,
66
+ :min_server_ver_cash_qty => 111,
67
+ :min_server_ver_req_mkt_depth_exchanges => 112,
68
+ :min_server_ver_tick_news => 113,
69
+ :min_server_ver_req_smart_components => 114,
70
+ :min_server_ver_req_news_providers => 115,
71
+ :min_server_ver_req_news_article => 116,
72
+ :min_server_ver_req_historical_news => 117,
73
+ :min_server_ver_req_head_timestamp => 118,
74
+ :min_server_ver_req_histogram => 119,
75
+ :min_server_ver_service_data_type => 120,
76
+ :min_server_ver_agg_group => 121,
77
+ :min_server_ver_underlying_info => 122,
78
+ :min_server_ver_cancel_headtimestamp => 123,
79
+ :min_server_ver_synt_realtime_bars => 124,
80
+ :min_server_ver_cfd_reroute => 125,
81
+ :min_server_ver_market_rules => 126,
82
+ :min_server_ver_pnl => 127,
83
+ :min_server_ver_news_query_origins => 128,
84
+ :min_server_ver_unrealized_pnl => 129,
85
+ :min_server_ver_historical_ticks => 130,
86
+ :min_server_ver_market_cap_price => 131,
87
+ :min_server_ver_pre_open_bid_ask => 132,
88
+ :min_server_ver_real_expiration_date => 134,
89
+ :min_server_ver_realized_pnl => 135,
90
+ :min_server_ver_last_liquidity => 136,
91
+ :min_server_ver_tick_by_tick => 137,
92
+ :min_server_ver_decision_maker => 138,
93
+ :min_server_ver_mifid_execution => 139,
94
+ :min_server_ver_tick_by_tick_ignore_size => 140,
95
+ :min_server_ver_auto_price_for_hedge => 141,
96
+ :min_server_ver_what_if_ext_fields => 142,
97
+ :min_server_ver_scanner_generic_opts => 143,
98
+ :min_server_ver_api_bind_order => 144,
99
+ :min_server_ver_order_container => 145, ### > Version Field in Order dropped
100
+ :min_server_ver_smart_depth => 146,
101
+ :min_server_ver_remove_null_all_casting => 147,
102
+ :min_server_ver_d_peg_orders => 148,
103
+ :min_server_ver_mkt_depth_prim_exchange => 149,
104
+ :min_server_ver_completed_orders => 150,
105
+ :min_server_ver_price_mgmt_algo => 151,
106
+ :min_server_ver_stock_type => 152,
107
+ :min_server_ver_encode_msg_ascii7 => 153,
108
+ :min_server_ver_send_all_family_codes => 154,
109
+ :min_server_ver_no_default_open_close => 155,
110
+ :min_server_ver_price_based_volitility => 156,
111
+ :min_server_ver_replace_fa_end => 157,
112
+ :min_server_ver_duration => 158,
113
+ :min_server_ver_market_data_in_shares => 159,
114
+ :min_server_ver_post_to_ats => 160,
115
+ :min_server_ver_wshe_calendar => 161,
116
+ :min_server_ver_auto_cancel_parent => 162,
117
+ :min_server_ver_fractional_size_support => 163,
118
+ :min_server_ver_size_rules => 164,
119
+ :min_server_ver_historical_schedule => 165,
120
+ :min_server_ver_advanced_order_reject => 166,
121
+ :min_server_ver_user_info => 167,
122
+ :min_server_ver_crypto_aggregated_trades => 168,
123
+ :min_server_ver_manual_order_time => 169,
124
+ :min_server_ver_pegbest_pegmid_offsets => 170,
125
+ :min_server_ver_wsh_event_data_filters => 171,
126
+ :min_server_ver_ipo_prices => 172,
127
+ :min_server_ver_wsh_event_data_filters_date => 173,
128
+ :min_server_ver_instrument_timezone => 174,
129
+ :min_server_ver_hmds_market_data_in_shares => 175,
130
+ :min_server_ver_bond_issuerid => 176,
131
+ :min_server_ver_fa_profile_desupport => 177,
132
+ :min_server_ver_pending_price_revision => 178,
133
+ :min_server_ver_fund_data_fields => 179,
134
+ :min_server_ver_manual_order_time_exercise_options => 180,
135
+ :min_server_ver_open_order_ad_strategy => 181,
136
+ :min_server_ver_last_trade_date => 182,
137
+ :min_server_ver_customer_account => 183,
138
+ :min_server_ver_professional_customer => 184
139
+ }
140
+ # 100+ messaging */
141
+ # 100 = enhanced handshake, msg length prefixes
142
+
143
+ MIN_CLIENT_VER = 100
144
+ MAX_CLIENT_VER = KNOWN_SERVERS[:min_server_ver_historical_schedule] # 165
145
+ # imessages/outgoing/request_tick_Data is prepared for change to ver. 140 , its commented for now