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
@@ -0,0 +1,43 @@
1
+ # Frequently used stock contracts definitions
2
+ module IB
3
+ module Symbols
4
+ module Index
5
+ extend Symbols
6
+
7
+ def self.contracts
8
+ @contracts.presence || super.merge(
9
+ :dax => IB::Index.new( :symbol => "DAX", :currency => "EUR", exchange: 'EUREX',
10
+ :description => "DAX Performance Index."),
11
+ :asx => IB::Index.new( :symbol => 'AP', :currency => 'AUD', exchange: 'ASX',
12
+ :description => "ASX 200 Index (Base for IndexOptions)" ),
13
+ :hsi => IB::Index.new( :symbol => 'HSI', :currency => 'HKD', exchange: 'HKFE',
14
+ :description => "Hang Seng Index" ),
15
+ :minihsi => IB::Index.new( :symbol => 'MHI', :currency => 'HKD', exchange: 'HKFE',
16
+ :description => "Mini Hang Seng Index" ),
17
+ :stoxx => IB::Index.new( :symbol => "ESTX50", :currency => "EUR", exchange: 'EUREX',
18
+ :description => "Dow Jones Euro STOXX50"),
19
+ :spx => IB::Index.new( :symbol => "SPX", :currency => "USD", exchange: 'CBOE',
20
+ :description => "S&P 500 Stock Index"),
21
+ :vhsi => IB::Index.new( symbol: 'VHSI', exchange: 'HKFE',
22
+ :description => "Hang Seng Volatility Index"),
23
+ :vasx => IB::Index.new( symbol: 'XVI', exchange: 'ASX',
24
+ :description => "ASX 200 Volatility Index") ,
25
+ :vstoxx => IB::Index.new( :symbol => "V2TX", :currency => "EUR", exchange: 'EUREX',
26
+ :description => "VSTOXX Volatility Index"),
27
+ :vdax => IB::Index.new( :symbol => "VDAX", exchange: 'EUREX',
28
+ :description => "German VDAX Volatility Index"),
29
+ :vix => IB::Index.new( :symbol => "VIX", exchange: 'CBOE',
30
+ :description => "CBOE Volatility Index"),
31
+ :volume => IB::Index.new( symbol: 'VOL-NYSE', exchange: 'NYSE',
32
+ description: "NYSE Volume Index" ),
33
+ :trin => IB::Index.new( symbol: 'TRIN-NYSE', exchange: 'NYSE',
34
+ description: "NYSE TRIN (or arms) Index"),
35
+ :tick => IB::Index.new( symbol: 'TICK-NYSE', exchange: 'NYSE',
36
+ description: "NYSE TICK Index"),
37
+ :a_d => IB::Index.new( symbol: 'AD-NYSE', exchange: 'NYSE',
38
+ description: "NYSE Advance Decline Index") )
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,99 @@
1
+ # Option contracts definitions.
2
+ # TODO: add next_expiry and other convenience from Futures module.
3
+ # Notice: OSI-Notation is broken
4
+ module IB
5
+ module Symbols
6
+ module Options
7
+ extend Symbols
8
+
9
+ ## usage: IB::Symbols::Options.stoxx.merge( strike: 5000, expiry: 202404 )
10
+ ## IB::Symbols::Options.stoxx.merge( strike: 5000 ).next_expiry => fetch the next regulary
11
+ ## monthly option (3.rd friday)
12
+ def self.contracts
13
+ @contracts ||= {
14
+ stoxx: IB::Option.new(symbol: :ESTX50,
15
+ expiry: IB::Option.next_expiry ,
16
+ right: :put,
17
+ trading_class: 'OESX',
18
+ currency: 'EUR',
19
+ exchange: 'EUREX',
20
+ description: "Monthly settled ESTX50 Options"),
21
+ spx: IB::Option.new( symbol: :SPX,
22
+ expiry: IB::Option.next_expiry ,
23
+ right: :put,
24
+ trading_class: 'SPX',
25
+ currency: 'USD',
26
+ exchange: 'SMART',
27
+ description: "Monthly settled SPX options"),
28
+ spxw: IB::Option.new( symbol: :SPX,
29
+ expiry: IB::Option.next_expiry ,
30
+ right: :put,
31
+ trading_class: 'SPXW',
32
+ currency: 'USD', exchange: 'SMART',
33
+ description: "Daily settled SPX options"),
34
+ xsp: IB::Option.new( symbol: 'XSP',
35
+ expiry: IB::Option.next_expiry ,
36
+ right: :put,
37
+ trading_class: 'XSP',
38
+ currency: 'USD',
39
+ exchange: 'SMART',
40
+ description: "Daily settled Mini-SPX options"),
41
+ :spy => IB::Option.new( :symbol => :SPY,
42
+ :expiry => IB::Option.next_expiry,
43
+ :right => :put,
44
+ :currency => "USD",
45
+ :exchange => 'SMART',
46
+ :description => "SPY Put next expiration"),
47
+ :rut => IB::Option.new( :symbol => :RUT,
48
+ :expiry => IB::Option.next_expiry,
49
+ :right => :put,
50
+ :currency => "USD",
51
+ :exchange => 'SMART',
52
+ :trading_class => 'RUT',
53
+ description: "Monthly settled RUT options"),
54
+ :rutw => IB::Option.new( :symbol => :RUT,
55
+ :expiry => IB::Option.next_expiry,
56
+ :right => :put,
57
+ :currency => "USD",
58
+ :exchange => 'SMART',
59
+ description: "Weekly settled RUT options"),
60
+ :russell => IB::Option.new( :symbol => :RUT, # :russell == :rut !
61
+ :expiry => IB::Option.next_expiry,
62
+ :right => :put,
63
+ :currency => "USD",
64
+ :exchange => 'SMART',
65
+ description: "Monthly settled RUT options"),
66
+ :mini_russell => IB::Option.new( :symbol => :MRUT,
67
+ :expiry => IB::Option.next_expiry,
68
+ :right => :put,
69
+ :currency => "USD",
70
+ :exchange => 'SMART',
71
+ :description => "Weekly settled Mini-Russell2000 options"),
72
+ :aapl => IB::Option.new( :symbol => "AAPL",
73
+ :expiry => IB::Option.next_expiry,
74
+ :right => "C",
75
+ :strike => 150,
76
+ :exchange => 'SMART',
77
+ :currency => 'USD',
78
+ :description => "Apple Call 130"),
79
+
80
+ :ibm => IB::Option.new( symbol: 'IBM',
81
+ exchange: 'SMART',
82
+ right: :put,
83
+ expiry: IB::Option.next_expiry ,
84
+ description: 'IBM-Option'),
85
+ :ibm_lazy_expiry => IB::Option.new( symbol: 'IBM',
86
+ right: :put,
87
+ strike: 180,
88
+ exchange: 'SMART',
89
+ description: 'IBM-Option Chain with strike 180'),
90
+ :ibm_lazy_strike => IB::Option.new( symbol: 'IBM',
91
+ right: :put,
92
+ exchange: 'SMART',
93
+ expiry: IB::Option.next_expiry,
94
+ description: 'IBM-Option Chain ( monthly expiry)')
95
+ }
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,44 @@
1
+ # Frequently used stock contracts definitions
2
+ # TODO: auto-request :ContractDetails from IB if unknown symbol is requested?
3
+ module IB
4
+ module Symbols
5
+ module Stocks
6
+ extend Symbols
7
+
8
+ def self.contracts
9
+ @contracts.presence || super.merge(
10
+ :ib_smart => IB::Stock.new( :symbol => 'IBKR',
11
+ :description => 'Interactive Brokers Stock with smart exchange setting'),
12
+ :ib => IB::Stock.new( :symbol=> 'IBKR', exchange: 'ISLAND',
13
+ :description => 'Interactive Brokers Stock'),
14
+ :aapl => IB::Stock.new( :symbol => "AAPL",
15
+ :currency => "USD",
16
+ :description => "Apple Inc."),
17
+
18
+ :msft_conid => IB::Stock.new( con_id: 272093,
19
+ currency: :usd ,
20
+ description: 'Microsoft selected by its con-id'),
21
+ :msft => IB::Stock.new( symbol: 'MSFT',
22
+ description: 'Microsoft selected by its symbol'),
23
+ :msft_island =>IB::Stock.new( symbol: 'MSFT', primary_exchange: 'ISLAND',
24
+ description: 'Microsoft, primary trading @ ISLAND'),
25
+ :vxx => IB::Stock.new( :symbol => "VXX",
26
+ :exchange => "ARCA",
27
+ :description => "iPath S&P500 VIX short term Futures ETN"),
28
+ :wfc => IB::Stock.new( :symbol => "WFC",
29
+ :exchange => "NYSE",
30
+ :currency => "USD",
31
+ :description => "Wells Fargo"),
32
+ :sie => IB::Stock.new( symbol: 'SIE',
33
+ currency: 'EUR',
34
+ description: 'Siemens AG'),
35
+ :wrong => IB::Stock.new( :symbol => "QEEUUE",
36
+ :exchange => "NYSE",
37
+ :currency => "USD",
38
+ :description => "Non-existent stock")
39
+ )
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ module IB
2
+ module Symbols
3
+ VERSION = "2.0"
4
+ end
5
+ end
@@ -0,0 +1,118 @@
1
+ =begin
2
+
3
+ Plugin that provides helper methods for predefined Contracts
4
+
5
+
6
+ Public API
7
+ ==========
8
+
9
+ Extends IB::Contract
10
+
11
+ =end
12
+
13
+ # These modules are used to facilitate referencing of most popular IB Contracts.
14
+ # Like pages in the TWS-GUI, they can be utilised to organise trading and research.
15
+ #
16
+ # Symbol Allocations are organized as modules. They represent the contents of yaml files in
17
+ #
18
+ # /lib/symbols/
19
+ #
20
+ # Any collection is represented as simple Hash, with __key__ as qualifier and an __IB::Contract__ as value.
21
+ # The Value is either a fully prequalified Contract (Stock, Option, Future, Forex, CFD, BAG) or
22
+ # a lazy qualified Contract acting as base für further calucaltions and requests.
23
+ #
24
+ # IB::Symbols.allocate_collection :Name
25
+ #
26
+ # creates the Module and file. If a previously created file is found, its contents are read and
27
+ # the vcollection ist reestablished.
28
+ #
29
+ # IB::Symbols::Name.add_contract :wfc, IB::Stock.new( symbol: 'WFC' )
30
+ #
31
+ # adds the contract and stores it in the yaml file
32
+ #
33
+ # IB::Symbols::Name.wfc # or IB::Symbols::Name[:wfc]
34
+ #
35
+ # retrieves the contract
36
+ #
37
+ # IB::Symbols::Name.all
38
+ #
39
+ # returns an Array of stored contracts
40
+ #
41
+ # IB::Symbols::Name.remove_contract :wfc
42
+ #
43
+ # deletes the contract from the list (and the file)
44
+ #
45
+ # To finish the cycle
46
+ #
47
+ # IB::Symbols::Name.purge_collection
48
+ #
49
+ # deletes the file and erases the collection in memory.
50
+ #
51
+ # Additional methods can be introduced
52
+ # * for individual contracts on the module-level or
53
+ # * to organize the list as methods of Array in Module IB::SymbolExtention
54
+ #
55
+ #
56
+ # Contracts can be hardcoded in the required standard-collections as well.
57
+ # Note that the :description field is local to ib-ruby, and is NOT part of the standard TWS API.
58
+ # It is never transmitted to IB. It's purely used clientside, and you can store any arbitrary
59
+ # string that you may find useful there.
60
+
61
+ module IB
62
+ module Symbols
63
+ class Error < StandardError; end
64
+
65
+
66
+
67
+ def hardcoded?
68
+ !self.methods.include? :yml_file
69
+ end
70
+ def method_missing(method, *key)
71
+ if key.empty?
72
+ if contracts.has_key?(method)
73
+ contracts[method]
74
+ elsif methods.include?(:each) && each.methods.include?(method)
75
+ self.each.send method
76
+ else
77
+ error "contract #{method} not defined. Try »all« for a list of defined Contracts.", :symbol
78
+ end
79
+ else
80
+ error "method missing"
81
+ end
82
+ end
83
+
84
+ def all
85
+ contracts.keys.sort rescue contracts.keys
86
+ end
87
+ def print_all
88
+ puts contracts.sort.map{|x,y| [x,y.description].join(" -> ")}.join "\n"
89
+ end
90
+ def contracts
91
+ if @contracts.present?
92
+ @contracts
93
+ else
94
+ @contracts = Hash.new
95
+ end
96
+ end
97
+ def [] symbol
98
+ if c=contracts[symbol]
99
+ return c
100
+ else
101
+ # symbol probably has not been predefined, tell user about it
102
+ file = self.to_s.split(/::/).last.downcase
103
+ msg = "Unknown symbol :#{symbol}, please pre-define it in lib/ib/symbols/#{file}.rb"
104
+ error msg, :symbol
105
+ end
106
+ end
107
+ end
108
+
109
+
110
+ Connection.current.activate_plugin "verify"
111
+ Connection.current.activate_plugin "roll"
112
+ Connection.current.activate_plugin "spread-prototypes"
113
+ [ :forex, :futures, :stocks, :index, :cfd, :commodity, :options, :combo, :bonds, :abstract ].each do |pt|
114
+ Connection.current.activate_plugin "symbols/#{pt.to_s}"
115
+ end
116
+
117
+ end
118
+
@@ -0,0 +1,226 @@
1
+ module IB
2
+ =begin rdoc
3
+
4
+ Plugin that provides Verifying a contract
5
+
6
+ Public API
7
+ ==========
8
+
9
+ Extends IB::Contract
10
+
11
+ * verify
12
+
13
+ returns an array of suitable IB::Contracts
14
+ ```ruby
15
+ a = Stock.new symbol: 'AA'
16
+ aa = a.verify.first
17
+ ```
18
+
19
+ an optional block may be used to modify and filter the tws-response
20
+
21
+ ```ruby
22
+ f = IB::Future.new symbol: 'M2K'
23
+ con_ids = f.verify{ |c| c.con_id }
24
+ => [412889018, 428519982, 446091466, 461318872, 477836981]
25
+ ```
26
+ =end
27
+
28
+ module Verify
29
+
30
+
31
+ # IB::Contract#Verify
32
+
33
+ # verifies the contract
34
+ #
35
+ # returns the number of contracts returned by the TWS.
36
+ #
37
+ #
38
+ # The method accepts a block. The queried contract-Object is accessible there.
39
+ # If multiple contracts are returned by the tws, the block is executed with each of these contracts.
40
+ #
41
+ #
42
+ # Verify returns an _Array_ of contracts. The operation leaves the contract untouched.
43
+ #
44
+ #
45
+ # Returns nil if the contract could not be verified.
46
+ #
47
+ # > s = Stock.new symbol: 'AA'
48
+ # => #<IB::Stock:0x0000000002626cc0
49
+ # @attributes={:symbol=>"AA", :con_id=>0, :right=>"", :include_expired=>false,
50
+ # :sec_type=>"STK", :currency=>"USD", :exchange=>"SMART"}
51
+ # > sp = s.verify.first.essential
52
+ # => #<IB::Stock:0x00000000025a3cf8
53
+ # @attributes={:symbol=>"AA", :con_id=>251962528, :exchange=>"SMART", :currency=>"USD",
54
+ # :strike=>0.0, :local_symbol=>"AA", :multiplier=>0, :primary_exchange=>"NYSE",
55
+ # :trading_class=>"AA", :sec_type=>"STK", :right=>"", :include_expired=>false}
56
+ #
57
+ # > s = Stock.new symbol: 'invalid'
58
+ # => @attributes={:symbol=>"invalid", :sec_type=>"STK", :currency=>"USD", :exchange=>"SMART"}
59
+ # > sp = s.verify
60
+ # => []
61
+ #
62
+ # Takes a Block to modify the queried contracts
63
+ #
64
+ # f = Future.new symbol: 'M2K'
65
+ # con_ids = f.verify{ |c| c.con_id }
66
+ # [412889018, 428519982, 446091466, 461318872, 477836981]
67
+ #
68
+ #
69
+ # Parameter: thread: (true/false)
70
+ #
71
+ # If multiple contracts are to be verified, they can be queried simultaneously.
72
+ # IB::Symbols::W500.map{|c| c.verify(thread: true){ |vc| do_something }}.join
73
+
74
+ def verify thread: nil, &b
75
+ if thread
76
+ Thread.new { _verify &b }
77
+ else
78
+ i = 0
79
+ begin
80
+ _verify &b
81
+ rescue IB::VerifyError
82
+ i += 1
83
+ if i < 3
84
+ sleep 1
85
+ retry
86
+ else
87
+ raise
88
+ end
89
+ end
90
+ end
91
+ end # def
92
+
93
+ # returns a hash
94
+ def necessary_attributes
95
+
96
+ v= { stock: { currency: 'USD', exchange: 'SMART', symbol: nil},
97
+ option: { currency: 'USD', exchange: 'SMART', right: 'P', expiry: nil, strike: nil, symbol: nil},
98
+ future: { currency: 'USD', exchange: nil, expiry: nil, symbol: nil },
99
+ forex: { currency: 'USD', exchange: 'IDEALPRO', symbol: nil }
100
+ }
101
+ sec_type.present? ? v[sec_type] : { con_id: nil, exchange: 'SMART' } # enables to use only con_id for verifying
102
+ # if the contract allows SMART routing
103
+ end
104
+
105
+
106
+ private
107
+
108
+ # Base method to verify a contract
109
+ #
110
+ # if :thread is given, the method subscribes to messages, fires the request and returns the thread, that
111
+ # receives the exit-condition-message
112
+ #
113
+ # otherwise the method waits until the response form tws is processed
114
+ #
115
+ #
116
+ # if :update is true, the attributes of the Contract itself are adapted
117
+ #
118
+ # otherwise the Contract is untouched
119
+ def _verify &b # :nodoc:
120
+ ib = Connection.current
121
+ error "No Connection" unless ib.is_a? Connection
122
+ # we generate a Request-Message-ID on the fly
123
+ error "Either con_id or sec_type have to be set", :verify if con_id.to_i.zero? && sec_type.blank?
124
+ # define local vars which are updated within the query-block
125
+ received_contracts = []
126
+ queue = Queue.new
127
+ message_id = nil
128
+
129
+ # a tws-request is suppressed for bags and if the contract_detail-record is present
130
+ tws_request_not_necessary = bag? || contract_detail.is_a?( ContractDetail )
131
+
132
+ if tws_request_not_necessary
133
+ yield self if block_given?
134
+ return [self] # return an array!
135
+ else # subscribe to ib-messages and describe what to do
136
+ a = ib.subscribe(:Alert, :ContractData, :ContractDataEnd) do |msg|
137
+ case msg
138
+ when Messages::Incoming::Alert
139
+ ## do not throw an error here, asynchronous operation!
140
+ ## just notice failure in log and return nil instead of contract-object
141
+ if msg.code == 200 && msg.error_id == message_id
142
+ ib.logger.error { "Not a valid Contract :: #{self.to_human} " }
143
+ queue.push "InvalidContract"
144
+ end
145
+ when Messages::Incoming::ContractData
146
+ if msg.request_id.to_i == message_id
147
+ c = if block_given?
148
+ yield msg.contract
149
+ else
150
+ msg.contract
151
+ end
152
+ queue.push c unless c.nil?
153
+ end
154
+ when Messages::Incoming::ContractDataEnd
155
+ queue.close if msg.request_id.to_i == message_id
156
+ end # case
157
+ end # subscribe
158
+
159
+ ### send the request !
160
+ # contract_to_be_queried = con_id.present? ? self : query_contract
161
+ # if no con_id is present, the given attributes are checked by query_contract
162
+ # if contract_to_be_queried.present? # is nil if query_contract fails
163
+ message_id = ib.send_message :RequestContractData, :contract => query_contract
164
+
165
+ Thread.new do
166
+ (0 .. 10).each{ sleep 0.1 }
167
+ queue.push "TimeOut" unless queue.closed?
168
+ end
169
+
170
+ while r = queue.pop
171
+ if r.is_a? IB::Contract
172
+ received_contracts << r
173
+ else
174
+ error "No data received from IB-Servers", :verify if r == "TimeOut"
175
+ end
176
+ end
177
+ ib.unsubscribe a
178
+ end
179
+ received_contracts # return contracts
180
+ end
181
+
182
+ # Generates an IB::Contract with the required attributes to retrieve a unique contract from the TWS
183
+ #
184
+ # Background: If the tws is queried with a »complete« IB::Contract, it fails occasionally.
185
+ # So – even to update its contents, a defined subset of query-parameters has to be used.
186
+ #
187
+ # The required data-fields are stored in a yaml-file and fetched by #YmlFile.
188
+ #
189
+ # If `con_id` is present, only `con_id` and `exchange` are transmitted to the tws.
190
+ # Otherwise a IB::Stock, IB::Option, IB::Future or IB::Forex-Object with necessary attributes
191
+ # to query the tws is build (and returned)
192
+ #
193
+ # If Attributes are missing, an IB::VerifyError is fired,
194
+ # This can be trapped with
195
+ # rescue IB::VerifyError do ...
196
+
197
+ def query_contract( invalid_record: true ) # :nodoc:
198
+ # don't raise a verify error at this time. Contract.new con_id= xxxx, currency = 'xyz' is also valid
199
+ ## raise VerifyError, "Querying Contract failed: Invalid Security Type" unless SECURITY_TYPES.values.include? sec_type
200
+
201
+ ## the yml contains symbol-entries
202
+ ## these are converted to capitalized strings
203
+ items_as_string = ->(i){i.map{|x,y| x.to_s.capitalize}.join(', ')}
204
+ ## here we read the corresponding attributes of the specified contract
205
+ item_values = ->(i){ i.map{|x,y| self.send(x).presence || y }}
206
+ ## and finally we create a attribute-hash to instantiate a new Contract
207
+ ## to_h is present only after ruby 2.1.0
208
+ item_attributehash = ->(i){ i.keys.zip(item_values[i]).to_h }
209
+ ## now lets proceed, but only if no con_id is present
210
+ if con_id.blank? || con_id.zero?
211
+ # if item_values[necessary_attributes].any?( &:nil? )
212
+ # raise VerifyError, "#{items_as_string[necessary_attributes]} are needed to retrieve Contract,
213
+ # got: #{item_values[necessary_attributes].join(',')}"
214
+ # end
215
+ # Contract.build item_attributehash[necessary_items].merge(:sec_type=> sec_type) # return this
216
+ Contract.build self.invariant_attributes # return this
217
+ else # its always possible, to retrieve a Contract if con_id and exchange or are present
218
+ Contract.new con_id: con_id , :exchange => exchange.presence || item_attributehash[necessary_attributes][:exchange].presence || 'SMART' # return this
219
+ end # if
220
+ end # def
221
+ end # module verify
222
+
223
+ class Contract
224
+ include Verify
225
+ end
226
+ end #module ib