ib-ruby 0.5.19 → 0.5.21
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +4 -0
- data/TODO +0 -3
- data/VERSION +1 -1
- data/bin/contract_details +3 -3
- data/bin/depth_of_market +1 -1
- data/bin/historic_data +5 -8
- data/bin/market_data +2 -2
- data/bin/option_data +2 -2
- data/lib/ib-ruby/connection.rb +1 -9
- data/lib/ib-ruby/extensions.rb +8 -0
- data/lib/ib-ruby/messages/abstract_message.rb +89 -0
- data/lib/ib-ruby/messages/incoming.rb +415 -487
- data/lib/ib-ruby/messages/outgoing.rb +241 -305
- data/lib/ib-ruby/models/bar.rb +3 -3
- data/lib/ib-ruby/models/contract/bag.rb +1 -5
- data/lib/ib-ruby/models/contract.rb +50 -33
- data/lib/ib-ruby/models/execution.rb +6 -3
- data/lib/ib-ruby/models/order.rb +7 -5
- data/lib/ib-ruby/socket.rb +13 -0
- data/lib/ib-ruby/symbols/forex.rb +7 -14
- data/lib/ib-ruby/symbols/futures.rb +16 -20
- data/lib/ib-ruby/symbols/options.rb +6 -4
- data/lib/ib-ruby/symbols/stocks.rb +1 -1
- data/lib/ib-ruby.rb +1 -0
- data/spec/README.md +34 -0
- data/spec/ib-ruby/connection_spec.rb +4 -4
- data/spec/ib-ruby/messages/incoming_spec.rb +50 -0
- data/spec/ib-ruby/messages/outgoing_spec.rb +32 -0
- data/spec/ib-ruby/models/contract_spec.rb +27 -25
- data/spec/ib-ruby/models/order_spec.rb +56 -23
- data/spec/integration/account_info_spec.rb +85 -0
- data/spec/integration/contract_info_spec.rb +209 -0
- data/spec/integration/depth_data_spec.rb +46 -0
- data/spec/integration/historic_data_spec.rb +82 -0
- data/spec/integration/market_data_spec.rb +97 -0
- data/spec/integration/option_data_spec.rb +96 -0
- data/spec/integration/orders/execution_spec.rb +135 -0
- data/spec/{ib-ruby/messages → integration/orders}/open_order +9 -205
- data/spec/integration/orders/placement_spec.rb +150 -0
- data/spec/integration/orders/valid_ids_spec.rb +84 -0
- data/spec/integration_helper.rb +110 -0
- data/spec/message_helper.rb +13 -18
- data/spec/spec_helper.rb +35 -26
- metadata +33 -17
- data/spec/ib-ruby/messages/README.md +0 -16
- data/spec/ib-ruby/messages/account_info_spec.rb +0 -84
- data/spec/ib-ruby/messages/just_connect_spec.rb +0 -33
- data/spec/ib-ruby/messages/market_data_spec.rb +0 -92
- data/spec/ib-ruby/messages/orders_spec.rb +0 -219
- data/spec/ib-ruby_spec.rb +0 -0
@@ -47,40 +47,73 @@ describe IB::Models::Order do
|
|
47
47
|
|
48
48
|
it 'allows setting attributes' do
|
49
49
|
x = IB::Models::Order.new
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
x.transmit = false
|
55
|
-
x.designated_location = "WHATEVER"
|
56
|
-
x.exempt_code = 123
|
57
|
-
x.delta_neutral_order_type = "HACK"
|
58
|
-
x.what_if = true
|
59
|
-
x.not_held = true
|
60
|
-
}.to_not raise_error
|
61
|
-
|
62
|
-
x.outside_rth.should == true
|
63
|
-
x.open_close.should == 'C'
|
64
|
-
x.origin.should == IB::Models::Order::Origin_Firm
|
65
|
-
x.transmit.should == false
|
66
|
-
x.designated_location.should == "WHATEVER"
|
67
|
-
x.exempt_code.should == 123
|
68
|
-
x.delta_neutral_order_type.should == "HACK"
|
69
|
-
x.what_if.should == true
|
70
|
-
x.not_held.should == true
|
50
|
+
properties.each do |name, value|
|
51
|
+
subject.send("#{name}=", value)
|
52
|
+
subject.send(name).should == value
|
53
|
+
end
|
71
54
|
end
|
72
55
|
end #instantiation
|
73
56
|
|
74
57
|
context 'equality' do
|
75
58
|
subject { IB::Models::Order.new properties }
|
76
59
|
|
77
|
-
it '
|
60
|
+
it 'is self-equal ' do
|
78
61
|
should == subject
|
79
62
|
end
|
80
63
|
|
81
|
-
it '
|
64
|
+
it 'is equal to Order with the same properties' do
|
82
65
|
should == IB::Models::Order.new(properties)
|
83
66
|
end
|
67
|
+
|
68
|
+
it 'is not equal for Orders with different limit price' do
|
69
|
+
order1 = IB::Models::Order.new :total_quantity => 100,
|
70
|
+
:limit_price => 1,
|
71
|
+
:action => 'BUY'
|
72
|
+
|
73
|
+
order2 = IB::Models::Order.new :total_quantity => 100,
|
74
|
+
:limit_price => 2,
|
75
|
+
:action => 'BUY'
|
76
|
+
order1.should_not == order2
|
77
|
+
order2.should_not == order1
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'is not equal for Orders with different total_quantity' do
|
81
|
+
order1 = IB::Models::Order.new :total_quantity => 20000,
|
82
|
+
:limit_price => 1,
|
83
|
+
:action => 'BUY'
|
84
|
+
|
85
|
+
order2 = IB::Models::Order.new :total_quantity => 100,
|
86
|
+
:action => 'BUY',
|
87
|
+
:limit_price => 1
|
88
|
+
order1.should_not == order2
|
89
|
+
order2.should_not == order1
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'is not equal for Orders with different action/side' do
|
93
|
+
order1 = IB::Models::Order.new :total_quantity => 100,
|
94
|
+
:limit_price => 1,
|
95
|
+
:action => 'SELL'
|
96
|
+
|
97
|
+
order2 = IB::Models::Order.new :total_quantity => 100,
|
98
|
+
:action => 'BUY',
|
99
|
+
:limit_price => 1
|
100
|
+
order1.should_not == order2
|
101
|
+
order2.should_not == order1
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'is not equal for Orders with different order_type' do
|
105
|
+
order1 = IB::Models::Order.new :total_quantity => 100,
|
106
|
+
:limit_price => 1,
|
107
|
+
:action => 'BUY',
|
108
|
+
:order_type => 'LMT'
|
109
|
+
|
110
|
+
order2 = IB::Models::Order.new :total_quantity => 100,
|
111
|
+
:action => 'BUY',
|
112
|
+
:limit_price => 1,
|
113
|
+
:order_type => 'MKT'
|
114
|
+
order1.should_not == order2
|
115
|
+
order2.should_not == order1
|
116
|
+
end
|
84
117
|
end
|
85
118
|
|
86
119
|
end # describe IB::Models::Order
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
shared_examples_for 'Valid account data request' do
|
4
|
+
|
5
|
+
after(:all) do
|
6
|
+
@ib.send_message :RequestAccountData, :subscribe => false
|
7
|
+
clean_connection
|
8
|
+
end
|
9
|
+
|
10
|
+
context "received :AccountUpdateTime message" do
|
11
|
+
subject { @received[:AccountUpdateTime].first }
|
12
|
+
|
13
|
+
it { should be_an IB::Messages::Incoming::AccountUpdateTime }
|
14
|
+
its(:data) { should be_a Hash }
|
15
|
+
its(:time_stamp) { should =~ /\d\d:\d\d/ }
|
16
|
+
its(:to_human) { should =~ /AccountUpdateTime/ }
|
17
|
+
end
|
18
|
+
|
19
|
+
context "received :AccountValue message" do
|
20
|
+
subject { @received[:AccountValue].first }
|
21
|
+
|
22
|
+
#ps
|
23
|
+
it { should be_an IB::Messages::Incoming::AccountValue }
|
24
|
+
its(:data) { should be_a Hash }
|
25
|
+
its(:account_name) { should =~ /\w\d/ }
|
26
|
+
its(:key) { should be_a String }
|
27
|
+
its(:value) { should be_a String }
|
28
|
+
its(:currency) { should be_a String }
|
29
|
+
its(:to_human) { should =~ /AccountValue/ }
|
30
|
+
end
|
31
|
+
|
32
|
+
context "received :PortfolioValue message" do
|
33
|
+
subject { @received[:PortfolioValue].first }
|
34
|
+
|
35
|
+
it { should be_an IB::Messages::Incoming::PortfolioValue }
|
36
|
+
its(:contract) { should be_a IB::Models::Contract }
|
37
|
+
its(:data) { should be_a Hash }
|
38
|
+
its(:position) { should be_a Integer }
|
39
|
+
its(:market_price) { should be_a Float }
|
40
|
+
its(:market_value) { should be_a Float }
|
41
|
+
its(:average_cost) { should be_a Float }
|
42
|
+
its(:unrealized_pnl) { should be_a Float }
|
43
|
+
its(:realized_pnl) { should be_a Float }
|
44
|
+
its(:account_name) { should =~ /\w\d/ }
|
45
|
+
its(:to_human) { should =~ /PortfolioValue/ }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "received :AccountDownloadEnd message" do
|
49
|
+
subject { @received[:AccountDownloadEnd].first }
|
50
|
+
|
51
|
+
it { should be_an IB::Messages::Incoming::AccountDownloadEnd }
|
52
|
+
its(:data) { should be_a Hash }
|
53
|
+
its(:account_name) { should =~ /\w\d/ }
|
54
|
+
its(:to_human) { should =~ /AccountDownloadEnd/ }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "Request Account Data", :connected => true, :integration => true do
|
59
|
+
|
60
|
+
before(:all) do
|
61
|
+
verify_account
|
62
|
+
connect_and_receive(:Alert, :AccountValue, :AccountDownloadEnd,
|
63
|
+
:PortfolioValue, :AccountUpdateTime)
|
64
|
+
end
|
65
|
+
|
66
|
+
after(:all) { close_connection }
|
67
|
+
|
68
|
+
context "with subscribe option set" do
|
69
|
+
before(:all) do
|
70
|
+
@ib.send_message :RequestAccountData, :subscribe => true
|
71
|
+
wait_for(5) { received? :AccountDownloadEnd }
|
72
|
+
end
|
73
|
+
|
74
|
+
it_behaves_like 'Valid account data request'
|
75
|
+
end
|
76
|
+
|
77
|
+
context "without subscribe option" do
|
78
|
+
before(:all) do
|
79
|
+
@ib.send_message :RequestAccountData
|
80
|
+
wait_for(5) { received? :AccountDownloadEnd }
|
81
|
+
end
|
82
|
+
|
83
|
+
it_behaves_like 'Valid account data request'
|
84
|
+
end
|
85
|
+
end # Request Account Data
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
describe "Request Contract Info", :connected => true, :integration => true do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
verify_account
|
7
|
+
connect_and_receive :NextValidID, :Alert, :ContractData, :ContractDataEnd
|
8
|
+
wait_for { received? :NextValidID }
|
9
|
+
end
|
10
|
+
|
11
|
+
after(:all) { close_connection }
|
12
|
+
|
13
|
+
context "Request Stock data" do
|
14
|
+
|
15
|
+
before(:all) do
|
16
|
+
@contract = IB::Models::Contract.new :symbol => 'AAPL',
|
17
|
+
:sec_type => IB::SECURITY_TYPES[:stock]
|
18
|
+
@ib.send_message :RequestContractData,
|
19
|
+
:id => 111,
|
20
|
+
:contract => @contract
|
21
|
+
|
22
|
+
wait_for(3) { received? :ContractDataEnd }
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:all) { clean_connection } # Clear logs and message collector
|
26
|
+
|
27
|
+
it { @received[:ContractData].should have_exactly(2).contract_data }
|
28
|
+
it { @received[:ContractDataEnd].should have_exactly(1).contract_data_end }
|
29
|
+
|
30
|
+
it 'receives Contract Data for requested contract' do
|
31
|
+
msg = @received[:ContractData].first
|
32
|
+
msg.request_id.should == 111
|
33
|
+
msg.contract.should == @contract
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'receives Contract Data with extended fields' do
|
37
|
+
# Returns 2 contracts, one for NASDAQ and one for IBIS - internal crossing?
|
38
|
+
|
39
|
+
@received[:ContractData].each do |msg|
|
40
|
+
contract = msg.contract
|
41
|
+
contract.symbol.should == 'AAPL'
|
42
|
+
|
43
|
+
contract.local_symbol.should =~ /AAPL|APC/
|
44
|
+
contract.market_name.should =~ /NMS|USSTARS/
|
45
|
+
contract.trading_class.should =~ /NMS|USSTARS/
|
46
|
+
contract.long_name.should == 'APPLE INC'
|
47
|
+
contract.industry.should == 'Technology'
|
48
|
+
contract.category.should == 'Computers'
|
49
|
+
contract.subcategory.should == 'Computers'
|
50
|
+
contract.exchange.should == 'SMART'
|
51
|
+
contract.con_id.should be_an Integer
|
52
|
+
contract.trading_hours.should =~ /\d{8}:\d{4}-\d{4}/
|
53
|
+
contract.liquid_hours.should =~ /\d{8}:\d{4}-\d{4}/
|
54
|
+
contract.valid_exchanges.should =~ /ISLAND|IBIS/
|
55
|
+
contract.order_types.should be_a String
|
56
|
+
contract.price_magnifier.should == 1
|
57
|
+
contract.min_tick.should be <= 0.01
|
58
|
+
|
59
|
+
contract.expiry.should be_nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end # Stock
|
63
|
+
|
64
|
+
context "Request Option contract data" do
|
65
|
+
|
66
|
+
before(:all) do
|
67
|
+
@contract = IB::Models::Contract::Option.new :symbol => "AAPL",
|
68
|
+
:expiry => "201301",
|
69
|
+
:right => "CALL",
|
70
|
+
:strike => 500
|
71
|
+
@ib.send_message :RequestContractData,
|
72
|
+
:id => 123,
|
73
|
+
:contract => @contract
|
74
|
+
|
75
|
+
wait_for(3) { received? :ContractDataEnd }
|
76
|
+
end
|
77
|
+
|
78
|
+
after(:all) { clean_connection } # Clear logs and message collector
|
79
|
+
|
80
|
+
subject { @received[:ContractData].first }
|
81
|
+
|
82
|
+
it { @received[:ContractData].should have_exactly(1).contract_data }
|
83
|
+
it { @received[:ContractDataEnd].should have_exactly(1).contract_data_end }
|
84
|
+
|
85
|
+
it 'receives Contract Data for requested contract' do
|
86
|
+
subject.request_id.should == 123
|
87
|
+
subject.contract.should == @contract
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'receives Contract Data with extended fields' do
|
91
|
+
contract = subject.contract
|
92
|
+
contract.symbol.should == 'AAPL'
|
93
|
+
|
94
|
+
contract.local_symbol.should == 'AAPL 130119C00500000'
|
95
|
+
contract.market_name.should == 'AAPL'
|
96
|
+
contract.trading_class.should == 'AAPL'
|
97
|
+
contract.long_name.should == 'APPLE INC'
|
98
|
+
contract.industry.should == 'Technology'
|
99
|
+
contract.category.should == 'Computers'
|
100
|
+
contract.subcategory.should == 'Computers'
|
101
|
+
contract.expiry.should == '20130118'
|
102
|
+
contract.exchange.should == 'SMART'
|
103
|
+
contract.con_id.should be_an Integer
|
104
|
+
contract.trading_hours.should =~ /\d{8}:\d{4}-\d{4}/
|
105
|
+
contract.liquid_hours.should =~ /\d{8}:\d{4}-\d{4}/
|
106
|
+
contract.valid_exchanges.should =~ /CBOE/
|
107
|
+
contract.order_types.should be_a String
|
108
|
+
contract.price_magnifier.should == 1
|
109
|
+
contract.min_tick.should == 0.01
|
110
|
+
end
|
111
|
+
end # Request Option data
|
112
|
+
|
113
|
+
context "Request Forex contract data" do
|
114
|
+
|
115
|
+
before(:all) do
|
116
|
+
@contract = IB::Models::Contract.new :symbol => 'EUR', # EURUSD pair
|
117
|
+
:currency => "USD",
|
118
|
+
:exchange => "IDEALPRO",
|
119
|
+
:sec_type => IB::SECURITY_TYPES[:forex]
|
120
|
+
|
121
|
+
@ib.send_message :RequestContractData,
|
122
|
+
:id => 135,
|
123
|
+
:contract => @contract
|
124
|
+
|
125
|
+
wait_for(3) { received? :ContractDataEnd }
|
126
|
+
end
|
127
|
+
|
128
|
+
after(:all) { clean_connection } # Clear logs and message collector
|
129
|
+
|
130
|
+
subject { @received[:ContractData].first }
|
131
|
+
|
132
|
+
it { @received[:ContractData].should have_exactly(1).contract_data }
|
133
|
+
it { @received[:ContractDataEnd].should have_exactly(1).contract_data_end }
|
134
|
+
|
135
|
+
it 'receives Contract Data for requested contract' do
|
136
|
+
subject.request_id.should == 135
|
137
|
+
subject.contract.should == @contract
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'receives Contract Data with extended fields' do
|
141
|
+
contract = subject.contract
|
142
|
+
contract.symbol.should == 'EUR'
|
143
|
+
|
144
|
+
contract.local_symbol.should == 'EUR.USD'
|
145
|
+
contract.market_name.should == 'EUR.USD'
|
146
|
+
contract.trading_class.should == 'EUR.USD'
|
147
|
+
contract.long_name.should == 'European Monetary Union euro'
|
148
|
+
contract.industry.should == ''
|
149
|
+
contract.category.should == ''
|
150
|
+
contract.subcategory.should == ''
|
151
|
+
contract.expiry.should be_nil
|
152
|
+
contract.exchange.should == 'IDEALPRO'
|
153
|
+
contract.con_id.should be_an Integer
|
154
|
+
contract.trading_hours.should =~ /\d{8}:\d{4}-\d{4}/
|
155
|
+
contract.liquid_hours.should =~ /\d{8}:\d{4}-\d{4}/
|
156
|
+
contract.valid_exchanges.should =~ /IDEALPRO/
|
157
|
+
contract.order_types.should be_a String
|
158
|
+
contract.price_magnifier.should == 1
|
159
|
+
contract.min_tick.should be <= 0.0001
|
160
|
+
end
|
161
|
+
end # Request Forex data
|
162
|
+
|
163
|
+
context "Request Futures contract data" do
|
164
|
+
|
165
|
+
before(:all) do
|
166
|
+
@contract = IB::Symbols::Futures[:ym] # Mini Dow Jones Industrial
|
167
|
+
|
168
|
+
@ib.send_message :RequestContractData,
|
169
|
+
:id => 147,
|
170
|
+
:contract => @contract
|
171
|
+
|
172
|
+
wait_for(3) { received? :ContractDataEnd }
|
173
|
+
end
|
174
|
+
|
175
|
+
after(:all) { clean_connection } # Clear logs and message collector
|
176
|
+
|
177
|
+
subject { @received[:ContractData].first }
|
178
|
+
|
179
|
+
it { @received[:ContractData].should have_exactly(1).contract_data }
|
180
|
+
it { @received[:ContractDataEnd].should have_exactly(1).contract_data_end }
|
181
|
+
|
182
|
+
it 'receives Contract Data for requested contract' do
|
183
|
+
subject.request_id.should == 147
|
184
|
+
subject.contract.should == @contract
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'receives Contract Data with extended fields' do
|
188
|
+
contract = subject.contract
|
189
|
+
contract.symbol.should == 'YM'
|
190
|
+
|
191
|
+
contract.local_symbol.should =~ /YM/
|
192
|
+
contract.market_name.should == 'YM'
|
193
|
+
contract.trading_class.should == 'YM'
|
194
|
+
contract.long_name.should == 'Mini Sized Dow Jones Industrial Average $5'
|
195
|
+
contract.industry.should == ''
|
196
|
+
contract.category.should == ''
|
197
|
+
contract.subcategory.should == ''
|
198
|
+
contract.expiry.should =~ Regexp.new(IB::Symbols.next_expiry)
|
199
|
+
contract.exchange.should == 'ECBOT'
|
200
|
+
contract.con_id.should be_an Integer
|
201
|
+
contract.trading_hours.should =~ /\d{8}:\d{4}-\d{4}/
|
202
|
+
contract.liquid_hours.should =~ /\d{8}:\d{4}-\d{4}/
|
203
|
+
contract.valid_exchanges.should =~ /ECBOT/
|
204
|
+
contract.order_types.should be_a String
|
205
|
+
contract.price_magnifier.should == 1
|
206
|
+
contract.min_tick.should == 1
|
207
|
+
end
|
208
|
+
end # Request Forex data
|
209
|
+
end # Contract Data
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
describe 'Request Depth of Market Data', :connected => true,
|
4
|
+
:integration => true, :if => :forex_trading_hours do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
verify_account
|
8
|
+
connect_and_receive :Alert, :MarketDepth
|
9
|
+
|
10
|
+
@ib.send_message :RequestMarketDepth, :id => 456, :num_rows => 3,
|
11
|
+
:contract => IB::Symbols::Forex[:eurusd]
|
12
|
+
|
13
|
+
wait_for(3) { received? :MarketDepth, 8 }
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:all) do
|
17
|
+
@ib.send_message :CancelMarketDepth, :id => 456
|
18
|
+
close_connection
|
19
|
+
end
|
20
|
+
|
21
|
+
subject { @received[:MarketDepth].last }
|
22
|
+
|
23
|
+
it { @received[:MarketDepth].should have_at_least(8).depth_data }
|
24
|
+
|
25
|
+
it { should be_an IB::Messages::Incoming::MarketDepth }
|
26
|
+
its(:request_id) { should == 456 }
|
27
|
+
its(:price) { should be_a Float }
|
28
|
+
its(:size) { should be_an Integer }
|
29
|
+
its(:to_human) { should =~ /MarketDepth/ }
|
30
|
+
|
31
|
+
it 'has position field reflecting the row Id of this market depth entry' do
|
32
|
+
subject.position.should be_an Integer
|
33
|
+
subject.position.should be >= 1
|
34
|
+
subject.position.should be <= 3
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'has operation field reflecting how this entry is applied' do
|
38
|
+
subject.operation.should be_a Symbol
|
39
|
+
subject.operation.to_s.should =~ /insert|update|delete/
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'has side field reflecting side of the book: 0 = ask, 1 = bid' do
|
43
|
+
subject.side.should be_a Symbol
|
44
|
+
subject.side.to_s.should =~ /ask|bid/
|
45
|
+
end
|
46
|
+
end # Request Market Depth
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
describe 'Request Historic Data', :connected => true, :integration => true do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
verify_account
|
7
|
+
connect_and_receive :Alert, :HistoricalData
|
8
|
+
end
|
9
|
+
|
10
|
+
after(:all) do
|
11
|
+
@ib.send_message :CancelHistoricalData, :id => 456
|
12
|
+
close_connection
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'Wrong Requests' do
|
16
|
+
it 'raises if incorrect bar size' do
|
17
|
+
expect do
|
18
|
+
@ib.send_message :RequestHistoricalData, :id => 456,
|
19
|
+
:contract => IB::Symbols::Stocks[:wfc],
|
20
|
+
:end_date_time => Time.now.to_ib,
|
21
|
+
:duration => '1 D',
|
22
|
+
:bar_size => '11 min',
|
23
|
+
:what_to_show => :trades,
|
24
|
+
:format_date => 1
|
25
|
+
end.to raise_error /bar_size must be one of/
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'raises if incorrect what_to_show' do
|
29
|
+
expect do
|
30
|
+
@ib.send_message :RequestHistoricalData, :id => 456,
|
31
|
+
:contract => IB::Symbols::Stocks[:wfc],
|
32
|
+
:end_date_time => Time.now.to_ib,
|
33
|
+
:duration => '1 D',
|
34
|
+
:bar_size => '15 mins',
|
35
|
+
:what_to_show => :nonsense,
|
36
|
+
:format_date => 1
|
37
|
+
end.to raise_error /:what_to_show must be one of/
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'Correct Request' do
|
42
|
+
before(:all) do
|
43
|
+
# No historical data for GBP/CASH@IDEALPRO
|
44
|
+
@ib.send_message :RequestHistoricalData, :id => 456,
|
45
|
+
:contract => IB::Symbols::Stocks[:wfc],
|
46
|
+
:end_date_time => Time.now.to_ib,
|
47
|
+
:duration => '1 D',
|
48
|
+
:bar_size => '15 mins',
|
49
|
+
:what_to_show => :trades,
|
50
|
+
:format_date => 1
|
51
|
+
|
52
|
+
wait_for(3) { received? :HistoricalData }
|
53
|
+
end
|
54
|
+
|
55
|
+
subject { @received[:HistoricalData].last }
|
56
|
+
|
57
|
+
it { @received[:HistoricalData].should have_at_least(1).historic_data }
|
58
|
+
|
59
|
+
it { should be_an IB::Messages::Incoming::HistoricalData }
|
60
|
+
its(:request_id) { should == 456 }
|
61
|
+
its(:count) { should be_an Integer }
|
62
|
+
its(:start_date) { should =~ /\d{8} *\d\d:\d\d:\d\d/ } # "20120302 22:46:42"
|
63
|
+
its(:end_date) { should =~ /\d{8} *\d\d:\d\d:\d\d/ }
|
64
|
+
its(:to_human) { should =~ /HistoricalData/ }
|
65
|
+
|
66
|
+
it 'has results Array with returned historic data' do
|
67
|
+
subject.results.should be_an Array
|
68
|
+
subject.results.size.should == subject.count
|
69
|
+
subject.results.each do |bar|
|
70
|
+
bar.should be_an IB::Models::Bar
|
71
|
+
bar.time.should =~ /\d{8} *\d\d:\d\d:\d\d/
|
72
|
+
bar.open.should be_a Float
|
73
|
+
bar.high.should be_a Float
|
74
|
+
bar.low.should be_a Float
|
75
|
+
bar.close.should be_a Float
|
76
|
+
bar.wap.should be_a Float
|
77
|
+
bar.trades.should be_an Integer
|
78
|
+
bar.volume.should be_an Integer
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end # Request Historic Data
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
describe 'Request Market Data', :connected => true, :integration => true do
|
4
|
+
|
5
|
+
context 'when subscribed to :Tick... messages' do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
verify_account
|
9
|
+
connect_and_receive :Alert, :TickPrice, :TickSize
|
10
|
+
|
11
|
+
##TODO consider a follow the sun market lookup for windening the types tested
|
12
|
+
@ib.send_message :RequestMarketData, :id => 456,
|
13
|
+
:contract => IB::Symbols::Forex[:eurusd]
|
14
|
+
wait_for(3) { received? :TickPrice }
|
15
|
+
end
|
16
|
+
|
17
|
+
after(:all) do
|
18
|
+
@ib.send_message :CancelMarketData, :id => 456
|
19
|
+
close_connection
|
20
|
+
end
|
21
|
+
|
22
|
+
context "received :Alert message " do
|
23
|
+
subject { @received[:Alert].first }
|
24
|
+
|
25
|
+
it { should be_an IB::Messages::Incoming::Alert }
|
26
|
+
it { should be_warning }
|
27
|
+
it { should_not be_error }
|
28
|
+
its(:code) { should be_an Integer }
|
29
|
+
its(:message) { should =~ /Market data farm connection is OK/ }
|
30
|
+
its(:to_human) { should =~ /TWS Warning/ }
|
31
|
+
end
|
32
|
+
|
33
|
+
context "received :TickPrice message" do
|
34
|
+
subject { @received[:TickPrice].first }
|
35
|
+
|
36
|
+
it { should be_an IB::Messages::Incoming::TickPrice }
|
37
|
+
its(:tick_type) { should be_an Integer }
|
38
|
+
its(:type) { should be_a Symbol }
|
39
|
+
its(:price) { should be_a Float }
|
40
|
+
its(:size) { should be_an Integer }
|
41
|
+
its(:data) { should be_a Hash }
|
42
|
+
its(:ticker_id) { should == 456 } # ticker_id
|
43
|
+
its(:to_human) { should =~ /TickPrice/ }
|
44
|
+
end
|
45
|
+
|
46
|
+
context "received :TickSize message", :if => :forex_trading_hours do
|
47
|
+
before(:all) do
|
48
|
+
wait_for(3) { received? :TickSize }
|
49
|
+
end
|
50
|
+
|
51
|
+
subject { @received[:TickSize].first }
|
52
|
+
|
53
|
+
it { should be_an IB::Messages::Incoming::TickSize }
|
54
|
+
its(:type) { should_not be_nil }
|
55
|
+
its(:data) { should be_a Hash }
|
56
|
+
its(:tick_type) { should be_an Integer }
|
57
|
+
its(:type) { should be_a Symbol }
|
58
|
+
its(:size) { should be_an Integer }
|
59
|
+
its(:ticker_id) { should == 456 }
|
60
|
+
its(:to_human) { should =~ /TickSize/ }
|
61
|
+
end
|
62
|
+
end # when subscribed to :Tick... messages
|
63
|
+
|
64
|
+
context 'when NOT subscribed to :Tick... messages', :slow => true do
|
65
|
+
|
66
|
+
before(:all) do
|
67
|
+
connect_and_receive :NextValidID
|
68
|
+
|
69
|
+
@ib.send_message :RequestMarketData, :id => 456,
|
70
|
+
:contract => IB::Symbols::Forex[:eurusd]
|
71
|
+
wait_for(2)
|
72
|
+
end
|
73
|
+
|
74
|
+
after(:all) do
|
75
|
+
@ib.send_message :CancelMarketData, :id => 456
|
76
|
+
close_connection
|
77
|
+
end
|
78
|
+
|
79
|
+
it "logs warning about unhandled :OpenOrderEnd message" do
|
80
|
+
should_log /No subscribers for message .*:OpenOrderEnd/
|
81
|
+
end
|
82
|
+
|
83
|
+
it "logs warning about unhandled :Alert message" do
|
84
|
+
should_log /No subscribers for message .*:Alert/
|
85
|
+
end
|
86
|
+
|
87
|
+
it "logs warning about unhandled :Tick... messages" do
|
88
|
+
should_log /No subscribers for message .*:TickPrice/
|
89
|
+
end
|
90
|
+
|
91
|
+
it "logs warning about unhandled :Tick... messages", :if => :forex_trading_hours do
|
92
|
+
should_log /No subscribers for message .*:TickSize/
|
93
|
+
end
|
94
|
+
|
95
|
+
end # NOT subscribed to :Tick... messages
|
96
|
+
|
97
|
+
end # Request Market Data
|