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.
- checksums.yaml +4 -4
- data/Gemfile +2 -3
- data/Gemfile.lock +26 -26
- data/README.md +56 -10
- data/bin/console +10 -3
- data/bin/gateway +14 -9
- data/changelog.md +54 -0
- data/ib-extensions.gemspec +6 -4
- data/lib/ib/alerts/base-alert.rb +10 -13
- data/lib/ib/eod.rb +239 -127
- data/lib/ib/extensions/version.rb +1 -1
- data/lib/ib/extensions.rb +5 -0
- data/lib/ib/gateway/account-infos.rb +74 -47
- data/lib/ib/gateway/order-handling.rb +57 -25
- data/lib/ib/gateway.rb +45 -31
- data/lib/ib/market-price.rb +34 -34
- data/lib/ib/models/account.rb +177 -144
- data/lib/ib/models/bag.rb +19 -0
- data/lib/ib/models/contract.rb +16 -0
- data/lib/ib/models/future.rb +20 -0
- data/lib/ib/models/option.rb +20 -13
- data/lib/ib/option-chain.rb +4 -6
- data/lib/ib/option-greeks.rb +27 -21
- data/lib/ib/order_prototypes/all-in-one.rb +46 -0
- data/lib/ib/plot-poec.rb +60 -0
- data/lib/ib/probability_of_expiring.rb +109 -0
- data/lib/ib/spread-prototypes.rb +1 -0
- data/lib/ib/spread_prototypes/butterfly.rb +2 -4
- data/lib/ib/spread_prototypes/calendar.rb +25 -23
- data/lib/ib/spread_prototypes/straddle.rb +3 -3
- data/lib/ib/spread_prototypes/strangle.rb +8 -9
- data/lib/ib/spread_prototypes/vertical.rb +6 -7
- data/lib/ib/verify.rb +34 -39
- data/lib/ib-gateway.rb +12 -0
- metadata +53 -5
data/lib/ib/models/account.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
module IB
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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.
|
47
|
-
contract = contract.verify.first
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
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
|
-
|
87
|
+
g.preview contract: j36, order: order
|
89
88
|
=> {:init_margin=>0.10864874e6,
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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
|
-
|
182
|
-
|
183
|
-
|
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
|
-
|
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::
|
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
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
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
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
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
|
-
|
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
|
data/lib/ib/models/option.rb
CHANGED
@@ -1,20 +1,27 @@
|
|
1
1
|
module IB
|
2
2
|
class Option
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
14
|
-
target.add_leg
|
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
|
data/lib/ib/option-chain.rb
CHANGED
@@ -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
|
|
data/lib/ib/option-greeks.rb
CHANGED
@@ -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
|
-
#
|
11
|
-
#
|
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)
|
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
|
-
|
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
|
-
|
66
|
+
iji = 0
|
67
|
+
loop do
|
68
|
+
the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
|
66
69
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|