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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57f9f4c819b5b0af154107d63a7cb970b2ab9209ef849f88246c185d287d3202
4
- data.tar.gz: c8572a97aa46fe3ef9331bb7a4247f33ef43f5b4584cbd6223770ac1adcd3ced
3
+ metadata.gz: f8b69b2b7e20244f0c30ce1f354eb1f413abf2ec19e7955522b74c8dff693708
4
+ data.tar.gz: 3f6572cf816ba536a148ec6d843be20cc7c85e35a4b7ab63198e98b1c4f52ae0
5
5
  SHA512:
6
- metadata.gz: 383509cac1aa159affe7e8c73b62d0ea60fe825987b0cd8efaa60d24b50cd9f4eb8f6085c4b9d9bc22d0a06923a774df0c59436eabcfa4563620da60ae964503
7
- data.tar.gz: c615ef1b9948f1db60d868911f6f169df14425c42ef50e6e2dad8082f8e1fe8666d47626251e0ec3e7e1dc8e2b0e25bb01d233c4c45da8c2935cbf39cf08a21b
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
- r = block_given? ? msg.results.map{|y| yield y} : msg.results
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
- Timeout::timeout(5) do # max 5 sec.
136
- sleep 0.1
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
- r # the collected result
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
- def associate_ticdata
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
 
@@ -1,5 +1,5 @@
1
1
  module IB
2
2
  module Extensions
3
- VERSION = "1.1"
3
+ VERSION = "1.2"
4
4
  end
5
5
  end
@@ -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
- #Raises IB::Error
45
- # if no Marketdata Subscription is present and delayed: false is specified
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
- tws= Connection.current # get the initialized ib-ruby instance
55
- the_id , the_price = nil, nil
56
- tickdata = Hash.new
57
- # define requested tick-attributes
58
- last, close, bid, ask = [ [ :delayed_last , :last_price ] , [:delayed_close , :close_price ],
59
- [ :delayed_bid , :bid_price ], [ :delayed_ask , :ask_price ]]
60
- request_data_type = delayed ? :frozen_delayed : :frozen
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
- tws.send_message :RequestMarketDataType, :market_data_type => IB::MARKET_DATA_TYPES.rassoc( request_data_type).first
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
- #keep the method-call running until the request finished
65
- #and cancel subscriptions to the message handler
66
- # method returns the (running) thread
67
- th = Thread.new do
68
- finalize, raise_delay_alert = false, false
69
- s_id = tws.subscribe(:TickSnapshotEnd){|x| finalize = true if x.ticker_id == the_id }
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
- e_id = tws.subscribe(:Alert){|x| raise_delay_alert = true if x.code == 354 && x.error_id == the_id }
72
- # TWS Error 354: Requested market data is not subscribed.
73
- # r_id = tws.subscribe(:TickRequestParameters) {|x| } # raise_snapshot_alert = true if x.snapshot_permissions.to_i.zero? && x.ticker_id == the_id }
74
-
75
- # subscribe to TickPrices
76
- sub_id = tws.subscribe(:TickPrice ) do |msg| #, :TickSize, :TickGeneric, :TickOption) do |msg|
77
- [last,close,bid,ask].each do |x|
78
- tickdata[x] = msg.the_data[:price] if x.include?( IB::TICK_TYPES[ msg.the_data[:tick_type]])
79
- # fast exit condition
80
- finalize = true if tickdata.size >= 4 || ( tickdata[bid].present? && tickdata[ask].present? )
81
- end if msg.ticker_id == the_id
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
- # todo implement config-feature to set timeout in configuration (DRY-Feature)
88
- # Alternative zu Timeout
89
- # Thread.new do
90
- # i=0; loop{ i+=1; sleep 0.1; break if finalize || i > 1000 }
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
- i=0;
93
- loop{ i+=1; break if i > 1000 || finalize || raise_delay_alert; sleep 0.05 }
94
- tws.unsubscribe sub_id, s_id, e_id
95
- # reduce :close_price delayed_close to close a.s.o
96
- if raise_delay_alert && !delayed
97
- error "No Marketdata Subscription, use delayed data <-- #{to_human}"
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
- end
125
- if thread
126
- th # return thread
127
- else
128
- th.join
129
- the_price # return
130
- end
131
- end #
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
@@ -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 > .active_accounts.first.focuses.to_a.to_human
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>
@@ -9,17 +9,20 @@ class Contract
9
9
 
10
10
 
11
11
 
12
- # returns the Option Chain of the contract (if available)
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
- def option_chain ref_price: :request, right: :put, sort: :strike, exchange: ''
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 = Queue.new
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 , sub_ocd
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 = if block_given?
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 = @option_chain_definition[:strikes].min +
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 = yield the_grouped_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 = @option_chain_definition[:expirations].find_all{|y| (15..21).include? y.day }
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
 
@@ -14,31 +14,32 @@ module IB
14
14
  #
15
15
  def request_greeks delayed: true, what: :model, thread: false
16
16
 
17
- tws= Connection.current # get the initialized ib-ruby instance
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 : :frozen ).first
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
- greek.updated_at = Time.now
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
- s_id = tws.subscribe(:TickSnapshotEnd) { |msg| finalize = true if msg.ticker_id == the_id }
36
- e_id = tws.subscribe(:Alert){|x| finalize = true if [200,353].include?( x.code) && x.error_id == the_id }
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
- tickdata = tickdata &.first unless [:bidask, :all].include? what
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
- begin
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
- self.misc = tickdata if thread # store internally if in thread modus
73
- end
74
- rescue Timeout::Error
75
- Connection.logger.info{ "#{to_human} --> No Marketdata received " }
76
- end
77
- tws.unsubscribe sub_id, s_id, e_id
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
- tickdata # return
81
+ greek
84
82
  end
85
- end #
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.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-05-16 00:00:00.000000000 Z
11
+ date: 2021-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ox