require 'ib-api' ib = IB::Connection.new ib.activate_plugin :market_price # or "market-price"in your scripts.
Basics
The market-price
plugin provides a handy IB::Contract#market_price
method, easing the process of collecting
market price data from the TWS.
Basically IB::Contract.market_price
combines the process of sending a :RequestMarketData
-Message, collecting the TickType-Response and returning evaluated data. The results can be further customized in a block.
zn = Symbols::Futures.zn
zn.market_price => 132.78125
zn.bars => [{:bid=>132.765625, :ask=>132.78125, :last=>132.78125, :close=>132.859375}]
zn.misc => {:realtime=>132.78125}
zn.market_price{|y| y[:close]}
=> 132.859375
The default response is to return the :last
-price, if present. Otherwise the average of :bid
and :ask
.
If this fails too, the :close
-price is returned.
Snapshot, delayed and frozen Data
Market data subscriptions are expensive and in many cases not needed. For most contracts, the TWS-API offers delayed data, too. They can be accessed without subscription.
If no marketdata subscription is detected, market_price
automatically switches to delayed data.
This behavior can be controlled by the parameter delayed: false
# assuming market data for stocks trading in singapore are not subscribed
c = Stock.new symbol: :'C09', exchange: 'SGX', currency: :sgd
c.market_price delayed: false
INFO::Requested market data is not subscribed. Delayed market data is not enabled.
ERROR::<Stock: C09 sgd SGX> --> No marketdata permissions
=> nil
c.market_price delayed: true
INFO::Requested market data is not subscribed. Delayed market data is not enabled.
INFO::<Stock: C09 sgd SGX> --> requesting delayed data
=> 7.67
c.bars => [{:bid=>7.67, :ask=>7.69, :last=>7.67, :close=>7.67}]
c.misc => {:delayed=>7.67}
Exercise
Assuming, a watchlist W20
containing 19 single stocks exists.
How to fetch effectively market prices of each of the contracts?
How to extend this to larger collections?
Creating the Watchlist
> symbols = %W( ANW AEB AED AEG AEH AER HIVE AJRD AET AMG AFL MITT AGCO A AEM ADC AL APD AYR )
> IB::Symbols.allocate_collection :w20 => IB::Symbols::W20
> symbols.each_with_index{ |s,i| IB::Symbols::W20.add_contract i, IB::Stock.new( symbol: s ) }
> IB::Symbols::W20.size => 19
Sequential Access to Market Prices
Because the IB::Symbols-Enumerator
yields IB::Contract's
its just possible to fetch market prices sequentially by traversing though the elements of the Enumerator.
# first filter invalid symbols via Contract.verify
prices = IB::Symbols::W20.map {|s| s.verify.first &.market_price }
=> [nil, 24.97, nil, 4.73, nil, 59.0, nil, 48.35, nil, 165.93, 56.82, 4.2, 138.72, 137.54, 71.7, 69.77, 47.08, 298.28, nil]
# non existing stocks are omitted
> Symbols::W20.map( &:symbol ).zip( prices ).to_h
=> {"ANW"=>nil, "AEB"=>24.97, "AED"=>nil, "AEG"=>4.73, "AEH"=>nil,
"AER"=>59.0, "HIVE"=>nil, "AJRD"=>48.35, "AET"=>nil, "AMG"=>165.93,
"AFL"=>56.82,"MITT"=>4.2, "AGCO"=>138.72,"A"=>137.54, "AEM"=>71.7,
"ADC"=>69.77,"AL"=>47.08, "APD"=>298.28, "AYR"=>nil}
In an experiment on a public holiday, it took 105 seconds to fetch the data for just 19 IB::Contracts
. This is not acceptable.
Asynchronous Access
The alternative approach: fire the :RequestMarketData
messages for all contract at once and wait
for the response.
IB::Contract.market_price
has a threaded-modus
, which collects data in background.
start = Time.now;
# the parameter no_error prevents interrupts in case the symbol does not exists
prices = Symbols::W20.map{|x| x.market_price(thread: true, no_error: true )}.each( &:join);
puts "eclipsed Time: #{Time.now - start }" ==> eclipsed Time: 5.206946497
Symbols::W20.map{ |s| [s.symbol , s.misc.values.first ] }.to_h
=> {"ANW"=>nil, "AEB"=>24.97, "AED"=>nil, "AEG"=>4.73, "AEH"=>nil,
"AER"=>59.0, "HIVE"=>nil, "AJRD"=>48.35, "AET"=>nil, "AMG"=>165.93,
"AFL"=>56.82,"MITT"=>4.2, "AGCO"=>138.72,"A"=>137.54, "AEM"=>71.7,
"ADC"=>69.77,"AL"=>47.08, "APD"=>298.28, "AYR"=>nil}
Definitively an improvement!
In threaded mode the method fires all :RequestMarketData
messages at once and then waits for the TWS to respond.
The approach has the disadvantage, that the thread itself has to be returned. If thread: true
is specified
the fetched market-price is stored in IB::Contract.misc
, raw-data are still present in IB::Contract.bars
.
```