ib-extensions 1.1 → 1.2
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/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
|