ib-ruby 0.5.21 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +8 -0
- data/README.md +46 -27
- data/TODO +13 -2
- data/VERSION +1 -1
- data/bin/generic_data.rb +26 -0
- data/bin/place_order +1 -1
- data/lib/ib-ruby/connection.rb +126 -65
- data/lib/ib-ruby/messages/incoming.rb +3 -3
- data/lib/ib-ruby/models/bar.rb +11 -11
- data/lib/ib-ruby/models/combo_leg.rb +23 -29
- data/lib/ib-ruby/models/contract/bag.rb +34 -2
- data/lib/ib-ruby/models/contract/option.rb +2 -2
- data/lib/ib-ruby/models/contract.rb +151 -197
- data/lib/ib-ruby/models/execution.rb +27 -45
- data/lib/ib-ruby/models/model.rb +10 -4
- data/lib/ib-ruby/models/model_properties.rb +63 -0
- data/lib/ib-ruby/models/order.rb +274 -320
- data/lib/ib-ruby/symbols/stocks.rb +11 -5
- data/spec/account_helper.rb +80 -0
- data/spec/ib-ruby/connection_spec.rb +195 -52
- data/spec/ib-ruby/messages/incoming_spec.rb +4 -4
- data/spec/ib-ruby/models/combo_leg_spec.rb +1 -0
- data/spec/ib-ruby/models/contract_spec.rb +1 -1
- data/spec/ib-ruby/models/execution_spec.rb +73 -0
- data/spec/integration/account_info_spec.rb +12 -59
- data/spec/integration/contract_info_spec.rb +23 -37
- data/spec/integration/depth_data_spec.rb +4 -4
- data/spec/integration/historic_data_spec.rb +15 -27
- data/spec/integration/market_data_spec.rb +74 -61
- data/spec/integration/option_data_spec.rb +5 -48
- data/spec/integration/orders/execution_spec.rb +26 -31
- data/spec/integration/orders/open_order +2 -0
- data/spec/integration/orders/placement_spec.rb +28 -28
- data/spec/integration/orders/valid_ids_spec.rb +11 -11
- data/spec/integration_helper.rb +46 -32
- data/spec/message_helper.rb +4 -38
- data/spec/spec_helper.rb +2 -3
- metadata +9 -2
@@ -13,11 +13,17 @@ module IB
|
|
13
13
|
:currency => "USD",
|
14
14
|
:sec_type => SECURITY_TYPES[:stock],
|
15
15
|
:description => "Wells Fargo"),
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
|
17
|
+
:aapl => Models::Contract.new(:symbol => "AAPL",
|
18
|
+
:currency => "USD",
|
19
|
+
:sec_type => SECURITY_TYPES[:stock],
|
20
|
+
:description => "Apple Inc."),
|
21
|
+
|
22
|
+
:wrong => Models::Contract.new(:symbol => "QEEUUE",
|
23
|
+
:exchange => "NYSE",
|
24
|
+
:currency => "USD",
|
25
|
+
:sec_type => SECURITY_TYPES[:stock],
|
26
|
+
:description => "Non-existent stock"),
|
21
27
|
}
|
22
28
|
end
|
23
29
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Make sure integration tests are only run against the pre-configured PAPER ACCOUNT
|
2
|
+
def verify_account
|
3
|
+
return OPTS[:account_verified] if OPTS[:account_verified]
|
4
|
+
|
5
|
+
puts
|
6
|
+
puts 'WARNING: MAKE SURE TO RUN INTEGRATION TESTS AGAINST IB PAPER ACCOUNT ONLY!'
|
7
|
+
puts 'WARNING: FINANCIAL LOSSES MAY RESULT IF YOU RUN TESTS WITH REAL IB ACCOUNT!'
|
8
|
+
puts 'WARNING: YOU HAVE BEEN WARNED!'
|
9
|
+
puts
|
10
|
+
puts 'Configure your connection to IB PAPER ACCOUNT in spec/spec_helper.rb'
|
11
|
+
puts
|
12
|
+
|
13
|
+
account = OPTS[:connection][:account] || OPTS[:connection][:account_name]
|
14
|
+
raise "Please configure IB PAPER ACCOUNT in spec/spec_helper.rb" unless account
|
15
|
+
|
16
|
+
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
|
17
|
+
@ib.send_message :RequestAccountData, :subscribe => true
|
18
|
+
|
19
|
+
@ib.wait_for 3, :AccountValue
|
20
|
+
raise "Unable to verify IB PAPER ACCOUNT" unless @ib.received?(:AccountValue)
|
21
|
+
|
22
|
+
received = @ib.received[:AccountValue].first.account_name
|
23
|
+
raise "Connected to wrong account #{received}, expected #{account}" if account != received
|
24
|
+
|
25
|
+
close_connection
|
26
|
+
OPTS[:account_verified] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
### Helpers for placing and verifying orders
|
30
|
+
|
31
|
+
shared_examples_for 'Valid account data request' do
|
32
|
+
|
33
|
+
context "received :AccountUpdateTime message" do
|
34
|
+
subject { @ib.received[:AccountUpdateTime].first }
|
35
|
+
|
36
|
+
it { should be_an IB::Messages::Incoming::AccountUpdateTime }
|
37
|
+
its(:data) { should be_a Hash }
|
38
|
+
its(:time_stamp) { should =~ /\d\d:\d\d/ }
|
39
|
+
its(:to_human) { should =~ /AccountUpdateTime/ }
|
40
|
+
end
|
41
|
+
|
42
|
+
context "received :AccountValue message" do
|
43
|
+
subject { @ib.received[:AccountValue].first }
|
44
|
+
|
45
|
+
#ps
|
46
|
+
it { should be_an IB::Messages::Incoming::AccountValue }
|
47
|
+
its(:data) { should be_a Hash }
|
48
|
+
its(:account_name) { should =~ /\w\d/ }
|
49
|
+
its(:key) { should be_a String }
|
50
|
+
its(:value) { should be_a String }
|
51
|
+
its(:currency) { should be_a String }
|
52
|
+
its(:to_human) { should =~ /AccountValue/ }
|
53
|
+
end
|
54
|
+
|
55
|
+
context "received :PortfolioValue message" do
|
56
|
+
subject { @ib.received[:PortfolioValue].first }
|
57
|
+
|
58
|
+
it { should be_an IB::Messages::Incoming::PortfolioValue }
|
59
|
+
its(:contract) { should be_a IB::Models::Contract }
|
60
|
+
its(:data) { should be_a Hash }
|
61
|
+
its(:position) { should be_a Integer }
|
62
|
+
its(:market_price) { should be_a Float }
|
63
|
+
its(:market_value) { should be_a Float }
|
64
|
+
its(:average_cost) { should be_a Float }
|
65
|
+
its(:unrealized_pnl) { should be_a Float }
|
66
|
+
its(:realized_pnl) { should be_a Float }
|
67
|
+
its(:account_name) { should =~ /\w\d/ }
|
68
|
+
its(:to_human) { should =~ /PortfolioValue/ }
|
69
|
+
end
|
70
|
+
|
71
|
+
context "received :AccountDownloadEnd message" do
|
72
|
+
subject { @ib.received[:AccountDownloadEnd].first }
|
73
|
+
|
74
|
+
it { should be_an IB::Messages::Incoming::AccountDownloadEnd }
|
75
|
+
its(:data) { should be_a Hash }
|
76
|
+
its(:account_name) { should =~ /\w\d/ }
|
77
|
+
its(:to_human) { should =~ /AccountDownloadEnd/ }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -1,94 +1,237 @@
|
|
1
1
|
require 'message_helper'
|
2
|
+
require 'account_helper'
|
3
|
+
|
4
|
+
shared_examples_for 'Connected Connection' do
|
5
|
+
|
6
|
+
subject { @ib }
|
7
|
+
|
8
|
+
it_behaves_like 'Connected Connection without receiver'
|
9
|
+
|
10
|
+
it 'keeps received messages in Hash by default' do
|
11
|
+
subject.received.should be_a Hash
|
12
|
+
subject.received[:NextValidId].should_not be_empty
|
13
|
+
subject.received[:NextValidId].should have_exactly(1).message
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
shared_examples_for 'Connected Connection without receiver' do
|
18
|
+
|
19
|
+
it { should_not be_nil }
|
20
|
+
it { should be_connected }
|
21
|
+
its(:server) { should be_a Hash }
|
22
|
+
its(:server) { should have_key :reader }
|
23
|
+
its(:subscribers) { should have_at_least(1).item } # :NextValidId and empty Hashes
|
24
|
+
its(:next_order_id) { should be_a Fixnum } # Not before :NextValidId arrives
|
25
|
+
end
|
26
|
+
|
27
|
+
# Need top level method to access instance var (@received) in nested context
|
28
|
+
def create_connection opts={}
|
29
|
+
# Start disconnected (we need to set up catch-all subscriber first)
|
30
|
+
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger).merge(opts)
|
31
|
+
|
32
|
+
# Hash of received messages, keyed by message type
|
33
|
+
@received = Hash.new { |hash, key| hash[key] = Array.new }
|
34
|
+
|
35
|
+
@subscriber = proc { |msg| @received[msg.message_type] << msg }
|
36
|
+
end
|
2
37
|
|
3
38
|
describe IB::Connection do
|
4
39
|
|
5
|
-
context '
|
6
|
-
# THIS depends on TWS|Gateway connectivity
|
40
|
+
context 'instantiated with default options', :connected => true do
|
7
41
|
before(:all) do
|
8
|
-
|
9
|
-
@ib.
|
42
|
+
create_connection
|
43
|
+
@ib.wait_for :NextValidId
|
10
44
|
end
|
11
45
|
|
12
46
|
after(:all) { close_connection }
|
13
47
|
|
14
|
-
|
15
|
-
subject { @ib }
|
16
|
-
|
17
|
-
it { should_not be_nil }
|
18
|
-
it { should be_connected }
|
19
|
-
its(:server) { should be_a Hash }
|
20
|
-
its(:server) { should have_key :reader }
|
21
|
-
its(:subscribers) { should have_at_least(1).item } # :NextValidID and empty Hashes
|
22
|
-
its(:next_order_id) { should be_a Fixnum } # Not before :NextValidID arrives
|
23
|
-
end
|
48
|
+
it_behaves_like 'Connected Connection'
|
24
49
|
|
25
50
|
describe '#send_message', 'sending messages' do
|
26
51
|
it 'allows 3 signatures representing IB::Messages::Outgoing' do
|
27
|
-
expect {
|
28
|
-
@ib.send_message :RequestOpenOrders, :subscribe => true
|
29
|
-
}.to_not raise_error
|
52
|
+
expect { @ib.send_message :RequestOpenOrders }.to_not raise_error
|
30
53
|
|
31
|
-
expect {
|
32
|
-
@ib.send_message IB::Messages::Outgoing::RequestOpenOrders, :subscribe => true
|
54
|
+
expect { @ib.send_message IB::Messages::Outgoing::RequestOpenOrders
|
33
55
|
}.to_not raise_error
|
34
56
|
|
35
|
-
expect {
|
36
|
-
@ib.send_message IB::Messages::Outgoing::RequestOpenOrders.new :subscribe => true
|
57
|
+
expect { @ib.send_message IB::Messages::Outgoing::RequestOpenOrders.new
|
37
58
|
}.to_not raise_error
|
38
59
|
end
|
39
60
|
|
40
61
|
it 'has legacy #dispatch alias' do
|
41
|
-
expect { @ib.dispatch :RequestOpenOrders
|
42
|
-
}.to_not raise_error
|
62
|
+
expect { @ib.dispatch :RequestOpenOrders }.to_not raise_error
|
43
63
|
end
|
44
64
|
end
|
45
65
|
|
46
66
|
context "subscriptions" do
|
67
|
+
before(:all) do
|
68
|
+
@id = {} # Moving id between contexts. Feels dirty.
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#subscribe' do
|
72
|
+
after(:all) { clean_connection }
|
73
|
+
|
74
|
+
it 'adds (multiple) subscribers, returning subscription id' do
|
75
|
+
@id[:first] = @ib.subscribe(IB::Messages::Incoming::OrderStatus) do |msg|
|
76
|
+
log msg.to_human
|
77
|
+
end
|
78
|
+
|
79
|
+
@id[:second] = @ib.subscribe /Value/, :OpenOrder, @subscriber
|
80
|
+
|
81
|
+
@id[:third] = @ib.subscribe /Account/, &@subscriber
|
82
|
+
|
83
|
+
[[@id[:first], IB::Messages::Incoming::OrderStatus],
|
84
|
+
[@id[:second], IB::Messages::Incoming::OpenOrder],
|
85
|
+
[@id[:second], IB::Messages::Incoming::PortfolioValue],
|
86
|
+
[@id[:second], IB::Messages::Incoming::AccountValue], # as /Value/
|
87
|
+
[@id[:third], IB::Messages::Incoming::AccountValue], # as /Account/
|
88
|
+
[@id[:third], IB::Messages::Incoming::AccountDownloadEnd],
|
89
|
+
[@id[:third], IB::Messages::Incoming::AccountUpdateTime],
|
90
|
+
].each do |(subscriber_id, message_class)|
|
91
|
+
@ib.subscribers.should have_key(message_class)
|
92
|
+
@ib.subscribers[message_class].should have_key(subscriber_id)
|
93
|
+
end
|
94
|
+
end
|
47
95
|
|
48
|
-
|
49
|
-
|
96
|
+
it 'returns Integer subscription id' do
|
97
|
+
@id[:first].should be_an Integer
|
98
|
+
@id[:second].should be_an Integer
|
99
|
+
@id[:third].should be_an Integer
|
50
100
|
end
|
51
101
|
|
52
|
-
|
102
|
+
context 'when subscribed' do
|
53
103
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
@ib.
|
60
|
-
|
104
|
+
before(:all) do
|
105
|
+
@ib.send_message :RequestAccountData
|
106
|
+
@ib.wait_for :AccountDownloadEnd
|
107
|
+
end
|
108
|
+
|
109
|
+
after(:all) { @ib.send_message :RequestAccountData, :subscribe => false }
|
110
|
+
|
111
|
+
it 'receives subscribed message types and processes them in subscriber callback' do
|
112
|
+
@received[:AccountValue].should_not be_empty
|
113
|
+
@received[:PortfolioValue].should_not be_empty
|
114
|
+
@received[:AccountDownloadEnd].should_not be_empty
|
115
|
+
@received[:AccountUpdateTime].should_not be_empty
|
116
|
+
end
|
117
|
+
|
118
|
+
it_behaves_like 'Valid account data request'
|
119
|
+
end
|
120
|
+
end # subscribe
|
121
|
+
|
122
|
+
describe '#unsubscribe' do
|
123
|
+
before(:all) { @result = @ib.unsubscribe @id[:first], @id[:second] }
|
124
|
+
|
125
|
+
it 'removes all subscribers at given id or ids' do
|
126
|
+
[IB::Messages::Incoming::OrderStatus,
|
127
|
+
IB::Messages::Incoming::OpenOrder,
|
128
|
+
IB::Messages::Incoming::PortfolioValue,
|
129
|
+
IB::Messages::Incoming::AccountValue,
|
130
|
+
].each do |message_class|
|
131
|
+
@ib.subscribers[message_class].should_not have_key(@id[:first])
|
132
|
+
@ib.subscribers[message_class].should_not have_key(@id[:second])
|
133
|
+
end
|
61
134
|
end
|
62
|
-
end
|
63
135
|
|
64
|
-
|
65
|
-
|
136
|
+
it 'does not remove subscribers at other ids' do
|
137
|
+
@ib.subscribers[IB::Messages::Incoming::AccountValue].should have_key(@id[:third])
|
138
|
+
@ib.subscribers[IB::Messages::Incoming::AccountDownloadEnd].should have_key(@id[:third])
|
139
|
+
@ib.subscribers[IB::Messages::Incoming::AccountUpdateTime].should have_key(@id[:third])
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'returns an Array of removed subscribers' do
|
143
|
+
@result.should be_an Array
|
144
|
+
@result.should have_exactly(4).deleted_subscribers
|
145
|
+
end
|
66
146
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
IB::Messages::Incoming::PortfolioValue
|
71
|
-
].each do |message_class|
|
72
|
-
@ib.subscribers[message_class].should_not have_key(@subscriber_id)
|
147
|
+
it 'raises on nosense id given' do
|
148
|
+
expect { @ib.unsubscribe 'nonsense' }.to raise_error /No subscribers with id/
|
149
|
+
expect { @ib.unsubscribe rand(9999999) }.to raise_error /No subscribers with id/
|
73
150
|
end
|
74
151
|
end
|
75
152
|
|
153
|
+
context 'when unsubscribed' do
|
154
|
+
|
155
|
+
before(:all) do
|
156
|
+
@ib.send_message :RequestAccountData
|
157
|
+
@ib.wait_for { !@received[:AccountDownloadEnd].empty? }
|
158
|
+
end
|
159
|
+
|
160
|
+
after(:all) { @ib.send_message :RequestAccountData, :subscribe => false }
|
161
|
+
|
162
|
+
it 'receives subscribed message types still subscribed' do
|
163
|
+
@received[:AccountValue].should_not be_empty
|
164
|
+
@received[:AccountUpdateTime].should_not be_empty
|
165
|
+
@received[:AccountDownloadEnd].should_not be_empty
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'does not receive unsubscribed message types' do
|
169
|
+
@received[:PortfolioValue].should be_empty
|
170
|
+
end
|
171
|
+
|
172
|
+
it { should_log /No subscribers for message .*:PortfolioValue/ }
|
173
|
+
it { should_not_log /No subscribers for message .*:AccountValue/ }
|
174
|
+
end # when subscribed
|
76
175
|
end # subscriptions
|
176
|
+
|
177
|
+
describe '#connect' do
|
178
|
+
it 'raises on another connection attempt' do
|
179
|
+
expect { @ib.connect }.to raise_error /Already connected/
|
180
|
+
end
|
181
|
+
end
|
77
182
|
end # connected
|
78
183
|
|
79
|
-
context '
|
80
|
-
before(:all) {
|
184
|
+
context 'instantiated passing :connect => false' do
|
185
|
+
before(:all) { create_connection :connect => false,
|
186
|
+
:reader => false }
|
187
|
+
subject { @ib }
|
188
|
+
|
189
|
+
it { should_not be_nil }
|
190
|
+
it { should_not be_connected }
|
191
|
+
its(:server) { should be_a Hash }
|
192
|
+
its(:server) { should_not have_key :reader }
|
193
|
+
its(:received) { should be_empty }
|
194
|
+
its(:subscribers) { should be_empty }
|
195
|
+
its(:next_order_id) { should be_nil }
|
196
|
+
|
197
|
+
describe 'connecting idle conection' do
|
198
|
+
before(:all) do
|
199
|
+
@ib.connect
|
200
|
+
@ib.start_reader
|
201
|
+
@ib.wait_for :NextValidId
|
202
|
+
end
|
203
|
+
after(:all) { close_connection }
|
204
|
+
|
205
|
+
it_behaves_like 'Connected Connection'
|
206
|
+
end
|
81
207
|
|
82
|
-
|
83
|
-
subject { @ib }
|
208
|
+
end # not connected
|
84
209
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
210
|
+
context 'instantiated passing :received => false' do
|
211
|
+
before(:all) { create_connection :connect => false,
|
212
|
+
:reader => false,
|
213
|
+
:received => false }
|
214
|
+
subject { @ib }
|
215
|
+
|
216
|
+
it { should_not be_nil }
|
217
|
+
it { should_not be_connected }
|
218
|
+
its(:server) { should be_a Hash }
|
219
|
+
its(:server) { should_not have_key :reader }
|
220
|
+
its(:received) { should be_empty }
|
221
|
+
its(:subscribers) { should be_empty }
|
222
|
+
its(:next_order_id) { should be_nil }
|
223
|
+
|
224
|
+
describe 'connecting idle conection' do
|
225
|
+
before(:all) do
|
226
|
+
@ib.connect
|
227
|
+
@ib.start_reader
|
228
|
+
@ib.wait_for 1 # ib.received not supposed to work!
|
229
|
+
end
|
230
|
+
after(:all) { close_connection }
|
231
|
+
|
232
|
+
it_behaves_like 'Connected Connection without receiver'
|
91
233
|
end
|
92
234
|
|
93
235
|
end # not connected
|
236
|
+
|
94
237
|
end # describe IB::Connection
|
@@ -7,7 +7,7 @@ shared_examples_for 'Alert message' do
|
|
7
7
|
its(:message_type) { should == :Alert }
|
8
8
|
its(:message_id) { should == 4 }
|
9
9
|
its(:version) { should == 2 }
|
10
|
-
its(:data) { should == {:version=>2, :error_id=>-1, :code=>2104, :message=>"Market data farm connection is OK:cashfarm"}}
|
10
|
+
its(:data) { should == {:version=>2, :error_id=>-1, :code=>2104, :message=>"Market data farm connection is OK:cashfarm"} }
|
11
11
|
its(:error_id) { should == -1 }
|
12
12
|
its(:code) { should == 2104 }
|
13
13
|
its(:message) { should =~ /Market data farm connection is OK/ }
|
@@ -37,13 +37,13 @@ describe IB::Messages::Incoming do
|
|
37
37
|
context 'Message received from IB', :connected => true do
|
38
38
|
|
39
39
|
before(:all) do
|
40
|
-
|
41
|
-
wait_for
|
40
|
+
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
|
41
|
+
@ib.wait_for :Alert
|
42
42
|
end
|
43
43
|
|
44
44
|
after(:all) { close_connection }
|
45
45
|
|
46
|
-
subject { @received[:Alert].first }
|
46
|
+
subject { @ib.received[:Alert].first }
|
47
47
|
|
48
48
|
it_behaves_like 'Alert message'
|
49
49
|
end #
|
@@ -21,7 +21,7 @@ describe IB::Models::Contract do
|
|
21
21
|
it { should_not be_nil }
|
22
22
|
its(:con_id) { should == 0 }
|
23
23
|
its(:strike) { should == 0 }
|
24
|
-
its(:sec_type) { should
|
24
|
+
its(:sec_type) { should be_nil }
|
25
25
|
its(:created_at) { should be_a Time }
|
26
26
|
its(:include_expired) { should == false }
|
27
27
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe IB::Models::Execution do
|
4
|
+
|
5
|
+
let(:properties) do
|
6
|
+
{:account_name => "DU111110",
|
7
|
+
:average_price => 1.31075,
|
8
|
+
:client_id => 1111,
|
9
|
+
:cumulative_quantity => 20000,
|
10
|
+
:exchange => "IDEALPRO",
|
11
|
+
:exec_id => "0001f4e8.4f5d48f1.01.01",
|
12
|
+
:liquidation => 0,
|
13
|
+
:order_id => 373,
|
14
|
+
:perm_id => 1695693613,
|
15
|
+
:price => 1.31075,
|
16
|
+
:shares => 20000,
|
17
|
+
:side => :buy,
|
18
|
+
:time => "20120312 15:41:09"
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
context "instantiation" do
|
23
|
+
context 'empty without properties' do
|
24
|
+
subject { IB::Models::Execution.new }
|
25
|
+
|
26
|
+
it { should_not be_nil }
|
27
|
+
its(:order_id) { should == 0 }
|
28
|
+
its(:client_id) { should == 0 }
|
29
|
+
its(:perm_id) { should == 0 }
|
30
|
+
its(:shares) { should == 0 }
|
31
|
+
its(:price) { should == 0 }
|
32
|
+
its(:liquidation) { should == 0 }
|
33
|
+
its(:created_at) { should be_a Time }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with properties' do
|
37
|
+
subject { IB::Models::Execution.new properties }
|
38
|
+
|
39
|
+
it 'sets properties right' do
|
40
|
+
properties.each do |name, value|
|
41
|
+
subject.send(name).should == value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end #instantiation
|
46
|
+
|
47
|
+
context "properties" do
|
48
|
+
|
49
|
+
it 'allows setting properties' do
|
50
|
+
expect {
|
51
|
+
x = IB::Models::Execution.new
|
52
|
+
properties.each do |name, value|
|
53
|
+
subject.send("#{name}=", value)
|
54
|
+
subject.send(name).should == value
|
55
|
+
end
|
56
|
+
}.to_not raise_error
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'sets side as directed by its setter' do
|
60
|
+
@x = IB::Models::Execution.new
|
61
|
+
['BOT', 'BUY', 'Buy', 'buy', :BUY, :BOT, :Buy, :buy, 'B', :b].each do |val|
|
62
|
+
expect { @x.side = val }.to_not raise_error
|
63
|
+
@x.side.should == :buy
|
64
|
+
end
|
65
|
+
|
66
|
+
['SELL', 'SLD', 'Sel', 'sell', :SELL, :SLD, :Sell, :sell, 'S', :S].each do |val|
|
67
|
+
expect { @x.side = val }.to_not raise_error
|
68
|
+
@x.side.should == :sell
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end # properties
|
72
|
+
|
73
|
+
end # describe IB::Models::Contract
|
@@ -1,66 +1,10 @@
|
|
1
1
|
require 'integration_helper'
|
2
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
3
|
describe "Request Account Data", :connected => true, :integration => true do
|
59
4
|
|
60
5
|
before(:all) do
|
61
6
|
verify_account
|
62
|
-
|
63
|
-
:PortfolioValue, :AccountUpdateTime)
|
7
|
+
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
|
64
8
|
end
|
65
9
|
|
66
10
|
after(:all) { close_connection }
|
@@ -68,7 +12,11 @@ describe "Request Account Data", :connected => true, :integration => true do
|
|
68
12
|
context "with subscribe option set" do
|
69
13
|
before(:all) do
|
70
14
|
@ib.send_message :RequestAccountData, :subscribe => true
|
71
|
-
wait_for
|
15
|
+
@ib.wait_for 5, :AccountDownloadEnd
|
16
|
+
end
|
17
|
+
after(:all) do
|
18
|
+
@ib.send_message :RequestAccountData, :subscribe => false
|
19
|
+
clean_connection
|
72
20
|
end
|
73
21
|
|
74
22
|
it_behaves_like 'Valid account data request'
|
@@ -77,7 +25,12 @@ describe "Request Account Data", :connected => true, :integration => true do
|
|
77
25
|
context "without subscribe option" do
|
78
26
|
before(:all) do
|
79
27
|
@ib.send_message :RequestAccountData
|
80
|
-
wait_for
|
28
|
+
@ib.wait_for 5, :AccountDownloadEnd
|
29
|
+
end
|
30
|
+
|
31
|
+
after(:all) do
|
32
|
+
@ib.send_message :RequestAccountData, :subscribe => false
|
33
|
+
clean_connection
|
81
34
|
end
|
82
35
|
|
83
36
|
it_behaves_like 'Valid account data request'
|