Class: IB::Contract
- Inherits:
-
Object
- Object
- IB::Contract
- Defined in:
- lib/ib/verify.rb,
lib/ib/eod.rb,
lib/ib/market-price.rb,
lib/ib/option-chain.rb,
lib/ib/extensions/contract.rb
Overview
end
Instance Method Summary collapse
-
#atm_options(ref_price: :request, right: :put) ⇒ Object
return a set of AtTheMoneyOptions.
-
#eod(start: nil, to: Date.today, duration: nil, what: :trades) ⇒ Object
puts Symbols::Stocks.wfc.eod( to: Date.new(2019,10,9), duration: 3 ) <Bar: 2019-10-04 wap 48.964 OHLC 48.61 49.25 48.54 49.21 trades 9899 vol 50561> <Bar: 2019-10-07 wap 48.9445 OHLC 48.91 49.29 48.75 48.81 trades 10317 vol 50189> <Bar: 2019-10-08 wap 47.9165 OHLC 48.25 48.34 47.55 47.82 trades 12607 vol 53577>.
-
#from_csv(file: nil) ⇒ Object
read csv-data into bars.
-
#itm_options(count: 5, right: :put, ref_price: :request, sort: :strike, exchange: '') ⇒ Object
return InTheMoneyOptions.
-
#market_price(delayed: true, thread: false, no_error: false) ⇒ Object
Raw-data are stored in the bars-attribute of IB::Contract (volatile, ie. data are not preserved when the Object is copied) Example: IB::Stock.new(symbol: :ge).market_price returns the current market-price.
-
#nessesary_attributes ⇒ Object
returns a hash.
-
#option_chain(ref_price: :request, right: :put, sort: :strike, exchange: '', trading_class: nil) ⇒ Object
returns the Option Chain (monthly options, expiry: third friday) of the contract (if available).
-
#otm_options(count: 5, right: :put, ref_price: :request, sort: :strike, exchange: '') ⇒ Object
return OutOfTheMoneyOptions.
-
#to_csv(file: nil) ⇒ Object
creates (or overwrites) the specified file (or symbol.csv) and saves bar-data.
-
#verify(thread: nil, &b) ⇒ Object
verifies the contract.
-
#verify! ⇒ Object
depreciated: Do not use anymore.
Instance Method Details
#atm_options(ref_price: :request, right: :put) ⇒ Object
return a set of AtTheMoneyOptions
134 135 136 137 138 139 140 |
# File 'lib/ib/option-chain.rb', line 134 def ref_price: :request, right: :put option_chain( right: right, ref_price: ref_price, sort: :expiry) do | chain | chain[0] end end |
#eod(start: nil, to: Date.today, duration: nil, what: :trades) ⇒ Object
puts Symbols::Stocks.wfc.eod( to: Date.new(2019,10,9), duration: 3 ) <Bar: 2019-10-04 wap 48.964 OHLC 48.61 49.25 48.54 49.21 trades 9899 vol 50561> <Bar: 2019-10-07 wap 48.9445 OHLC 48.91 49.29 48.75 48.81 trades 10317 vol 50189> <Bar: 2019-10-08 wap 47.9165 OHLC 48.25 48.34 47.55 47.82 trades 12607 vol 53577>
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/ib/eod.rb', line 94 def eod start:nil, to: Date.today, duration: nil , what: :trades tws = IB::Connection.current recieved = Queue.new r = nil # the hole response is transmitted at once! a = tws.subscribe(IB::Messages::Incoming::HistoricalData) do |msg| if msg.request_id == con_id # msg.results.each { |entry| puts " #{entry}" } self. = msg.results end recieved.push Time.now end b = tws.subscribe( IB::Messages::Incoming::Alert) do |msg| if [321,162,200].include? msg.code tws.logger.info msg. # TWS Error 200: No security definition has been found for the request # TWS Error 354: Requested market data is not subscribed. # TWS Error 162 # Historical Market Data Service error recieved.close end end duration = if duration.present? duration.is_a?(String) ? duration : duration.to_s + " D" elsif start.present? BuisinesDays.business_days_between(start, to).to_s + " D" else "1 D" end tws. IB::Messages::Outgoing::RequestHistoricalData.new( :request_id => con_id, :contract => self, :end_date_time => to.to_time.to_ib, # Time.now.to_ib, :duration => duration, # ? :bar_size => :day1, # IB::BAR_SIZES.key(:hour)? :what_to_show => what, :use_rth => 0, :format_date => 2, :keep_up_todate => 0) recieved.pop # blocks until a message is ready on the queue or the queue is closed tws.unsubscribe a tws.unsubscribe b block_given? ? .map{|y| yield y} : # return bars or result of block end |
#from_csv(file: nil) ⇒ Object
read csv-data into bars
156 157 158 159 160 161 162 |
# File 'lib/ib/eod.rb', line 156 def from_csv file: nil file ||= "#{symbol}.csv" self. = [] CSV.foreach( file, headers: true, header_converters: :symbol) do |row| self. << IB::Bar.new( **row.to_h ) end end |
#itm_options(count: 5, right: :put, ref_price: :request, sort: :strike, exchange: '') ⇒ Object
return InTheMoneyOptions
143 144 145 146 147 148 149 150 151 |
# File 'lib/ib/option-chain.rb', line 143 def count: 5, right: :put, ref_price: :request, sort: :strike, exchange: '' option_chain( right: right, ref_price: ref_price, sort: sort, exchange: exchange ) do | chain | if right == :put above_market_price_strikes = chain[1][0..count-1] else below_market_price_strikes = chain[-1][-count..-1].reverse end # branch end end |
#market_price(delayed: true, thread: false, no_error: false) ⇒ Object
Raw-data are stored in the bars-attribute of IB::Contract
(volatile, ie. data are not preserved when the Object is copied)
Example: IB::Stock.new(symbol: :ge).market_price returns the current market-price
Example: IB::Stock.new(symbol: :ge).market_price(thread: true).join assigns IB::Symbols.sie.misc with the value of the :last (or delayed_last) TickPrice-Message and returns this value, too
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/ib/market-price.rb', line 45 def market_price delayed: true, thread: false, no_error: false tws= Connection.current # get the initialized ib-ruby instance the_id , the_price = nil, nil tickdata = Hash.new q = Queue.new # define requested tick-attributes last, close, bid, ask = [ [ :delayed_last , :last_price ] , [:delayed_close , :close_price ], [ :delayed_bid , :bid_price ], [ :delayed_ask , :ask_price ]] request_data_type = delayed ? :frozen_delayed : :frozen # From the tws-documentation (https://interactivebrokers.github.io/tws-api/market_data_type.html) # Beginning in TWS v970, a IBApi.EClient.reqMarketDataType callback of 1 will occur automatically # after invoking reqMktData if the user has live data permissions for the instrument. # # so - even if "delayed" is specified, realtime-data are returned if RT-permissions are present # # method returns the (running) thread th = Thread.new do # about 11 sec after the request, the TES returns :TickSnapshotEnd if no ticks are transmitted # we don't have to implement out ow timeout-criteria s_id = tws.subscribe(:TickSnapshotEnd){|x| q.push(true) if x.ticker_id == the_id } a_id = tws.subscribe(:Alert){|x| q.push(x) if [200, 354, 10167, 10168].include?( x.code ) && x.error_id == the_id } # TWS Error 354: Requested market data is not subscribed. # r_id = tws.subscribe(:TickRequestParameters) {|x| } # raise_snapshot_alert = true if x.snapshot_permissions.to_i.zero? && x.ticker_id == the_id } # subscribe to TickPrices sub_id = tws.subscribe(:TickPrice ) do |msg| #, :TickSize, :TickGeneric, :TickOption) do |msg| [last,close,bid,ask].each do |x| tickdata[x] = msg.the_data[:price] if x.include?( IB::TICK_TYPES[ msg.the_data[:tick_type]]) # fast exit condition q.push(true) if tickdata.size >= 4 end if msg.ticker_id == the_id end # initialize »the_id« that is used to identify the received tick messages # by firing the market data request the_id = tws. :RequestMarketData, contract: self , snapshot: true while !q.closed? do result = q.pop if result.is_a? IB::Messages::Incoming::Alert tws.logger.info result. case result.code when 200 q.close error "#{to_human} --> #{result.message}" unless no_error when 354, # not subscribed to market data 10167, 10168 if delayed tws.logger.info "#{to_human} --> requesting delayed data" tws. :RequestMarketDataType, :market_data_type => 3 self.misc = :delayed sleep 0.1 the_id = tws. :RequestMarketData, contract: self , snapshot: true else q.close tws.logger.error "#{to_human} --> No marketdata permissions" unless no_error end end elsif result.present? q.close tz = -> (z){ z.map{|y| y.to_s.split('_')}.flatten.count_duplicates.max_by{|k,v| v}.first.to_sym} data = tickdata.map{|x,y| [tz[x],y]}.to_h valid_data = ->(d){ !(d.to_i.zero? || d.to_i == -1) } self. << data # store raw data in bars the_price = if block_given? yield data # yields {:bid=>0.10142e3, :ask=>0.10144e3, :last=>0.10142e3, :close=>0.10172e3} else # behavior if no block is provided if valid_data[data[:last]] data[:last] elsif valid_data[data[:bid]] (data[:bid]+data[:ask])/2 elsif data[:close].present? data[:close] else nil end end self.misc = misc == :delayed ? { :delayed => the_price } : { realtime: the_price } else q.close error "#{to_human} --> No Marketdata received " end end tws.unsubscribe sub_id, s_id, a_id end if thread th # return thread else th.join the_price # return end end |
#nessesary_attributes ⇒ Object
returns a hash
57 58 59 60 61 62 63 64 65 66 |
# File 'lib/ib/verify.rb', line 57 def nessesary_attributes v= { stock: { currency: 'USD', exchange: 'SMART', symbol: nil}, option: { currency: 'USD', exchange: 'SMART', right: 'P', expiry: nil, strike: nil, symbol: nil}, future: { currency: 'USD', exchange: nil, expiry: nil, symbol: nil }, forex: { currency: 'USD', exchange: 'IDEALPRO', symbol: nil } } sec_type.present? ? v[sec_type] : { con_id: nil, exchange: 'SMART' } # enables to use only con_id for verifying # if the contract allows SMART routing end |
#option_chain(ref_price: :request, right: :put, sort: :strike, exchange: '', trading_class: nil) ⇒ Object
returns the Option Chain (monthly options, expiry: third friday) of the contract (if available)
parameters
- right
-
:call, :put, :straddle ( default: :put )
- ref_price
-
:request or a numeric value ( default: :request )
- sort
-
:strike, :expiry
- exchange
-
List of Exchanges to be queried (Blank for all available Exchanges)
trading_class ( optional )
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/ib/option-chain.rb', line 22 def option_chain ref_price: :request, right: :put, sort: :strike, exchange: '', trading_class: nil ib = Connection.current finalize = Queue.new ## Enable Cashing of Definition-Matrix @option_chain_definition ||= [] my_req = nil # ----------------------------------------------------------------------------------------------------- # get OptionChainDefinition from IB ( instantiate cashed Hash ) if @option_chain_definition.blank? sub_sdop = ib.subscribe( :SecurityDefinitionOptionParameterEnd ) { |msg| finalize.push(true) if msg.request_id == my_req } sub_ocd = ib.subscribe( :OptionChainDefinition ) do | msg | if msg.request_id == my_req = msg.data # transfer the first record to @option_chain_definition if @option_chain_definition.blank? @option_chain_definition = msg.data end # override @option_chain_definition if a decent combination of attributes is met # us- options: use the smart dataset # other options: prefer options of the default trading class if [:exchange] == 'SMART' @option_chain_definition = msg.data finalize.push(true) end if [:trading_class] == symbol @option_chain_definition = msg.data finalize.push(true) end end end c = verify.first # ensure a complete set of attributes my_req = ib. :RequestOptionChainDefinition, con_id: c.con_id, symbol: c.symbol, exchange: c.sec_type == :future ? c.exchange : "", # BOX,CBOE', sec_type: c[:sec_type] finalize.pop # wait until data appeared #i=0; loop { sleep 0.1; break if i> 1000 || finalize; i+=1 } ib.unsubscribe sub_sdop, sub_ocd else Connection.logger.info { "#{to_human} : using cached data" } end # ----------------------------------------------------------------------------------------------------- # select values and assign to options # unless @option_chain_definition.blank? requested_strikes = if block_given? ref_price = market_price if ref_price == :request if ref_price.nil? ref_price = @option_chain_definition[:strikes].min + ( @option_chain_definition[:strikes].max - @option_chain_definition[:strikes].min ) / 2 Connection.logger.warn { "#{to_human} :: market price not set – using midpoint of available strikes instead: #{ref_price.to_f}" } end atm_strike = @option_chain_definition[:strikes].min_by { |x| (x - ref_price).abs } the_grouped_strikes = @option_chain_definition[:strikes].group_by{|e| e <=> atm_strike} begin the_strikes = yield the_grouped_strikes the_strikes.unshift atm_strike unless the_strikes.first == atm_strike # the first item is the atm-strike the_strikes rescue Connection.logger.error "#{to_human} :: not enough strikes :#{@option_chain_definition[:strikes].map(&:to_f).join(',')} " [] end else @option_chain_definition[:strikes] end # third Friday of a month monthly_expirations = @option_chain_definition[:expirations].find_all {|y| (15..21).include? y.day } # puts @option_chain_definition.inspect option_prototype = -> ( ltd, strike ) do IB::Option.new( symbol: symbol, exchange: @option_chain_definition[:exchange], trading_class: @option_chain_definition[:trading_class], multiplier: @option_chain_definition[:multiplier], currency: currency, last_trading_day: ltd, strike: strike, right: right).verify &.first end = -> ( schema ) do # Array: [ yymm -> Options] prepares for the correct conversion to a Hash Hash[ monthly_expirations.map do | l_t_d | [ l_t_d.strftime('%y%m').to_i , schema.map { | strike | option_prototype[ l_t_d, strike ]}.compact ] end ] # by Hash[ ] end = -> ( schema ) do Hash[ schema.map do | strike | [ strike , monthly_expirations.map { | l_t_d | option_prototype[ l_t_d, strike ]}.compact ] end ] # by Hash[ ] end if sort == :strike [ requested_strikes ] else [ requested_strikes ] end else Connection.logger.error "#{to_human} ::No Options available" nil # return_value end end |
#otm_options(count: 5, right: :put, ref_price: :request, sort: :strike, exchange: '') ⇒ Object
return OutOfTheMoneyOptions
154 155 156 157 158 159 160 161 162 163 |
# File 'lib/ib/option-chain.rb', line 154 def count: 5, right: :put, ref_price: :request, sort: :strike, exchange: '' option_chain( right: right, ref_price: ref_price, sort: sort, exchange: exchange ) do | chain | if right == :put # puts "Chain: #{chain}" below_market_price_strikes = chain[-1][-count..-1].reverse else above_market_price_strikes = chain[1][0..count-1] end end end |
#to_csv(file: nil) ⇒ Object
creates (or overwrites) the specified file (or symbol.csv) and saves bar-data
146 147 148 149 150 151 152 153 |
# File 'lib/ib/eod.rb', line 146 def to_csv file:nil file ||= "#{symbol}.csv" if .present? headers = .first.invariant_attributes.keys CSV.open( file, 'w' ) {|f| f << headers ; .each {|y| f << y.invariant_attributes.values } } end end |
#verify(thread: nil, &b) ⇒ Object
verifies the contract
returns the number of contracts returned by the TWS.
The method accepts a block. The queried contract-Object is accessible there. If multiple contracts are specified, the block is executed with each of these contracts.
Verify returns an Array of contracts. The operation leaves the contract untouched.
Returns nil if the contract could not be verified.
> s = Stock.new symbol: 'AA'
=> #<IB::Stock:0x0000000002626cc0
@attributes={:symbol=>"AA", :con_id=>0, :right=>"", :include_expired=>false,
:sec_type=>"STK", :currency=>"USD", :exchange=>"SMART"}
> sp = s.verify.first.essential
=> #<IB::Stock:0x00000000025a3cf8
@attributes={:symbol=>"AA", :con_id=>251962528, :exchange=>"SMART", :currency=>"USD",
:strike=>0.0, :local_symbol=>"AA", :multiplier=>0, :primary_exchange=>"NYSE",
:trading_class=>"AA", :sec_type=>"STK", :right=>"", :include_expired=>false}
> s = Stock.new symbol: 'invalid'
=> @attributes={:symbol=>"invalid", :sec_type=>"STK", :currency=>"USD", :exchange=>"SMART"}
> sp = s.verify
=> []
Takes a Block to modify the queried contracts
f = Future.new symbol: ‘M2K’
con_ids = f.verify{ |c| c.con_id }
[412889018, 428519982, 446091466, 461318872, 477836981]
Parameter: thread: (true/false)
If multiple contracts are to be verified, they can be queried simultaneously.
IB::Symbols::W500.map{|c| c.verify(thread: true){ |vc| do_something }}.join
51 52 53 54 |
# File 'lib/ib/verify.rb', line 51 def verify thread: nil, &b return [self] if contract_detail.present? || sec_type == :bag _verify update: false, thread: thread, &b # returns the allocated threads end |
#verify! ⇒ Object
depreciated: Do not use anymore
70 71 72 73 74 75 76 |
# File 'lib/ib/verify.rb', line 70 def verify! c = 0 IB::Connection.logger.warn "Contract.verify! is depreciated. Use \"contract = contract.verify.first\" instead" _verify( update: true){| response | c+=1 } # wait for the returned thread to finish IB::Connection.logger.error { "Multible Contracts detected during verify!." } if c > 1 self end |