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 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