ib-extensions 1.2 → 1.3

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.
@@ -1,24 +1,24 @@
1
1
  module IB
2
2
 
3
- class Account
4
-
5
-
6
- def account_data_scan search_key, search_currency=nil
7
- if account_values.is_a? Array
8
- if search_currency.present?
9
- account_values.find_all{|x| x.key.match( search_key ) && x.currency == search_currency.upcase }
10
- else
11
- account_values.find_all{|x| x.key.match( search_key ) }
12
- end
13
-
14
- else # not tested!!
15
- if search_currency.present?
16
- account_values.where( ['key like %', search_key] ).where( currency: search_currency )
17
- else # any currency
18
- account_values.where( ['key like %', search_key] )
19
- end
20
- end
21
- end
3
+ class Account
4
+
5
+
6
+ def account_data_scan search_key, search_currency=nil
7
+ if account_values.is_a? Array
8
+ if search_currency.present?
9
+ account_values.find_all{|x| x.key.match( search_key ) && x.currency == search_currency.upcase }
10
+ else
11
+ account_values.find_all{|x| x.key.match( search_key ) }
12
+ end
13
+
14
+ else # not tested!!
15
+ if search_currency.present?
16
+ account_values.where( ['key like %', search_key] ).where( currency: search_currency )
17
+ else # any currency
18
+ account_values.where( ['key like %', search_key] )
19
+ end
20
+ end
21
+ end
22
22
 
23
23
 
24
24
 
@@ -33,31 +33,32 @@ Thus if several Orders are placed with the same order_ref, the active one is ret
33
33
  (If multible keys are specified, local_id preceeds perm_id)
34
34
 
35
35
  =end
36
- def locate_order local_id: nil, perm_id: nil, order_ref: nil, status: /ubmitted/, contract: nil, con_id: nil
37
- search_option = [ local_id.present? ? [:local_id , local_id] : nil ,
38
- perm_id.present? ? [:perm_id, perm_id] : nil,
39
- order_ref.present? ? [:order_ref , order_ref ] : nil ].compact.first
40
- matched_items = if search_option.nil?
41
- orders
42
- else
43
- orders.find_all{|x| x[search_option.first].to_i == search_option.last.to_i }
44
- end
36
+ def locate_order local_id: nil, perm_id: nil, order_ref: nil, status: /ubmitted/, contract: nil, con_id: nil
37
+ search_option = [ local_id.present? ? [:local_id , local_id] : nil ,
38
+ perm_id.present? ? [:perm_id, perm_id] : nil,
39
+ order_ref.present? ? [:order_ref , order_ref ] : nil ].compact.first
40
+ matched_items = if search_option.nil?
41
+ orders
42
+ else
43
+ orders.find_all{|x| x[search_option.first].to_i == search_option.last.to_i }
44
+ end
45
+
45
46
  if contract.present?
46
- if contract.con_id.nil? || contract.con_id =="" || contract.con_id.zero?
47
- contract = contract.verify.first unless contract.is_a? IB::Bag
47
+ if contract.con_id.zero? && !contract.is_a?( IB::Bag )
48
+ contract = contract.verify.first
48
49
  end
49
50
  matched_items = matched_items.find_all{|o| o.contract.essential == contract.essential }
50
51
  elsif con_id.present?
51
52
  matched_items = matched_items.find_all{|o| o.contract.con_id == con_id }
52
53
  end
53
54
 
54
- if status.present?
55
- status = Regexp.new(status) unless status.is_a? Regexp
56
- matched_items.detect{|x| x.order_state.status =~ status }
57
- else
58
- matched_items.last # return the last item
59
- end
60
- end
55
+ if status.present?
56
+ status = Regexp.new(status) unless status.is_a? Regexp
57
+ matched_items.detect{|x| x.order_state.status =~ status }
58
+ else
59
+ matched_items.last # return the last item
60
+ end
61
+ end
61
62
 
62
63
 
63
64
  =begin rdoc
@@ -74,10 +75,8 @@ convert_size: The action-attribute (:buy :sell) is associated according the con
74
75
 
75
76
  The parameter «order» is modified!
76
77
 
77
- It can be used to modify and eventually cancel
78
+ It can further used to modify and eventually cancel
78
79
 
79
- The method raises an IB::TransmissionError if the transmitted order ist not acknowledged by the tws after
80
- one second.
81
80
 
82
81
  Example
83
82
 
@@ -85,111 +84,136 @@ Example
85
84
  order = IB::Limit.order size: 100, price: 65.5
86
85
  g = IB::Gateway.current.clients.last
87
86
 
88
- g.preview contract: j36, order: order
87
+ g.preview contract: j36, order: order
89
88
  => {:init_margin=>0.10864874e6,
90
- :maint_margin=>0.9704137e5,
91
- :equity_with_loan=>0.97877973e6,
92
- :commission=>0.524e1,
93
- :commission_currency=>"USD",
94
- :warning=>""}
89
+ :maint_margin=>0.9704137e5,
90
+ :equity_with_loan=>0.97877973e6,
91
+ :commission=>0.524e1,
92
+ :commission_currency=>"USD",
93
+ :warning=>""
95
94
 
96
95
  the_local_id = g.place order: order
97
- => 67 # returns local_id
98
- order.contract # updated contract-record
99
- => #<IB::Contract:0x00000000013c94b0 @attributes={:con_id=>95346693,
100
- :exchange=>"SGX",
101
- :right=>"",
102
- :include_expired=>false}>
103
-
104
- order.limit_price = 65 # set new price
105
- g.modify order: order # and transmit
106
- => 67 # returns local_id
107
-
108
- g.locate_order( local_id: the_local_id )
109
- => returns the assigned order-record for inspection
110
-
111
- g.cancel order: order
112
- # logger output: 05:17:11 Cancelling 65 New #250/ from 3000/DU167349>
96
+ => 67 # returns local_id
97
+ order.contract # updated contract-record
98
+
99
+ => #<IB::Contract:0x00000000013c94b0 @attributes={:con_id=>9534669,
100
+ :exchange=>"SGX",
101
+ :right=>"",
102
+ :include_expired=>false}>
103
+
104
+ order.limit_price = 65 # set new price
105
+ g.modify order: order # and transmit
106
+ => 67 # returns local_id
107
+
108
+ g.locate_order( local_id: the_local_id )
109
+ => returns the assigned order-record for inspection
110
+
111
+ g.cancel order: order
112
+ # logger output: 05:17:11 Cancelling 65 New #250/ from 3000/DU167349>
113
113
  =end
114
114
 
115
- def place_order order:, contract: nil, auto_adjust: true, convert_size: false, enable_error: false
116
- # adjust the orderprice to min-tick
117
- result = ->(l){ orders.detect{|x| x.local_id == l && x.submitted? } }
118
- #·IB::Symbols are always qualified. They carry a description-field
119
- qualified_contract = ->(c) { c.is_a?(IB::Contract) && ( c.description.present? || (c.con_id.present? && !c.con_id.to_i.zero?) || (c.con_id.to_i <0 && c.sec_type == :bag )) }
120
-
121
- order.contract ||= if qualified_contract[ contract ]
122
- contract
123
- else
124
- contract.verify.first
125
- end
126
-
127
- if order.contract.nil?
128
- error "No valid contract given" if enable_error
129
- return 0
130
- end
131
- ## sending of plain vanilla IB::Bags will fail using account.place, unless a (negative) con-id is provided!
132
- # error "place order: ContractVerification failed. No con_id assigned" unless qualified_contract[order.contract]
133
- ib = IB::Connection.current
134
- wrong_order = nil
135
- the_local_id = nil
136
-
137
- ### Handle Error messages
138
- ### Default action: raise IB::Transmission Error
139
- sa = ib.subscribe( :Alert ) do | msg |
140
- # puts "local_id: #{the_local_id}"
141
- if msg.error_id == the_local_id
142
- if [ 110, # The price does not confirm to the minimum price variation for this contract
143
- 388, # Order size x is smaller than the minimum required size of yy.
144
- ].include? msg.code
145
- error msg.message if enable_error
146
- wrong_order = msg.error_id.to_i
147
- ib.logger.error msg.message
148
- end
149
- end
150
- end
151
- order.account = account # assign the account_id to the account-field of IB::Order
152
- self.orders.update_or_create order, :order_ref
153
- order.auto_adjust # if auto_adjust /defined in lib/order_handling
154
- if convert_size
155
- order.action = order.total_quantity.to_i > 0 ? :buy : :sell
156
- logger.info{ "Converted ordesize to #{order.total_quantity} and triggered a #{order.action} order"} if order.total_quantity.to_i < 0
157
- order.total_quantity = order.total_quantity.to_i.abs
158
- end
159
- # apply non_guarenteed and other stuff bound to the contract to order.
160
- order.attributes.merge! order.contract.order_requirements unless order.contract.order_requirements.blank?
161
- # con_id and exchange fully qualify a contract, no need to transmit other data
162
- the_contract = order.contract.con_id >0 ? Contract.new( con_id: order.contract.con_id, exchange: order.contract.exchange) : nil
163
- the_local_id = order.place the_contract # return the local_id
164
- i=0; loop{i+=1; sleep(0.01); break if locate_order( local_id: the_local_id, status: nil ).present? || i> 1000 }
165
-
166
- ib.unsubscribe sa
167
- raise IB::TransmissionError," #{order.to_human} is not transmitted properly" if i >=1000
168
- the_local_id # return_value
169
- end # place
170
-
171
- # shortcut to enable
172
- # account.place order: {} , contract: {}
173
- # account.preview order: {} , contract: {}
174
- # account.modify order: {}
175
- alias place place_order
115
+ def place_order order:, contract: nil, auto_adjust: true, convert_size: true
116
+ # adjust the orderprice to min-tick
117
+ result = ->(l){ orders.detect{|x| x.local_id == l && x.submitted? } }
118
+ #·IB::Symbols are always qualified. They carry a description-field
119
+ qualified_contract = ->(c) { c.is_a?(IB::Contract) && ( c.description.present? || !c.con_id.to_i.zero? || (c.con_id.to_i <0 && c.sec_type == :bag )) }
120
+
121
+ order.contract ||= if qualified_contract[ contract ]
122
+ contract
123
+ else
124
+ contract.verify.first
125
+ end
126
+
127
+ error "No valid contract given" unless order.contract.is_a?(IB::Contract)
128
+
129
+ ## sending of plain vanilla IB::Bags will fail using account.place, unless a (negative) con-id is provided!
130
+ error "place order: ContractVerification failed. No con_id assigned" unless qualified_contract[order.contract]
131
+
132
+ ib = IB::Connection.current
133
+ wrong_order = nil
134
+ the_local_id = nil
135
+ q = Queue.new
136
+
137
+ ### Handle Error messages
138
+ ### Default action: raise IB::Transmission Error
139
+ sa = ib.subscribe( :Alert ) do | msg |
140
+ # puts "local_id: #{the_local_id}"a
141
+ puts msg.inspect
142
+ if msg.error_id == the_local_id
143
+ if [ 110, # The price does not confirm to the minimum price variation for this contract
144
+ 201, # Order rejected, No Trading permissions
145
+ 203, # Security is not allowed for trading
146
+ 325, # Disretionary Orders are not supported for ths combination of oerder-type and exchange
147
+ 355, # Order size does not conform to market rule
148
+ 361, 362, 363, 364, # invalid trigger or stop-price
149
+ 388, # Order size x is smaller than the minimum required size of yy.
150
+ ].include? msg.code
151
+ wrong_order = msg.message
152
+ ib.logger.error msg.message
153
+ q.close # closing the queue indicates that no order was transmitted
154
+ end
155
+ end
156
+ end
157
+ sb = ib.subscribe( :OpenOrder ){|m| q << m.order if m.order.local_id.to_i == the_local_id.to_i }
158
+ # modify order (parameter)
159
+ order.account = account # assign the account_id to the account-field of IB::Order
160
+ self.orders.update_or_create order, :order_ref
161
+ order.auto_adjust if auto_adjust # /defined in file order_handling.rb
162
+ if convert_size
163
+ order.action = order.total_quantity.to_i < 0 ? :sell : :buy unless order.action == :sell
164
+ logger.info{ "Converted ordesize to #{order.total_quantity} and triggered a #{order.action} order"} if order.total_quantity.to_i < 0
165
+ order.total_quantity = order.total_quantity.to_i.abs
166
+ end
167
+ # apply non_guarenteed and other stuff bound to the contract to order.
168
+ order.attributes.merge! order.contract.order_requirements unless order.contract.order_requirements.blank?
169
+ # con_id and exchange fully qualify a contract, no need to transmit other data
170
+ # if no contract is passed to order.place, order.contract is used for placement
171
+ the_contract = order.contract.con_id.to_i >0 ? Contract.new( con_id: order.contract.con_id, exchange: order.contract.exchange) : nil
172
+ the_local_id = order.place the_contract # return the local_id
173
+ # if transmit is false, just include the local_id in the order-record
174
+ Thread.new{ if order.transmit || order.what_if then sleep 1 else sleep 0.001 end ; q.close }
175
+ tws_answer = q.pop
176
+
177
+ ib.unsubscribe sa
178
+ ib.unsubscribe sb
179
+ if q.closed?
180
+ if wrong_order.present?
181
+ raise IB::SymbolError, wrong_order
182
+ elsif the_local_id.present?
183
+ order.local_id = the_local_id
184
+ else
185
+ error " #{order.to_human} is not transmitted properly", :symbol
186
+ end
187
+ else
188
+ order=tws_answer # return order-record received from tws
189
+ end
190
+ the_local_id # return_value
191
+ end # place
192
+
193
+
194
+ # shortcut to enable
195
+ # account.place order: {} , contract: {}
196
+ # account.preview order: {} , contract: {}
197
+ # account.modify order: {}
198
+ alias place place_order
176
199
 
177
200
  =begin #rdoc
178
201
  Account#ModifyOrder operates in two modi:
179
202
 
180
203
  First: The order is specified via local_id, perm_id or order_ref.
181
- It is checked, whether the order is still modificable.
182
- Then the Order ist provided through the block. Any modification is done there.
183
- Important: The Block has to return the modified IB::Order
204
+ It is checked, whether the order is still modificable.
205
+ Then the Order ist provided through the block. Any modification is done there.
206
+ Important: The Block has to return the modified IB::Order
184
207
 
185
208
  Second: The order can be provided as parameter as well. This will be used
186
209
  without further checking. The block is now optional.
187
- Important: The OrderRecord must provide a valid Contract.
210
+ Important: The OrderRecord must provide a valid Contract.
188
211
 
189
212
  The simple version does not adjust the given prices to tick-limits.
190
213
  This has to be done manualy in the provided block
191
214
  =end
192
215
 
216
+
193
217
  def modify_order local_id: nil, order_ref: nil, order:nil
194
218
 
195
219
  result = ->(l){ orders.detect{|x| x.local_id == l && x.submitted? } }
@@ -213,19 +237,27 @@ This has to be done manualy in the provided block
213
237
  #
214
238
  # The order received from the TWS is kept in account.orders
215
239
  #
216
- # Raises IB::TransmissionError if the Order could not be placed properly
240
+ # Raises IB::SymbolError if the Order could not be placed properly
217
241
  #
218
242
  def preview order:, contract: nil, **args_which_are_ignored
219
243
  # to_do: use a copy of order instead of temporary setting order.what_if
220
- result = ->(l){ orders.detect{|x| x.local_id == l && x.submitted? } }
221
- order.what_if = true
222
- the_local_id = place_order order: order, contract: contract
223
- i=0; loop{ i= i+1; break if result[the_local_id] || i > 1000; sleep 0.01 }
224
- raise IB::TransmissionError,"(Preview-) #{order.to_human} is not transmitted properly" if i >=1000
225
- order.what_if = false # reset what_if flag
226
- order.local_id = nil # reset local_id to enable re-using the order-object for placing
227
- result[the_local_id].order_state.forcast # return_value
228
- end
244
+ q = Queue.new
245
+ ib = IB::Connection.current
246
+ the_local_id = nil
247
+ req = ib.subscribe( :OpenOrder ){|m| q << m.order if m.order.local_id.to_i == the_local_id.to_i }
248
+
249
+ result = ->(l){ orders.detect{|x| x.local_id == l && x.submitted? } }
250
+ order.what_if = true
251
+ order.account = account
252
+ the_local_id = order.place contract
253
+ Thread.new{ sleep 1 ; q.close }
254
+ returned_order = q.pop
255
+ ib.unsubscribe req
256
+ order.what_if = false # reset what_if flag
257
+ order.local_id = nil # reset local_id to enable re-using the order-object for placing
258
+ raise IB::SymbolError,"(Preview-) #{order.to_human} is not transmitted properly" if q.closed?
259
+ returned_order.order_state.forcast # return_value
260
+ end
229
261
 
230
262
  # closes the contract by submitting an appropiate order
231
263
  # the action- and total_amount attributes of the assigned order are overwritten.
@@ -254,12 +286,12 @@ This has to be done manualy in the provided block
254
286
  error "Cannot transmit the order – No Contract given " unless order.contract.is_a?(IB::Contract)
255
287
 
256
288
  the_quantity = if reverse
257
- -contract_size[order.contract] * 2
258
- elsif order.total_quantity.abs < 1 && !order.total_quantity.zero?
259
- -contract_size[order.contract] * order.total_quantity.abs
260
- else
261
- -contract_size[order.contract]
262
- end
289
+ -contract_size[order.contract] * 2
290
+ elsif order.total_quantity.abs < 1 && !order.total_quantity.zero?
291
+ -contract_size[order.contract] * order.total_quantity.abs
292
+ else
293
+ -contract_size[order.contract]
294
+ end
263
295
  if the_quantity.zero?
264
296
  logger.info{ "Cannot close #{order.contract.to_human} - no position detected"}
265
297
  else
@@ -280,7 +312,7 @@ This has to be done manualy in the provided block
280
312
  #
281
313
  # Watchlist => [ contract => [ portfoliopositon] , ... ] ]
282
314
  #
283
- def organize_portfolio_positions the_watchlists
315
+ def organize_portfolio_positions the_watchlists= IB::Gateway.current.active_watchlists
284
316
  the_watchlists = [ the_watchlists ] unless the_watchlists.is_a?(Array)
285
317
  self.focuses = portfolio_values.map do | pw |
286
318
  z= the_watchlists.map do | w |
@@ -333,4 +365,5 @@ This has to be done manualy in the provided block
333
365
  #<PortfolioValue: DU167348 Pos=-3 @ 142.574;Value=-4277.22;PNL=-867.72 unrealized;<Option: ESTX50 20181221 put 3200.0 EUR>
334
366
  # => nil
335
367
  # #
368
+
336
369
  end ## module
@@ -0,0 +1,19 @@
1
+ module IB
2
+ class Bag
3
+ def included_in? account
4
+ # iterate over combo-legs
5
+ # and return the bag if all con_id's are present in the account.contracts-map
6
+ self if combo_legs.map do |c_l|
7
+ account.locate_contract c_l.con_id
8
+ end.count == combo_legs.count
9
+ end
10
+
11
+ # returns an array of portfolio-values
12
+ #
13
+ def portfolio_value account
14
+ combo_legs.map do | c_l |
15
+ account.portfolio_values.detect{|x| x.contract.con_id == c_l.con_id}
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+
2
+ module IB
3
+ class Contract
4
+ def included_in? account
5
+ self if account.locate_contract(con_id)
6
+ end
7
+
8
+ def portfolio_value account
9
+ if con_id.to_i > 0
10
+ account.portfolio_values.detect{|x| x.contract.con_id == con_id }
11
+ else
12
+ account.portfolio_values.detect{|x| x.contract == self }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module IB
2
+ class Future
3
+ # helper method to roll an existing future
4
+ #
5
+ # Argument is the expiry of the target-future.
6
+ #
7
+
8
+ def roll **args
9
+ error "specify expiry to roll a future" if args.empty?
10
+ args[:to] = args[:expiry] if args[:expiry].present? && args[:expiry] =~ /[mwMW]$/
11
+ args[:expiry]= IB::Spread.transform_distance( expiry, args.delete(:to )) if args[:to].present?
12
+
13
+ new_future = merge( **args ).verify.first
14
+ error "Cannot roll future; target is no IB::Contract" unless new_future.is_a? IB::Future
15
+ target = IB::Spread.new exchange: exchange, symbol: symbol, currency: currency
16
+ target.add_leg self, action: :buy
17
+ target.add_leg new_future, action: :sell
18
+ end
19
+ end
20
+ end
@@ -1,20 +1,27 @@
1
1
  module IB
2
2
  class Option
3
- def roll expiry, strike
4
- if strike.to_i > 2000
5
- expiry, strike = strike, expiry # exchange values if input occurs in wrong direction
6
- end
7
- new_option = Option.new( invariant_attributes.merge( con_id: nil, trading_class: '', last_trading_day: nil,
8
- local_symbol: "",
9
- expiry: expiry, strike: strike ))
10
- n_o = new_option.verify.first # get con_id
3
+ # helper method to roll an existing option
4
+ #
5
+ # Arguments are strike and expiry of the target-option.
6
+ #
7
+ # Example: ge= Symbols::Options.ge.verify.first.roll( strike: 13 )
8
+ # ge.to_human
9
+ # => " added <Option: GE 20210917 call 7.0 SMART USD> added <Option: GE 20210917 call 13.0 SMART USD>"
10
+ #
11
+ # rolls the Option to another strike
11
12
 
13
+ def roll **args
14
+ error "specify strike and expiry to roll option" if args.empty?
15
+ args[:to] = args[:expiry] if args[:expiry].present? && args[:expiry] =~ /[mwMW]$/
16
+ args[:expiry]= IB::Spread.transform_distance( expiry, args.delete(:to )) if args[:to].present?
17
+
18
+ new_option = merge( **args ).verify.first
19
+ myself = con_id.to_i.zero? ? self.verify.first : self
20
+ error "Cannot roll option; target is no IB::Contract" unless new_option.is_a? IB::Option
21
+ error "Cannot roll option; Option cannot be verified" unless myself.is_a? IB::Option
12
22
  target = IB::Spread.new exchange: exchange, symbol: symbol, currency: currency
13
- target.add_leg self, action: :buy
14
- target.add_leg n_o, action: :sell
15
- rescue NoMethodError
16
- Connection.logger.error "Rolling not possible. #{new_option.to_human} could not be verified"
17
- nil
23
+ target.add_leg myself, action: :buy
24
+ target.add_leg new_option, action: :sell
18
25
  end
19
26
  end
20
27
  end
@@ -1,9 +1,6 @@
1
1
  require 'ib/verify'
2
2
  require 'ib/market-price'
3
3
  module IB
4
- # define a custom ErrorClass which can be fired if a verification fails
5
- class VerifyError < StandardError
6
- end
7
4
 
8
5
  class Contract
9
6
 
@@ -22,6 +19,8 @@ class Contract
22
19
  def option_chain ref_price: :request, right: :put, sort: :strike, exchange: '', trading_class: nil
23
20
 
24
21
  ib = Connection.current
22
+
23
+ # binary interthread communication
25
24
  finalize = Queue.new
26
25
 
27
26
  ## Enable Cashing of Definition-Matrix
@@ -61,7 +60,6 @@ class Contract
61
60
  sec_type: c[:sec_type]
62
61
 
63
62
  finalize.pop # wait until data appeared
64
- #i=0; loop { sleep 0.1; break if i> 1000 || finalize; i+=1 }
65
63
 
66
64
  ib.unsubscribe sub_sdop, sub_ocd
67
65
  else
@@ -131,8 +129,8 @@ class Contract
131
129
  end # def
132
130
 
133
131
  # return a set of AtTheMoneyOptions
134
- def atm_options ref_price: :request, right: :put
135
- option_chain( right: right, ref_price: ref_price, sort: :expiry) do | chain |
132
+ def atm_options ref_price: :request, right: :put, **params
133
+ option_chain( right: right, ref_price: ref_price, sort: :expiry, **params) do | chain |
136
134
  chain[0]
137
135
  end
138
136
 
@@ -1,29 +1,29 @@
1
1
  module IB
2
2
 
3
3
 
4
-
5
4
  class Option
6
- # Ask for the Greeks and implied Vola
7
- #
5
+ # Ask for the Greeks and implied Vola
6
+ #
8
7
  # The result can be customized by a provided block.
9
- #
10
- # IB::Symbols::Options.aapl.greeks{ |x| x }
11
- # -> {"bid"=>0.10142e3, "ask"=>0.10144e3, "last"=>0.10142e3, "close"=>0.10172e3}
12
- #
8
+ #
9
+ # IB::Symbols::Options.aapl.greeks{ |x| x }
10
+ # -> {"bid"=>0.10142e3, "ask"=>0.10144e3, "last"=>0.10142e3, "close"=>0.10172e3}
11
+ #
13
12
  # Possible values for Parameter :what --> :all :model, :bid, :ask, :bidask, :last
14
- #
13
+ #
15
14
  def request_greeks delayed: true, what: :model, thread: false
16
15
 
17
16
  tws = Connection.current # get the initialized ib-ruby instance
18
17
  # define requested tick-attributes
19
18
  request_data_type = IB::MARKET_DATA_TYPES.rassoc( delayed ? :frozen_delayed : :frozen ).first
20
19
  # possible types = [ [ :delayed_model_option , :model_option ] , [:delayed_last_option , :last_option ],
21
- # [ :delayed_bid_option , :bid_option ], [ :delayed_ask_option , :ask_option ] ]
20
+ # [ :delayed_bid_option , :bid_option ], [ :delayed_ask_option , :ask_option ]]
22
21
  tws.send_message :RequestMarketDataType, :market_data_type => request_data_type
23
22
  tickdata = []
24
23
 
25
24
  self.greek = OptionDetail.new if greek.nil?
26
25
  greek.updated_at = Time.now
26
+ greek.option = self
27
27
  queue = Queue.new
28
28
 
29
29
  #keep the method-call running until the request finished
@@ -32,9 +32,9 @@ module IB
32
32
  th = Thread.new do
33
33
  the_id = nil
34
34
  # subscribe to TickPrices
35
- s_id = tws.subscribe(:TickSnapshotEnd) { |msg| queue.push(true) if msg.ticker_id == the_id }
36
- e_id = tws.subscribe(:Alert){|x| queue.push(false) if [200,353].include?( x.code) && x.error_id == the_id }
37
- t_id = tws.subscribe( :TickSnapshotEnd, :TickPrice, :TickString, :TickSize, :TickGeneric, :MarketDataType ) {|msg| msg }
35
+ s_id = tws.subscribe(:TickSnapshotEnd) { |msg| queue.push(true) if msg.ticker_id == the_id }
36
+ e_id = tws.subscribe(:Alert){|x| queue.push(false) if [200,353].include?( x.code) && x.error_id == the_id }
37
+ t_id = tws.subscribe( :TickSnapshotEnd, :TickPrice, :TickString, :TickSize, :TickGeneric, :MarketDataType, :TickRequestParameters ) {|msg| msg }
38
38
  # TWS Error 200: No security definition has been found for the request
39
39
  # TWS Error 354: Requested market data is not subscribed.
40
40
 
@@ -56,22 +56,28 @@ module IB
56
56
  (bf + msg.greeks.keys).each{ |a| greek.send( a.to_s+"=", msg.send( a)) }
57
57
  tickdata << msg if [ :all, :model ].include?( what )
58
58
  end
59
- queue.push(true) if tickdata.is_a?(IB::Messages::Incoming::TickOption) || (tickdata.size == 2 && what== :bidask) || (tickdata.size == 4 && what == :all)
59
+ # fast entry abortion ---> daiabled for now
60
+ # queue.push(true) if tickdata.is_a?(IB::Messages::Incoming::TickOption) || (tickdata.size == 2 && what== :bidask) || (tickdata.size == 4 && what == :all)
60
61
  end
61
62
  end # if sub_id
62
63
 
63
64
  # initialize »the_id« that is used to identify the received tick messages
64
65
  # by firing the market data request
65
- the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
66
+ iji = 0
67
+ loop do
68
+ the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
66
69
 
67
- result = queue.pop
68
- # reduce :close_price delayed_close to close a.s.o
69
- if result == false
70
- Connection.logger.info{ "#{to_human} --> No Marketdata received " }
71
- else
72
- self.misc = tickdata if thread # store internally if in thread modus
70
+ result = queue.pop
71
+ # reduce :close_price delayed_close to close a.s.o
72
+ if result == false
73
+ Connection.logger.info{ "#{to_human} --> No Marketdata received " }
74
+ else
75
+ self.misc = tickdata if thread # store internally if in thread modus
76
+ end
77
+ break if !tickdata.empty? || iji > 10
78
+ iji = iji + 1
79
+ Connection.logger.info{ "OptionGreeks::#{to_human} --> delayed processing. Trying again (#{iji}) " }
73
80
  end
74
-
75
81
  tws.unsubscribe sub_id, s_id, e_id, t_id
76
82
  end # thread
77
83
  if thread