ib-extensions 1.0 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/Gemfile.lock +33 -34
- data/README.md +12 -7
- data/bin/console +3 -5
- data/bin/{gateway.rb → gateway} +13 -4
- data/changelog.md +6 -0
- data/ib-extensions.gemspec +1 -0
- data/lib/ib/alerts/base-alert.rb +3 -1
- data/lib/ib/alerts/order-alerts.rb +5 -0
- data/lib/ib/eod.rb +19 -23
- data/lib/ib/extensions.rb +1 -0
- data/lib/ib/extensions/version.rb +1 -1
- data/lib/ib/gateway.rb +77 -63
- data/lib/ib/gateway/account-infos.rb +1 -2
- data/lib/ib/gateway/order-handling.rb +121 -126
- data/lib/ib/market-price.rb +2 -2
- data/lib/ib/models/account.rb +63 -56
- data/lib/ib/models/option.rb +20 -0
- data/lib/ib/option-chain.rb +180 -182
- data/lib/ib/order-prototypes.rb +1 -1
- data/lib/ib/spread-prototypes.rb +2 -2
- data/lib/ib/spread_prototypes/stock-spread.rb +1 -1
- data/lib/ib/verify.rb +192 -222
- metadata +19 -18
- data/examples/cancel_orders +0 -74
- data/examples/eod +0 -35
- data/examples/input.rb +0 -475
- data/examples/market_price +0 -57
- data/examples/option_chain +0 -67
- data/examples/place_and_modify_order +0 -162
- data/examples/place_bracket_order +0 -62
- data/examples/place_butterfly_order +0 -104
- data/examples/place_combo_order +0 -70
- data/examples/place_limit_order +0 -82
- data/examples/place_the_limit_order +0 -145
- data/examples/volatility_research +0 -139
- data/examples/what_if_order +0 -90
- data/lib/ib/models/spread.rb +0 -159
@@ -0,0 +1,20 @@
|
|
1
|
+
module IB
|
2
|
+
class Option
|
3
|
+
def roll expiry, strike
|
4
|
+
if strike.to_i > 2000
|
5
|
+
expiry, strike = strike, expiry # exchange values if input occurs in wrong direction
|
6
|
+
end
|
7
|
+
new_option = Option.new( invariant_attributes.merge( con_id: nil, trading_class: '', last_trading_day: nil,
|
8
|
+
local_symbol: "",
|
9
|
+
expiry: expiry, strike: strike ))
|
10
|
+
n_o = new_option.verify.first # get con_id
|
11
|
+
|
12
|
+
target = IB::Spread.new exchange: exchange, symbol: symbol, currency: currency
|
13
|
+
target.add_leg self, action: :buy
|
14
|
+
target.add_leg n_o, action: :sell
|
15
|
+
rescue NoMethodError
|
16
|
+
Connection.logger.error "Rolling not possible. #{new_option.to_human} could not be verified"
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/ib/option-chain.rb
CHANGED
@@ -8,188 +8,186 @@ end
|
|
8
8
|
class Contract
|
9
9
|
|
10
10
|
|
11
|
-
|
12
|
-
# returns the Option Chain of the contract (if available)
|
13
|
-
#
|
14
|
-
## parameters
|
15
|
-
### right:: :call, :put, :straddle
|
16
|
-
### ref_price:: :request or a numeric value
|
17
|
-
### sort:: :strike, :expiry
|
18
|
-
### exchange:: List of Exchanges to be queried (Blank for all available Exchanges)
|
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
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
end # def
|
11
|
+
|
12
|
+
# returns the Option Chain of the contract (if available)
|
13
|
+
#
|
14
|
+
## parameters
|
15
|
+
### right:: :call, :put, :straddle
|
16
|
+
### ref_price:: :request or a numeric value
|
17
|
+
### sort:: :strike, :expiry
|
18
|
+
### exchange:: List of Exchanges to be queried (Blank for all available Exchanges)
|
19
|
+
def option_chain ref_price: :request, right: :put, sort: :strike, exchange: ''
|
20
|
+
|
21
|
+
ib = Connection.current
|
22
|
+
finalize = Queue.new
|
23
|
+
|
24
|
+
## Enable Cashing of Definition-Matrix
|
25
|
+
@option_chain_definition ||= []
|
26
|
+
|
27
|
+
my_req = nil
|
28
|
+
|
29
|
+
# -----------------------------------------------------------------------------------------------------
|
30
|
+
# get OptionChainDefinition from IB ( instantiate cashed Hash )
|
31
|
+
if @option_chain_definition.blank?
|
32
|
+
sub_sdop = ib.subscribe( :SecurityDefinitionOptionParameterEnd ) { |msg| finalize.push(true) if msg.request_id == my_req }
|
33
|
+
sub_ocd = ib.subscribe( :OptionChainDefinition ) do | msg |
|
34
|
+
if msg.request_id == my_req
|
35
|
+
message = msg.data
|
36
|
+
# transfer the first record to @option_chain_definition
|
37
|
+
if @option_chain_definition.blank?
|
38
|
+
@option_chain_definition = msg.data
|
39
|
+
end
|
40
|
+
# override @option_chain_definition if a decent combination of attributes is met
|
41
|
+
# us- options: use the smart dataset
|
42
|
+
# other options: prefer options of the default trading class
|
43
|
+
if message[:exchange] == 'SMART'
|
44
|
+
@option_chain_definition = msg.data
|
45
|
+
finalize.push(true)
|
46
|
+
end
|
47
|
+
if message[:trading_class] == symbol
|
48
|
+
@option_chain_definition = msg.data
|
49
|
+
finalize.push(true)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
c = verify.first # ensure a complete set of attributes
|
55
|
+
my_req = ib.send_message :RequestOptionChainDefinition, con_id: c.con_id,
|
56
|
+
symbol: c.symbol,
|
57
|
+
exchange: c.sec_type == :future ? c.exchange : "", # BOX,CBOE',
|
58
|
+
sec_type: c[:sec_type]
|
59
|
+
|
60
|
+
finalize.pop # wait until data appeared
|
61
|
+
#i=0; loop { sleep 0.1; break if i> 1000 || finalize; i+=1 }
|
62
|
+
|
63
|
+
ib.unsubscribe sub_sdop , sub_ocd
|
64
|
+
else
|
65
|
+
Connection.logger.info { "#{to_human} : using cached data" }
|
66
|
+
end
|
67
|
+
|
68
|
+
# -----------------------------------------------------------------------------------------------------
|
69
|
+
# select values and assign to options
|
70
|
+
#
|
71
|
+
unless @option_chain_definition.blank?
|
72
|
+
requested_strikes = if block_given?
|
73
|
+
ref_price = market_price if ref_price == :request
|
74
|
+
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
|
+
Connection.logger.warn { "#{to_human} :: market price not set – using midpoint of available strikes instead: #{ref_price.to_f}" }
|
79
|
+
end
|
80
|
+
atm_strike = @option_chain_definition[:strikes].min_by { |x| (x - ref_price).abs }
|
81
|
+
the_grouped_strikes = @option_chain_definition[:strikes].group_by{|e| e <=> atm_strike}
|
82
|
+
begin
|
83
|
+
the_strikes = yield the_grouped_strikes
|
84
|
+
the_strikes.unshift atm_strike unless the_strikes.first == atm_strike # the first item is the atm-strike
|
85
|
+
the_strikes
|
86
|
+
rescue
|
87
|
+
Connection.logger.error "#{to_human} :: not enough strikes :#{@option_chain_definition[:strikes].map(&:to_f).join(',')} "
|
88
|
+
[]
|
89
|
+
end
|
90
|
+
else
|
91
|
+
@option_chain_definition[:strikes]
|
92
|
+
end
|
93
|
+
|
94
|
+
# third Friday of a month
|
95
|
+
monthly_expirations = @option_chain_definition[:expirations].find_all{|y| (15..21).include? y.day }
|
96
|
+
# 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
|
106
|
+
end
|
107
|
+
options_by_expiry = -> ( schema ) do
|
108
|
+
# Array: [ yymm -> Options] prepares for the correct conversion to a Hash
|
109
|
+
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 ]
|
111
|
+
end ] # by Hash[ ]
|
112
|
+
end
|
113
|
+
options_by_strike = -> ( schema ) do
|
114
|
+
Hash[ schema.map do | strike |
|
115
|
+
[ strike , monthly_expirations.map{ | l_t_d | option_prototype[ l_t_d, strike ]}.compact ]
|
116
|
+
end ] # by Hash[ ]
|
117
|
+
end
|
118
|
+
|
119
|
+
if sort == :strike
|
120
|
+
options_by_strike[ requested_strikes ]
|
121
|
+
else
|
122
|
+
options_by_expiry[ requested_strikes ]
|
123
|
+
end
|
124
|
+
else
|
125
|
+
Connection.logger.error "#{to_human} ::No Options available"
|
126
|
+
nil # return_value
|
127
|
+
end
|
128
|
+
end # def
|
129
|
+
|
130
|
+
# return a set of AtTheMoneyOptions
|
131
|
+
def atm_options ref_price: :request, right: :put
|
132
|
+
option_chain( right: right, ref_price: ref_price, sort: :expiry) do | chain |
|
133
|
+
chain[0]
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
# 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 |
|
142
|
+
if right == :put
|
143
|
+
above_market_price_strikes = chain[1][0..count-1]
|
144
|
+
else
|
145
|
+
below_market_price_strikes = chain[-1][-count..-1].reverse
|
146
|
+
end # branch
|
147
|
+
end
|
148
|
+
end # def
|
149
|
+
|
150
|
+
# 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 |
|
153
|
+
if right == :put
|
154
|
+
# puts "Chain: #{chain}"
|
155
|
+
below_market_price_strikes = chain[-1][-count..-1].reverse
|
156
|
+
else
|
157
|
+
above_market_price_strikes = chain[1][0..count-1]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
|
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
|
193
191
|
end # class
|
194
192
|
|
195
193
|
|
data/lib/ib/order-prototypes.rb
CHANGED
data/lib/ib/spread-prototypes.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'ib/models/spread'
|
1
|
+
#require 'ib/models/spread'
|
2
2
|
require 'ib/verify'
|
3
3
|
# These modules are used to facilitate referencing of most common Spreads
|
4
4
|
|
@@ -24,7 +24,7 @@ module IB
|
|
24
24
|
|
25
25
|
def initialize_spread ref_contract = nil, **attributes
|
26
26
|
error "Initializing of Spread failed – contract is missing" unless ref_contract.is_a?(IB::Contract)
|
27
|
-
the_contract = ref_contract.merge( attributes ).verify.first
|
27
|
+
the_contract = ref_contract.merge( **attributes ).verify.first
|
28
28
|
error "Underlying for Spread is not valid: #{ref_contract.to_human}" if the_contract.nil?
|
29
29
|
the_spread= IB::Spread.new the_contract.attributes.slice( :exchange, :symbol, :currency )
|
30
30
|
error "Initializing of Spread failed – Underling is no Contract" if the_spread.nil?
|
@@ -19,7 +19,7 @@ module IB
|
|
19
19
|
def fabricate *underlying, ratio: [1,-1], **args
|
20
20
|
#
|
21
21
|
are_stocks = ->(l){ l.all?{|y| y.is_a? IB::Stock} }
|
22
|
-
legs = underlying.map{|y| y.is_a?( IB::Stock ) ? y.merge(args) : IB::Stock.new( symbol: y ).merge(args)}
|
22
|
+
legs = underlying.map{|y| y.is_a?( IB::Stock ) ? y.merge(**args) : IB::Stock.new( symbol: y ).merge(**args)}
|
23
23
|
error "only spreads with two underyings of type »IB::Stock« are supported" unless legs.size==2 && are_stocks[legs]
|
24
24
|
initialize_spread( legs.first ) do | the_spread |
|
25
25
|
c_l = legs.zip(ratio).map do |l,r|
|
data/lib/ib/verify.rb
CHANGED
@@ -1,226 +1,196 @@
|
|
1
1
|
module IB
|
2
2
|
# define a custom ErrorClass which can be fired if a verification fails
|
3
|
-
class VerifyError < StandardError
|
4
|
-
end
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
future: { currency: 'USD', exchange: nil, expiry: nil, symbol: nil } ,
|
66
|
-
forex: { currency: 'USD', exchange: 'IDEALPRO', symbol: nil }
|
67
|
-
}
|
68
|
-
sec_type.present? ? v[sec_type] : { con_id: nil, exchange: 'SMART' } # enables to use only con_id for verifying
|
3
|
+
# class VerifyError < StandardError
|
4
|
+
# end
|
5
|
+
|
6
|
+
class Contract
|
7
|
+
|
8
|
+
# IB::Contract#Verify
|
9
|
+
|
10
|
+
# verifies the contract
|
11
|
+
#
|
12
|
+
# returns the number of contracts returned by the TWS.
|
13
|
+
#
|
14
|
+
#
|
15
|
+
# The method accepts a block. The queried contract-Object is accessible there.
|
16
|
+
# If multiple contracts are specified, the block is executed with each of these contracts.
|
17
|
+
#
|
18
|
+
#
|
19
|
+
# Verify returns an _Array_ of contracts. The operation leaves the contract untouched.
|
20
|
+
#
|
21
|
+
#
|
22
|
+
# Returns nil if the contract could not be verified.
|
23
|
+
#
|
24
|
+
# > s = Stock.new symbol: 'AA'
|
25
|
+
# => #<IB::Stock:0x0000000002626cc0
|
26
|
+
# @attributes={:symbol=>"AA", :con_id=>0, :right=>"", :include_expired=>false,
|
27
|
+
# :sec_type=>"STK", :currency=>"USD", :exchange=>"SMART"}
|
28
|
+
# > sp = s.verify.first.essential
|
29
|
+
# => #<IB::Stock:0x00000000025a3cf8
|
30
|
+
# @attributes={:symbol=>"AA", :con_id=>251962528, :exchange=>"SMART", :currency=>"USD",
|
31
|
+
# :strike=>0.0, :local_symbol=>"AA", :multiplier=>0, :primary_exchange=>"NYSE",
|
32
|
+
# :trading_class=>"AA", :sec_type=>"STK", :right=>"", :include_expired=>false}
|
33
|
+
#
|
34
|
+
# > s = Stock.new symbol: 'invalid'
|
35
|
+
# => @attributes={:symbol=>"invalid", :sec_type=>"STK", :currency=>"USD", :exchange=>"SMART"}
|
36
|
+
# > sp = s.verify
|
37
|
+
# => []
|
38
|
+
#
|
39
|
+
# Takes a Block to modify the queried contracts
|
40
|
+
#
|
41
|
+
# f = Future.new symbol: 'M2K'
|
42
|
+
# con_ids = f.verify{ |c| c.con_id }
|
43
|
+
# [412889018, 428519982, 446091466, 461318872, 477836981]
|
44
|
+
#
|
45
|
+
#
|
46
|
+
# Parameter: thread: (true/false)
|
47
|
+
#
|
48
|
+
# If multiple contracts are to be verified, they can be queried simultaneously.
|
49
|
+
# IB::Symbols::W500.map{|c| c.verify(thread: true){ |vc| do_something }}.join
|
50
|
+
|
51
|
+
def verify thread: nil, &b
|
52
|
+
return [self] if contract_detail.present? || sec_type == :bag
|
53
|
+
_verify update: false, thread: thread, &b # returns the allocated threads
|
54
|
+
end # def
|
55
|
+
|
56
|
+
# returns a hash
|
57
|
+
def nessesary_attributes
|
58
|
+
|
59
|
+
v= { stock: { currency: 'USD', exchange: 'SMART', symbol: nil},
|
60
|
+
option: { currency: 'USD', exchange: 'SMART', right: 'P', expiry: nil, strike: nil, symbol: nil},
|
61
|
+
future: { currency: 'USD', exchange: nil, expiry: nil, symbol: nil },
|
62
|
+
forex: { currency: 'USD', exchange: 'IDEALPRO', symbol: nil }
|
63
|
+
}
|
64
|
+
sec_type.present? ? v[sec_type] : { con_id: nil, exchange: 'SMART' } # enables to use only con_id for verifying
|
69
65
|
# if the contract allows SMART routing
|
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
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
def query_contract( invalid_record: true ) # :nodoc:
|
202
|
-
# don't raise a verify error at this time. Contract.new con_id= xxxx, currency = 'xyz' is also valid
|
203
|
-
## raise VerifyError, "Querying Contract faild: Invalid Security Type" unless SECURITY_TYPES.values.include? sec_type
|
204
|
-
|
205
|
-
## the yml contains symbol-entries
|
206
|
-
## these are converted to capitalized strings
|
207
|
-
items_as_string = ->(i){i.map{|x,y| x.to_s.capitalize}.join(', ')}
|
208
|
-
## here we read the corresponding attributes of the specified contract
|
209
|
-
item_values = ->(i){ i.map{|x,y| self.send(x).presence || y }}
|
210
|
-
## and finally we create a attribute-hash to instantiate a new Contract
|
211
|
-
## to_h is present only after ruby 2.1.0
|
212
|
-
item_attributehash = ->(i){ i.keys.zip(item_values[i]).to_h }
|
213
|
-
## now lets proceed, but only if no con_id is present
|
214
|
-
if con_id.blank? || con_id.zero?
|
215
|
-
# if item_values[nessesary_attributes].any?( &:nil? )
|
216
|
-
# raise VerifyError, "#{items_as_string[nessesary_attributes]} are needed to retrieve Contract,
|
217
|
-
# got: #{item_values[nessesary_attributes].join(',')}"
|
218
|
-
# end
|
219
|
-
# Contract.build item_attributehash[nessesary_items].merge(:sec_type=> sec_type) # return this
|
220
|
-
Contract.build self.attributes # return this
|
221
|
-
else # its always possible, to retrieve a Contract if con_id and exchange or are present
|
222
|
-
Contract.new con_id: con_id , :exchange => exchange.presence || item_attributehash[nessesary_attributes][:exchange].presence || 'SMART' # return this
|
223
|
-
end # if
|
224
|
-
end # def
|
225
|
-
end # class
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# depreciated: Do not use anymore
|
70
|
+
def verify!
|
71
|
+
c = 0
|
72
|
+
IB::Connection.logger.warn "Contract.verify! is depreciated. Use \"contract = contract.verify.first\" instead"
|
73
|
+
_verify( update: true){| response | c+=1 } # wait for the returned thread to finish
|
74
|
+
IB::Connection.logger.error { "Multible Contracts detected during verify!." } if c > 1
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Base method to verify a contract
|
81
|
+
#
|
82
|
+
# if :thread is given, the method subscribes to messages, fires the request and returns the thread, that
|
83
|
+
# receives the exit-condition-message
|
84
|
+
#
|
85
|
+
# otherwise the method waits until the response form tws is processed
|
86
|
+
#
|
87
|
+
#
|
88
|
+
# if :update is true, the attributes of the Contract itself are adapted
|
89
|
+
#
|
90
|
+
# otherwise the Contract is untouched
|
91
|
+
def _verify thread: nil , update:, &b # :nodoc:
|
92
|
+
ib = Connection.current
|
93
|
+
# we generate a Request-Message-ID on the fly
|
94
|
+
message_id = nil
|
95
|
+
# define local vars which are updated within the query-block
|
96
|
+
recieved_contracts = []
|
97
|
+
queue = Queue.new
|
98
|
+
a = nil
|
99
|
+
|
100
|
+
# a tws-request is suppressed for bags and if the contract_detail-record is present
|
101
|
+
tws_request_not_nessesary = bag? || contract_detail.is_a?( ContractDetail )
|
102
|
+
|
103
|
+
if tws_request_not_nessesary
|
104
|
+
yield self if block_given?
|
105
|
+
return self
|
106
|
+
else # subscribe to ib-messages and describe what to do
|
107
|
+
a = ib.subscribe(:Alert, :ContractData, :ContractDataEnd) do |msg|
|
108
|
+
case msg
|
109
|
+
when Messages::Incoming::Alert
|
110
|
+
if msg.code == 200 && msg.error_id == message_id
|
111
|
+
ib.logger.error { "Not a valid Contract :: #{self.to_human} " }
|
112
|
+
queue.close
|
113
|
+
end
|
114
|
+
when Messages::Incoming::ContractData
|
115
|
+
if msg.request_id.to_i == message_id
|
116
|
+
# if multiple contracts are present, all of them are assigned
|
117
|
+
# Only the last contract is saved in self; 'count' is incremented
|
118
|
+
## a specified block gets the contract_object on any unique ContractData-Event
|
119
|
+
recieved_contracts << if block_given?
|
120
|
+
yield msg.contract
|
121
|
+
else
|
122
|
+
msg.contract
|
123
|
+
end
|
124
|
+
if update
|
125
|
+
self.attributes = msg.contract.attributes
|
126
|
+
self.contract_detail = msg.contract_detail unless msg.contract_detail.nil?
|
127
|
+
end
|
128
|
+
end
|
129
|
+
when Messages::Incoming::ContractDataEnd
|
130
|
+
queue.push(1) if msg.request_id.to_i == message_id
|
131
|
+
|
132
|
+
end # case
|
133
|
+
end # subscribe
|
134
|
+
|
135
|
+
### send the request !
|
136
|
+
# contract_to_be_queried = con_id.present? ? self : query_contract
|
137
|
+
# if no con_id is present, the given attributes are checked by query_contract
|
138
|
+
# if contract_to_be_queried.present? # is nil if query_contract fails
|
139
|
+
message_id = ib.send_message :RequestContractData, :contract => query_contract
|
140
|
+
|
141
|
+
th = Thread.new do
|
142
|
+
queue.pop
|
143
|
+
# j=0; loop{ sleep(0.01); j+=1; break if queue.closed? || queue.pop || j> 100; }
|
144
|
+
# ib.logger.error{ "#{to_human} --> No ContractData recieved " } if j >= 100 && !queue.closed?
|
145
|
+
ib.unsubscribe a
|
146
|
+
end
|
147
|
+
if thread.nil?
|
148
|
+
th.join # wait for the thread to finish
|
149
|
+
recieved_contracts # return queue of contracts
|
150
|
+
else
|
151
|
+
th # return active thread
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Generates an IB::Contract with the required attributes to retrieve a unique contract from the TWS
|
157
|
+
#
|
158
|
+
# Background: If the tws is queried with a »complete« IB::Contract, it fails occasionally.
|
159
|
+
# So – even to update its contents, a defined subset of query-parameters has to be used.
|
160
|
+
#
|
161
|
+
# The required data-fields are stored in a yaml-file and fetched by #YmlFile.
|
162
|
+
#
|
163
|
+
# If `con_id` is present, only `con_id` and `exchange` are transmitted to the tws.
|
164
|
+
# Otherwise a IB::Stock, IB::Option, IB::Future or IB::Forex-Object with necessary attributes
|
165
|
+
# to query the tws is build (and returned)
|
166
|
+
#
|
167
|
+
# If Attributes are missing, an IB::VerifyError is fired,
|
168
|
+
# This can be trapped with
|
169
|
+
# rescue IB::VerifyError do ...
|
170
|
+
|
171
|
+
def query_contract( invalid_record: true ) # :nodoc:
|
172
|
+
# don't raise a verify error at this time. Contract.new con_id= xxxx, currency = 'xyz' is also valid
|
173
|
+
## raise VerifyError, "Querying Contract failed: Invalid Security Type" unless SECURITY_TYPES.values.include? sec_type
|
174
|
+
|
175
|
+
## the yml contains symbol-entries
|
176
|
+
## these are converted to capitalized strings
|
177
|
+
items_as_string = ->(i){i.map{|x,y| x.to_s.capitalize}.join(', ')}
|
178
|
+
## here we read the corresponding attributes of the specified contract
|
179
|
+
item_values = ->(i){ i.map{|x,y| self.send(x).presence || y }}
|
180
|
+
## and finally we create a attribute-hash to instantiate a new Contract
|
181
|
+
## to_h is present only after ruby 2.1.0
|
182
|
+
item_attributehash = ->(i){ i.keys.zip(item_values[i]).to_h }
|
183
|
+
## now lets proceed, but only if no con_id is present
|
184
|
+
if con_id.blank? || con_id.zero?
|
185
|
+
# if item_values[necessary_attributes].any?( &:nil? )
|
186
|
+
# raise VerifyError, "#{items_as_string[necessary_attributes]} are needed to retrieve Contract,
|
187
|
+
# got: #{item_values[necessary_attributes].join(',')}"
|
188
|
+
# end
|
189
|
+
# Contract.build item_attributehash[necessary_items].merge(:sec_type=> sec_type) # return this
|
190
|
+
Contract.build self.invariant_attributes # return this
|
191
|
+
else # its always possible, to retrieve a Contract if con_id and exchange or are present
|
192
|
+
Contract.new con_id: con_id , :exchange => exchange.presence || item_attributehash[nessesary_attributes][:exchange].presence || 'SMART' # return this
|
193
|
+
end # if
|
194
|
+
end # def
|
195
|
+
end
|
226
196
|
end #module
|