ib-ruby 0.5.19 → 0.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|