ib-extensions 1.1 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ib/eod.rb +24 -7
- data/lib/ib/extensions/contract.rb +2 -30
- data/lib/ib/extensions/version.rb +1 -1
- data/lib/ib/market-price.rb +104 -93
- data/lib/ib/models/account.rb +2 -2
- data/lib/ib/option-chain.rb +32 -57
- data/lib/ib/option-greeks.rb +19 -22
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8b69b2b7e20244f0c30ce1f354eb1f413abf2ec19e7955522b74c8dff693708
|
4
|
+
data.tar.gz: 3f6572cf816ba536a148ec6d843be20cc7c85e35a4b7ab63198e98b1c4f52ae0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4fbc954cb9619f01cc4b58340b12e62d9600c248822d593993125b0b0aab2aaee8b9d8bdfd4d4748a470be1fbf4c7a4055e21501f3bd8a7b47a2cd0d89e5873
|
7
|
+
data.tar.gz: 06f0f9e79db5c1af97ac6f991aa72fde91d883440e7e26b41c1d0bef700f8edfd04b040bf9756fdb2c637aefc4d529a9769a5ccb9e1b956d7756b9064eeefcd7
|
data/lib/ib/eod.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module IB
|
2
2
|
require 'active_support/core_ext/date/calculations'
|
3
|
+
require 'csv'
|
3
4
|
module BuisinesDays
|
4
5
|
# https://stackoverflow.com/questions/4027768/calculate-number-of-business-days-between-two-days
|
5
6
|
|
@@ -99,7 +100,7 @@ require 'active_support/core_ext/date/calculations'
|
|
99
100
|
a = tws.subscribe(IB::Messages::Incoming::HistoricalData) do |msg|
|
100
101
|
if msg.request_id == con_id
|
101
102
|
# msg.results.each { |entry| puts " #{entry}" }
|
102
|
-
|
103
|
+
self.bars = msg.results
|
103
104
|
end
|
104
105
|
recieved.push Time.now
|
105
106
|
end
|
@@ -132,17 +133,33 @@ require 'active_support/core_ext/date/calculations'
|
|
132
133
|
:format_date => 2,
|
133
134
|
:keep_up_todate => 0)
|
134
135
|
|
135
|
-
|
136
|
-
|
137
|
-
recieved.pop # blocks until a message is ready on the queue
|
138
|
-
break if recieved.closed? || recieved.empty? # finish if data received
|
139
|
-
end
|
136
|
+
recieved.pop # blocks until a message is ready on the queue or the queue is closed
|
137
|
+
|
140
138
|
tws.unsubscribe a
|
141
139
|
tws.unsubscribe b
|
142
140
|
|
143
|
-
|
141
|
+
block_given? ? bars.map{|y| yield y} : bars # return bars or result of block
|
144
142
|
|
145
143
|
end # def
|
144
|
+
|
145
|
+
# creates (or overwrites) the specified file (or symbol.csv) and saves bar-data
|
146
|
+
def to_csv file:nil
|
147
|
+
file ||= "#{symbol}.csv"
|
148
|
+
|
149
|
+
if bars.present?
|
150
|
+
headers = bars.first.invariant_attributes.keys
|
151
|
+
CSV.open( file, 'w' ) {|f| f << headers ; bars.each {|y| f << y.invariant_attributes.values } }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# read csv-data into bars
|
156
|
+
def from_csv file: nil
|
157
|
+
file ||= "#{symbol}.csv"
|
158
|
+
self.bars = []
|
159
|
+
CSV.foreach( file, headers: true, header_converters: :symbol) do |row|
|
160
|
+
self.bars << IB::Bar.new( **row.to_h )
|
161
|
+
end
|
162
|
+
end
|
146
163
|
end # class
|
147
164
|
end # module
|
148
165
|
|
@@ -1,34 +1,6 @@
|
|
1
|
+
module IB
|
1
2
|
|
2
|
-
|
3
|
-
|
4
|
-
tws= IB::Gateway.tws # get the initialized ib-ruby instance
|
5
|
-
the_id = nil
|
6
|
-
finalize= false
|
7
|
-
# switch to delayed data
|
8
|
-
tws.send_message :RequestMarketDataType, :market_data_type => :delayed
|
9
|
-
|
10
|
-
s_id = tws.subscribe(:TickSnapshotEnd) { |msg| finalize = true if msg.ticker_id == the_id }
|
11
|
-
|
12
|
-
sub_id = tws.subscribe(:TickPrice, :TickSize, :TickGeneric, :TickOption) do |msg|
|
13
|
-
self.bars << msg.the_data if msg.ticker_id == the_id
|
14
|
-
end
|
15
|
-
|
16
|
-
# initialize »the_id« that is used to identify the received tick messages
|
17
|
-
# by firing the market data request
|
18
|
-
the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
|
19
|
-
|
20
|
-
#keep the method-call running until the request finished
|
21
|
-
#and cancel subscriptions to the message handler.
|
22
|
-
Thread.new do
|
23
|
-
i=0; loop{ i+=1; sleep 0.1; break if finalize || i > 1000 }
|
24
|
-
tws.unsubscribe sub_id
|
25
|
-
tws.unsubscribe s_id
|
26
|
-
puts "#{symbol} data gathered"
|
27
|
-
end # method returns the (running) thread
|
28
|
-
|
29
|
-
end # def
|
30
|
-
###################### private methods
|
31
|
-
|
3
|
+
class Contract
|
32
4
|
end # class
|
33
5
|
|
34
6
|
|
data/lib/ib/market-price.rb
CHANGED
@@ -29,106 +29,117 @@ module IB
|
|
29
29
|
#
|
30
30
|
# IB::Symbols::Stocks.sie.market_price{ |x| x }
|
31
31
|
# -> {"bid"=>0.10142e3, "ask"=>0.10144e3, "last"=>0.10142e3, "close"=>0.10172e3}
|
32
|
-
#
|
33
|
-
#
|
34
|
-
# Raw-data are stored in the _bars_-attribute of IB::Contract
|
35
|
-
# (volatile, ie. data are not preserved when the Object is copied)
|
36
|
-
#
|
37
|
-
#Example: IB::Stock.new(symbol: :ge).market_price
|
38
|
-
# returns the current market-price
|
39
|
-
#
|
40
|
-
#Example: IB::Stock.new(symbol: :ge).market_price(thread: true).join
|
41
|
-
# assigns IB::Symbols.sie.misc with the value of the :last (or delayed_last) TickPrice-Message
|
42
|
-
# and returns this value, too
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# Solutions: Catch the Error and retry with delayed: true
|
49
|
-
#
|
50
|
-
# if that fails use alternative exchanges (look to Contract.valid_exchanges)
|
51
|
-
#
|
52
|
-
def market_price delayed: true, thread: false
|
32
|
+
#
|
33
|
+
#
|
34
|
+
# Raw-data are stored in the _bars_-attribute of IB::Contract
|
35
|
+
# (volatile, ie. data are not preserved when the Object is copied)
|
36
|
+
#
|
37
|
+
#Example: IB::Stock.new(symbol: :ge).market_price
|
38
|
+
# returns the current market-price
|
39
|
+
#
|
40
|
+
#Example: IB::Stock.new(symbol: :ge).market_price(thread: true).join
|
41
|
+
# assigns IB::Symbols.sie.misc with the value of the :last (or delayed_last) TickPrice-Message
|
42
|
+
# and returns this value, too
|
43
|
+
#
|
44
|
+
|
45
|
+
def market_price delayed: true, thread: false, no_error: false
|
53
46
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
47
|
+
tws= Connection.current # get the initialized ib-ruby instance
|
48
|
+
the_id , the_price = nil, nil
|
49
|
+
tickdata = Hash.new
|
50
|
+
q = Queue.new
|
51
|
+
# define requested tick-attributes
|
52
|
+
last, close, bid, ask = [ [ :delayed_last , :last_price ] , [:delayed_close , :close_price ],
|
53
|
+
[ :delayed_bid , :bid_price ], [ :delayed_ask , :ask_price ]]
|
54
|
+
request_data_type = delayed ? :frozen_delayed : :frozen
|
61
55
|
|
62
|
-
|
56
|
+
# From the tws-documentation (https://interactivebrokers.github.io/tws-api/market_data_type.html)
|
57
|
+
# Beginning in TWS v970, a IBApi.EClient.reqMarketDataType callback of 1 will occur automatically
|
58
|
+
# after invoking reqMktData if the user has live data permissions for the instrument.
|
59
|
+
#
|
60
|
+
# so - even if "delayed" is specified, realtime-data are returned if RT-permissions are present
|
61
|
+
#
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
# method returns the (running) thread
|
64
|
+
th = Thread.new do
|
65
|
+
# about 11 sec after the request, the TES returns :TickSnapshotEnd if no ticks are transmitted
|
66
|
+
# we don't have to implement out ow timeout-criteria
|
67
|
+
s_id = tws.subscribe(:TickSnapshotEnd){|x| q.push(true) if x.ticker_id == the_id }
|
68
|
+
a_id = tws.subscribe(:Alert){|x| q.push(x) if [200, 354, 10167, 10168].include?( x.code ) && x.error_id == the_id }
|
69
|
+
# TWS Error 354: Requested market data is not subscribed.
|
70
|
+
# r_id = tws.subscribe(:TickRequestParameters) {|x| } # raise_snapshot_alert = true if x.snapshot_permissions.to_i.zero? && x.ticker_id == the_id }
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end
|
83
|
-
# initialize »the_id« that is used to identify the received tick messages
|
84
|
-
# by firing the market data request
|
85
|
-
the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
|
72
|
+
# subscribe to TickPrices
|
73
|
+
sub_id = tws.subscribe(:TickPrice ) do |msg| #, :TickSize, :TickGeneric, :TickOption) do |msg|
|
74
|
+
[last,close,bid,ask].each do |x|
|
75
|
+
tickdata[x] = msg.the_data[:price] if x.include?( IB::TICK_TYPES[ msg.the_data[:tick_type]])
|
76
|
+
# fast exit condition
|
77
|
+
q.push(true) if tickdata.size >= 4
|
78
|
+
end if msg.ticker_id == the_id
|
79
|
+
end
|
80
|
+
# initialize »the_id« that is used to identify the received tick messages
|
81
|
+
# by firing the market data request
|
82
|
+
the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
|
86
83
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
84
|
+
while !q.closed? do
|
85
|
+
result = q.pop
|
86
|
+
if result.is_a? IB::Messages::Incoming::Alert
|
87
|
+
tws.logger.info result.message
|
88
|
+
case result.code
|
89
|
+
when 200
|
90
|
+
q.close
|
91
|
+
error "#{to_human} --> #{result.message}" unless no_error
|
92
|
+
when 354, # not subscribed to market data
|
93
|
+
10167,
|
94
|
+
10168
|
95
|
+
if delayed
|
96
|
+
tws.logger.info "#{to_human} --> requesting delayed data"
|
97
|
+
tws.send_message :RequestMarketDataType, :market_data_type => 3
|
98
|
+
self.misc = :delayed
|
99
|
+
sleep 0.1
|
100
|
+
the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
|
101
|
+
else
|
102
|
+
q.close
|
103
|
+
tws.logger.error "#{to_human} --> No marketdata permissions" unless no_error
|
104
|
+
end
|
105
|
+
end
|
106
|
+
elsif result.present?
|
107
|
+
q.close
|
108
|
+
tz = -> (z){ z.map{|y| y.to_s.split('_')}.flatten.count_duplicates.max_by{|k,v| v}.first.to_sym}
|
109
|
+
data = tickdata.map{|x,y| [tz[x],y]}.to_h
|
110
|
+
valid_data = ->(d){ !(d.to_i.zero? || d.to_i == -1) }
|
111
|
+
self.bars << data # store raw data in bars
|
112
|
+
the_price = if block_given?
|
113
|
+
yield data
|
114
|
+
# yields {:bid=>0.10142e3, :ask=>0.10144e3, :last=>0.10142e3, :close=>0.10172e3}
|
115
|
+
else # behavior if no block is provided
|
116
|
+
if valid_data[data[:last]]
|
117
|
+
data[:last]
|
118
|
+
elsif valid_data[data[:bid]]
|
119
|
+
(data[:bid]+data[:ask])/2
|
120
|
+
elsif data[:close].present?
|
121
|
+
data[:close]
|
122
|
+
else
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
end
|
91
126
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
# elsif raise_snapshot_alert
|
99
|
-
# error "No Snapshot Permissions, try alternative exchange <-- #{to_human}"
|
100
|
-
elsif i <= 1000
|
101
|
-
tz = -> (z){ z.map{|y| y.to_s.split('_')}.flatten.count_duplicates.max_by{|k,v| v}.first.to_sym}
|
102
|
-
data = tickdata.map{|x,y| [tz[x],y]}.to_h
|
103
|
-
valid_data = ->(d){ !(d.to_i.zero? || d.to_i == -1) }
|
104
|
-
self.bars << data # store raw data in bars
|
105
|
-
the_price = if block_given?
|
106
|
-
yield data
|
107
|
-
# yields {:bid=>0.10142e3, :ask=>0.10144e3, :last=>0.10142e3, :close=>0.10172e3}
|
108
|
-
else # behavior if no block is provided
|
109
|
-
if valid_data[data[:last]]
|
110
|
-
data[:last]
|
111
|
-
elsif valid_data[data[:bid]]
|
112
|
-
(data[:bid]+data[:ask])/2
|
113
|
-
elsif data[:close].present?
|
114
|
-
data[:close]
|
115
|
-
else
|
116
|
-
nil
|
117
|
-
end
|
118
|
-
end
|
119
|
-
self.misc = the_price if thread # store internally if in thread modus
|
120
|
-
else # i > 1000
|
121
|
-
tws.logger.info{ "#{to_human} --> No Marketdata received " }
|
122
|
-
end
|
127
|
+
self.misc = misc == :delayed ? { :delayed => the_price } : { realtime: the_price }
|
128
|
+
else
|
129
|
+
q.close
|
130
|
+
error "#{to_human} --> No Marketdata received "
|
131
|
+
end
|
132
|
+
end
|
123
133
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
134
|
+
tws.unsubscribe sub_id, s_id, a_id
|
135
|
+
end
|
136
|
+
if thread
|
137
|
+
th # return thread
|
138
|
+
else
|
139
|
+
th.join
|
140
|
+
the_price # return
|
141
|
+
end
|
142
|
+
end #
|
132
143
|
|
133
144
|
end
|
134
145
|
end
|
data/lib/ib/models/account.rb
CHANGED
@@ -34,7 +34,7 @@ Thus if several Orders are placed with the same order_ref, the active one is ret
|
|
34
34
|
|
35
35
|
=end
|
36
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 ,
|
37
|
+
search_option = [ local_id.present? ? [:local_id , local_id] : nil ,
|
38
38
|
perm_id.present? ? [:perm_id, perm_id] : nil,
|
39
39
|
order_ref.present? ? [:order_ref , order_ref ] : nil ].compact.first
|
40
40
|
matched_items = if search_option.nil?
|
@@ -310,7 +310,7 @@ This has to be done manualy in the provided block
|
|
310
310
|
end # class
|
311
311
|
##
|
312
312
|
# in the console (call gateway with watchlist: [:Spreads, :BuyAndHold])
|
313
|
-
#head :001 > .
|
313
|
+
#head :001 > .clients.first.focuses.to_a.to_human
|
314
314
|
#Unspecified
|
315
315
|
#<Stock: BLUE EUR SBF>
|
316
316
|
#<PortfolioValue: DU167348 Pos=720 @ 15.88;Value=11433.24;PNL=-4870.05 unrealized;<Stock: BLUE EUR SBF>
|
data/lib/ib/option-chain.rb
CHANGED
@@ -9,17 +9,20 @@ class Contract
|
|
9
9
|
|
10
10
|
|
11
11
|
|
12
|
-
# returns the Option Chain
|
12
|
+
# returns the Option Chain (monthly options, expiry: third friday)
|
13
|
+
# of the contract (if available)
|
14
|
+
#
|
13
15
|
#
|
14
16
|
## parameters
|
15
|
-
### right:: :call, :put, :straddle
|
16
|
-
### ref_price:: :request or a numeric value
|
17
|
+
### right:: :call, :put, :straddle ( default: :put )
|
18
|
+
### ref_price:: :request or a numeric value ( default: :request )
|
17
19
|
### sort:: :strike, :expiry
|
18
20
|
### exchange:: List of Exchanges to be queried (Blank for all available Exchanges)
|
19
|
-
|
21
|
+
### trading_class ( optional )
|
22
|
+
def option_chain ref_price: :request, right: :put, sort: :strike, exchange: '', trading_class: nil
|
20
23
|
|
21
24
|
ib = Connection.current
|
22
|
-
finalize =
|
25
|
+
finalize = Queue.new
|
23
26
|
|
24
27
|
## Enable Cashing of Definition-Matrix
|
25
28
|
@option_chain_definition ||= []
|
@@ -60,7 +63,7 @@ class Contract
|
|
60
63
|
finalize.pop # wait until data appeared
|
61
64
|
#i=0; loop { sleep 0.1; break if i> 1000 || finalize; i+=1 }
|
62
65
|
|
63
|
-
ib.unsubscribe sub_sdop
|
66
|
+
ib.unsubscribe sub_sdop, sub_ocd
|
64
67
|
else
|
65
68
|
Connection.logger.info { "#{to_human} : using cached data" }
|
66
69
|
end
|
@@ -69,18 +72,18 @@ class Contract
|
|
69
72
|
# select values and assign to options
|
70
73
|
#
|
71
74
|
unless @option_chain_definition.blank?
|
72
|
-
requested_strikes =
|
75
|
+
requested_strikes = if block_given?
|
73
76
|
ref_price = market_price if ref_price == :request
|
74
77
|
if ref_price.nil?
|
75
|
-
ref_price =
|
76
|
-
( @option_chain_definition[:strikes].max -
|
77
|
-
@option_chain_definition[:strikes].min ) / 2
|
78
|
+
ref_price = @option_chain_definition[:strikes].min +
|
79
|
+
( @option_chain_definition[:strikes].max -
|
80
|
+
@option_chain_definition[:strikes].min ) / 2
|
78
81
|
Connection.logger.warn { "#{to_human} :: market price not set – using midpoint of available strikes instead: #{ref_price.to_f}" }
|
79
82
|
end
|
80
83
|
atm_strike = @option_chain_definition[:strikes].min_by { |x| (x - ref_price).abs }
|
81
84
|
the_grouped_strikes = @option_chain_definition[:strikes].group_by{|e| e <=> atm_strike}
|
82
85
|
begin
|
83
|
-
the_strikes =
|
86
|
+
the_strikes = yield the_grouped_strikes
|
84
87
|
the_strikes.unshift atm_strike unless the_strikes.first == atm_strike # the first item is the atm-strike
|
85
88
|
the_strikes
|
86
89
|
rescue
|
@@ -92,34 +95,34 @@ class Contract
|
|
92
95
|
end
|
93
96
|
|
94
97
|
# third Friday of a month
|
95
|
-
monthly_expirations =
|
98
|
+
monthly_expirations = @option_chain_definition[:expirations].find_all {|y| (15..21).include? y.day }
|
96
99
|
# puts @option_chain_definition.inspect
|
97
|
-
option_prototype = -> ( ltd, strike ) do
|
98
|
-
IB::Option.new symbol: symbol,
|
99
|
-
exchange: @option_chain_definition[:exchange],
|
100
|
-
trading_class: @option_chain_definition[:trading_class],
|
101
|
-
multiplier: @option_chain_definition[:multiplier],
|
102
|
-
currency: currency,
|
103
|
-
last_trading_day: ltd,
|
104
|
-
strike: strike,
|
105
|
-
right: right
|
100
|
+
option_prototype = -> ( ltd, strike ) do
|
101
|
+
IB::Option.new( symbol: symbol,
|
102
|
+
exchange: @option_chain_definition[:exchange],
|
103
|
+
trading_class: @option_chain_definition[:trading_class],
|
104
|
+
multiplier: @option_chain_definition[:multiplier],
|
105
|
+
currency: currency,
|
106
|
+
last_trading_day: ltd,
|
107
|
+
strike: strike,
|
108
|
+
right: right).verify &.first
|
106
109
|
end
|
107
110
|
options_by_expiry = -> ( schema ) do
|
108
111
|
# Array: [ yymm -> Options] prepares for the correct conversion to a Hash
|
109
112
|
Hash[ monthly_expirations.map do | l_t_d |
|
110
|
-
[ l_t_d.strftime('%y%m').to_i , schema.map{ | strike | option_prototype[ l_t_d, strike ]}.compact ]
|
113
|
+
[ l_t_d.strftime('%y%m').to_i , schema.map { | strike | option_prototype[ l_t_d, strike ]}.compact ]
|
111
114
|
end ] # by Hash[ ]
|
112
115
|
end
|
113
116
|
options_by_strike = -> ( schema ) do
|
114
117
|
Hash[ schema.map do | strike |
|
115
|
-
[ strike , monthly_expirations.map{ | l_t_d | option_prototype[ l_t_d, strike ]}.compact ]
|
118
|
+
[ strike , monthly_expirations.map { | l_t_d | option_prototype[ l_t_d, strike ]}.compact ]
|
116
119
|
end ] # by Hash[ ]
|
117
120
|
end
|
118
121
|
|
119
122
|
if sort == :strike
|
120
|
-
options_by_strike[ requested_strikes ]
|
123
|
+
options_by_strike[ requested_strikes ]
|
121
124
|
else
|
122
|
-
options_by_expiry[ requested_strikes ]
|
125
|
+
options_by_expiry[ requested_strikes ]
|
123
126
|
end
|
124
127
|
else
|
125
128
|
Connection.logger.error "#{to_human} ::No Options available"
|
@@ -137,8 +140,8 @@ class Contract
|
|
137
140
|
end
|
138
141
|
|
139
142
|
# return InTheMoneyOptions
|
140
|
-
def itm_options count: 5, right: :put, ref_price: :request, sort: :strike
|
141
|
-
option_chain( right: right, ref_price: ref_price, sort: sort ) do | chain |
|
143
|
+
def itm_options count: 5, right: :put, ref_price: :request, sort: :strike, exchange: ''
|
144
|
+
option_chain( right: right, ref_price: ref_price, sort: sort, exchange: exchange ) do | chain |
|
142
145
|
if right == :put
|
143
146
|
above_market_price_strikes = chain[1][0..count-1]
|
144
147
|
else
|
@@ -148,8 +151,8 @@ class Contract
|
|
148
151
|
end # def
|
149
152
|
|
150
153
|
# return OutOfTheMoneyOptions
|
151
|
-
def otm_options count: 5, right: :put, ref_price: :request, sort: :strike
|
152
|
-
option_chain( right: right, ref_price: ref_price, sort: sort ) do | chain |
|
154
|
+
def otm_options count: 5, right: :put, ref_price: :request, sort: :strike, exchange: ''
|
155
|
+
option_chain( right: right, ref_price: ref_price, sort: sort, exchange: exchange ) do | chain |
|
153
156
|
if right == :put
|
154
157
|
# puts "Chain: #{chain}"
|
155
158
|
below_market_price_strikes = chain[-1][-count..-1].reverse
|
@@ -160,34 +163,6 @@ class Contract
|
|
160
163
|
end
|
161
164
|
|
162
165
|
|
163
|
-
def associate_ticdata
|
164
|
-
|
165
|
-
tws= IB::Connection.current # get the initialized ib-ruby instance
|
166
|
-
the_id = nil
|
167
|
-
finalize= false
|
168
|
-
# switch to delayed data
|
169
|
-
tws.send_message :RequestMarketDataType, :market_data_type => :delayed
|
170
|
-
|
171
|
-
s_id = tws.subscribe(:TickSnapshotEnd) { |msg| finalize = true if msg.ticker_id == the_id }
|
172
|
-
|
173
|
-
sub_id = tws.subscribe(:TickPrice, :TickSize, :TickGeneric, :TickOption) do |msg|
|
174
|
-
self.bars << msg.the_data if msg.ticker_id == the_id
|
175
|
-
end
|
176
|
-
|
177
|
-
# initialize »the_id« that is used to identify the received tick messages
|
178
|
-
# by firing the market data request
|
179
|
-
the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
|
180
|
-
|
181
|
-
#keep the method-call running until the request finished
|
182
|
-
#and cancel subscriptions to the message handler.
|
183
|
-
Thread.new do
|
184
|
-
i=0; loop{ i+=1; sleep 0.1; break if finalize || i > 1000 }
|
185
|
-
tws.unsubscribe sub_id
|
186
|
-
tws.unsubscribe s_id
|
187
|
-
#puts "#{symbol} data gathered"
|
188
|
-
end # method returns the (running) thread
|
189
|
-
|
190
|
-
end # def
|
191
166
|
end # class
|
192
167
|
|
193
168
|
|
data/lib/ib/option-greeks.rb
CHANGED
@@ -14,31 +14,32 @@ module IB
|
|
14
14
|
#
|
15
15
|
def request_greeks delayed: true, what: :model, thread: false
|
16
16
|
|
17
|
-
tws=
|
17
|
+
tws = Connection.current # get the initialized ib-ruby instance
|
18
18
|
# define requested tick-attributes
|
19
|
-
request_data_type = IB::MARKET_DATA_TYPES.rassoc( delayed ? :frozen_delayed :
|
19
|
+
request_data_type = IB::MARKET_DATA_TYPES.rassoc( delayed ? :frozen_delayed : :frozen ).first
|
20
20
|
# possible types = [ [ :delayed_model_option , :model_option ] , [:delayed_last_option , :last_option ],
|
21
21
|
# [ :delayed_bid_option , :bid_option ], [ :delayed_ask_option , :ask_option ] ]
|
22
22
|
tws.send_message :RequestMarketDataType, :market_data_type => request_data_type
|
23
23
|
tickdata = []
|
24
24
|
|
25
25
|
self.greek = OptionDetail.new if greek.nil?
|
26
|
-
|
26
|
+
greek.updated_at = Time.now
|
27
|
+
queue = Queue.new
|
27
28
|
|
28
29
|
#keep the method-call running until the request finished
|
29
30
|
#and cancel subscriptions to the message handler
|
30
31
|
# method returns the (running) thread
|
31
32
|
th = Thread.new do
|
32
33
|
the_id = nil
|
33
|
-
finalize= false
|
34
34
|
# subscribe to TickPrices
|
35
|
-
|
36
|
-
|
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 }
|
37
38
|
# TWS Error 200: No security definition has been found for the request
|
38
39
|
# TWS Error 354: Requested market data is not subscribed.
|
39
40
|
|
40
41
|
sub_id = tws.subscribe(:TickOption ) do |msg| #, :TickSize, :TickGeneric do |msg|
|
41
|
-
if msg.ticker_id == the_id && tickdata.is_a?(Array) # do nothing if tickdata have already gathered
|
42
|
+
if msg.ticker_id == the_id # && tickdata.is_a?(Array) # do nothing if tickdata have already gathered
|
42
43
|
case msg.type
|
43
44
|
when /ask/
|
44
45
|
greek.ask_price = msg.option_price unless msg.option_price.nil?
|
@@ -55,8 +56,7 @@ module IB
|
|
55
56
|
(bf + msg.greeks.keys).each{ |a| greek.send( a.to_s+"=", msg.send( a)) }
|
56
57
|
tickdata << msg if [ :all, :model ].include?( what )
|
57
58
|
end
|
58
|
-
|
59
|
-
finalize = true if tickdata.is_a?(IB::Messages::Incoming::TickOption) || (tickdata.size == 2 && what== :bidask) || (tickdata.size == 4 && what == :all)
|
59
|
+
queue.push(true) if tickdata.is_a?(IB::Messages::Incoming::TickOption) || (tickdata.size == 2 && what== :bidask) || (tickdata.size == 4 && what == :all)
|
60
60
|
end
|
61
61
|
end # if sub_id
|
62
62
|
|
@@ -64,25 +64,22 @@ module IB
|
|
64
64
|
# by firing the market data request
|
65
65
|
the_id = tws.send_message :RequestMarketData, contract: self , snapshot: true
|
66
66
|
|
67
|
-
|
68
|
-
# todo implement config-feature to set timeout in configuration (DRY-Feature)
|
69
|
-
Timeout::timeout(5) do # max 5 sec.
|
70
|
-
loop{ break if finalize ; sleep 0.05 }
|
67
|
+
result = queue.pop
|
71
68
|
# reduce :close_price delayed_close to close a.s.o
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
73
|
+
end
|
74
|
+
|
75
|
+
tws.unsubscribe sub_id, s_id, e_id, t_id
|
78
76
|
end # thread
|
79
77
|
if thread
|
80
78
|
th # return thread
|
81
79
|
else
|
82
80
|
th.join
|
83
|
-
|
81
|
+
greek
|
84
82
|
end
|
85
|
-
|
86
|
-
|
83
|
+
end
|
87
84
|
end
|
88
85
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ib-extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hartmut Bischoff
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ox
|