Class: IB::Contract
- Inherits:
-
Object
- Object
- IB::Contract
- Includes:
- Eod, ProbabilityOfExpiring
- Defined in:
- lib/ib/eod.rb,
lib/ib/verify.rb,
lib/ib/market-price.rb,
lib/ib/option-chain.rb,
lib/ib/models/contract.rb,
lib/ib/extensions/contract.rb,
lib/ib/probability_of_expiring.rb
Overview
end
Instance Method Summary collapse
-
#atm_options(ref_price: :request, right: :put, **params) ⇒ Object
return a set of AtTheMoneyOptions.
- #included_in?(account) ⇒ Boolean
-
#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).
-
#necessary_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.
- #portfolio_value(account) ⇒ Object
-
#verify(thread: nil, &b) ⇒ Object
verifies the contract.
-
#verify! ⇒ Object
depreciated: Do not use anymore.
Methods included from ProbabilityOfExpiring
#probability_of_assignment, #probability_of_expiring
Methods included from Eod
#eod, #from_csv, #get_bars, #to_csv
Instance Method Details
#atm_options(ref_price: :request, right: :put, **params) ⇒ Object
return a set of AtTheMoneyOptions
132 133 134 135 136 137 138 |
# File 'lib/ib/option-chain.rb', line 132 def ref_price: :request, right: :put, **params option_chain( right: right, ref_price: ref_price, sort: :expiry, **params) do | chain | chain[0] end end |
#included_in?(account) ⇒ Boolean
4 5 6 |
# File 'lib/ib/models/contract.rb', line 4 def included_in? account self if account.locate_contract(con_id) end |
#itm_options(count: 5, right: :put, ref_price: :request, sort: :strike, exchange: '') ⇒ Object
return InTheMoneyOptions
141 142 143 144 145 146 147 148 149 |
# File 'lib/ib/option-chain.rb', line 141 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 TWS returns :TickSnapshotEnd if no ticks are transmitted # we don't have to implement out own 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.debug result. case result.code when 200 q.close error "#{to_human} --> #{result.}" unless no_error when 354, # not subscribed to market data 10167, 10168 if delayed && !(result. =~ /market data is not available/) tws.logger.debug "#{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 |
#necessary_attributes ⇒ Object
returns a hash
62 63 64 65 66 67 68 69 70 71 |
# File 'lib/ib/verify.rb', line 62 def necessary_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 )
19 20 21 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 |
# File 'lib/ib/option-chain.rb', line 19 def option_chain ref_price: :request, right: :put, sort: :strike, exchange: '', trading_class: nil ib = Connection.current # binary interthread communication 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 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
152 153 154 155 156 157 158 159 160 161 |
# File 'lib/ib/option-chain.rb', line 152 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 |
#portfolio_value(account) ⇒ Object
8 9 10 11 12 13 14 |
# File 'lib/ib/models/contract.rb', line 8 def portfolio_value account if con_id.to_i > 0 account.portfolio_values.detect{|x| x.contract.con_id == con_id } else account.portfolio_values.detect{|x| x.contract == self } 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
53 54 55 56 57 58 59 |
# File 'lib/ib/verify.rb', line 53 def verify thread: nil, &b if thread Thread.new { _verify &b } else _verify &b end end |
#verify! ⇒ Object
depreciated: Do not use anymore
75 76 77 78 79 80 81 82 |
# File 'lib/ib/verify.rb', line 75 def verify! c = 0 IB::Connection.logger.warn "Contract.verify! is depreciated. Use \"contract = contract.verify.first\" instead" c= verify.first self.attributes = c.invariant_attributes self.contract_detail = c.contract_detail self end |