ib-ruby 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +4 -0
- data/README.md +25 -17
- data/VERSION +1 -1
- data/bin/account_info +1 -1
- data/bin/cancel_orders +2 -1
- data/bin/contract_details +3 -2
- data/bin/depth_of_market +1 -1
- data/bin/historic_data +1 -1
- data/bin/historic_data_cli +57 -104
- data/bin/list_orders +4 -3
- data/bin/market_data +1 -1
- data/bin/option_data +1 -1
- data/bin/place_combo_order +63 -0
- data/bin/place_order +2 -4
- data/bin/template +1 -1
- data/bin/{generic_data.rb → tick_data} +3 -1
- data/bin/time_and_sales +1 -1
- data/lib/ib-ruby.rb +1 -0
- data/lib/ib-ruby/connection.rb +68 -68
- data/lib/ib-ruby/errors.rb +28 -0
- data/lib/ib-ruby/extensions.rb +7 -0
- data/lib/ib-ruby/messages.rb +1 -0
- data/lib/ib-ruby/messages/abstract_message.rb +16 -11
- data/lib/ib-ruby/messages/incoming.rb +125 -329
- data/lib/ib-ruby/messages/incoming/open_order.rb +193 -0
- data/lib/ib-ruby/messages/incoming/ticks.rb +131 -0
- data/lib/ib-ruby/messages/outgoing.rb +44 -45
- data/lib/ib-ruby/models/combo_leg.rb +16 -1
- data/lib/ib-ruby/models/contract.rb +18 -10
- data/lib/ib-ruby/models/contract/bag.rb +1 -7
- data/lib/ib-ruby/models/execution.rb +2 -1
- data/lib/ib-ruby/models/model.rb +1 -1
- data/lib/ib-ruby/models/order.rb +116 -56
- data/lib/ib-ruby/socket.rb +24 -3
- data/spec/account_helper.rb +2 -1
- data/spec/ib-ruby/messages/outgoing_spec.rb +1 -1
- data/spec/ib-ruby/models/combo_leg_spec.rb +0 -1
- data/spec/integration/account_info_spec.rb +2 -2
- data/spec/integration/contract_info_spec.rb +4 -4
- data/spec/integration/depth_data_spec.rb +3 -3
- data/spec/integration/historic_data_spec.rb +1 -1
- data/spec/integration/market_data_spec.rb +4 -4
- data/spec/integration/option_data_spec.rb +1 -1
- data/spec/integration/orders/combo_spec.rb +51 -0
- data/spec/integration/orders/execution_spec.rb +15 -8
- data/spec/integration/orders/placement_spec.rb +46 -72
- data/spec/integration/orders/valid_ids_spec.rb +6 -6
- data/spec/integration_helper.rb +0 -79
- data/spec/order_helper.rb +153 -0
- metadata +13 -4
data/lib/ib-ruby/socket.rb
CHANGED
@@ -4,7 +4,12 @@ module IB
|
|
4
4
|
class IBSocket < TCPSocket
|
5
5
|
|
6
6
|
# send nice null terminated binary data into socket
|
7
|
-
def
|
7
|
+
def write_data data
|
8
|
+
# TWS wants to receive booleans as 1 or 0
|
9
|
+
data = "1" if data == true
|
10
|
+
data = "0" if data == false
|
11
|
+
|
12
|
+
#p data.to_s + EOL
|
8
13
|
self.syswrite(data.to_s + EOL)
|
9
14
|
end
|
10
15
|
|
@@ -26,11 +31,12 @@ module IB
|
|
26
31
|
|
27
32
|
def read_int_max
|
28
33
|
str = self.read_string
|
29
|
-
str.nil? || str.empty?
|
34
|
+
str.to_i unless str.nil? || str.empty?
|
30
35
|
end
|
31
36
|
|
32
37
|
def read_boolean
|
33
|
-
self.read_string
|
38
|
+
str = self.read_string
|
39
|
+
str.nil? ? false : str.to_i != 0
|
34
40
|
end
|
35
41
|
|
36
42
|
def read_decimal
|
@@ -60,6 +66,21 @@ module IB
|
|
60
66
|
def read_decimal_limit_2
|
61
67
|
read_decimal_limit -2
|
62
68
|
end
|
69
|
+
|
70
|
+
### Complex operations
|
71
|
+
|
72
|
+
# Returns loaded Array or [] if count was 0
|
73
|
+
def read_array &block
|
74
|
+
count = read_int
|
75
|
+
count > 0 ? Array.new(count, &block) : []
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns loaded Hash
|
79
|
+
def read_hash
|
80
|
+
tags = read_array { |_| [read_string, read_string] }
|
81
|
+
tags.empty? ? Hash.new : Hash[*tags.flatten]
|
82
|
+
end
|
83
|
+
|
63
84
|
end # class IBSocket
|
64
85
|
|
65
86
|
end # module IB
|
data/spec/account_helper.rb
CHANGED
@@ -16,7 +16,8 @@ def verify_account
|
|
16
16
|
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
|
17
17
|
@ib.send_message :RequestAccountData, :subscribe => true
|
18
18
|
|
19
|
-
@ib.wait_for
|
19
|
+
@ib.wait_for :AccountDownloadEnd
|
20
|
+
@ib.send_message :RequestAccountData, :subscribe => false
|
20
21
|
raise "Unable to verify IB PAPER ACCOUNT" unless @ib.received?(:AccountValue)
|
21
22
|
|
22
23
|
received = @ib.received[:AccountValue].first.account_name
|
@@ -12,7 +12,7 @@ describe "Request Account Data", :connected => true, :integration => true do
|
|
12
12
|
context "with subscribe option set" do
|
13
13
|
before(:all) do
|
14
14
|
@ib.send_message :RequestAccountData, :subscribe => true
|
15
|
-
@ib.wait_for
|
15
|
+
@ib.wait_for :AccountDownloadEnd, 5 # sec
|
16
16
|
end
|
17
17
|
after(:all) do
|
18
18
|
@ib.send_message :RequestAccountData, :subscribe => false
|
@@ -25,7 +25,7 @@ describe "Request Account Data", :connected => true, :integration => true do
|
|
25
25
|
context "without subscribe option" do
|
26
26
|
before(:all) do
|
27
27
|
@ib.send_message :RequestAccountData
|
28
|
-
@ib.wait_for
|
28
|
+
@ib.wait_for :AccountDownloadEnd, 5 # sec
|
29
29
|
end
|
30
30
|
|
31
31
|
after(:all) do
|
@@ -16,7 +16,7 @@ describe "Request Contract Info", :connected => true, :integration => true do
|
|
16
16
|
@contract = IB::Models::Contract.new :symbol => 'AAPL',
|
17
17
|
:sec_type => IB::SECURITY_TYPES[:stock]
|
18
18
|
@ib.send_message :RequestContractData, :id => 111, :contract => @contract
|
19
|
-
@ib.wait_for
|
19
|
+
@ib.wait_for :ContractDataEnd, 3 # sec
|
20
20
|
end
|
21
21
|
|
22
22
|
after(:all) { clean_connection } # Clear logs and message collector
|
@@ -66,7 +66,7 @@ describe "Request Contract Info", :connected => true, :integration => true do
|
|
66
66
|
:right => "CALL",
|
67
67
|
:strike => 500
|
68
68
|
@ib.send_message :RequestContractData, :id => 123, :contract => @contract
|
69
|
-
@ib.wait_for
|
69
|
+
@ib.wait_for :ContractDataEnd, 3 # sec
|
70
70
|
end
|
71
71
|
|
72
72
|
after(:all) { clean_connection } # Clear logs and message collector
|
@@ -112,7 +112,7 @@ describe "Request Contract Info", :connected => true, :integration => true do
|
|
112
112
|
:exchange => "IDEALPRO",
|
113
113
|
:sec_type => IB::SECURITY_TYPES[:forex]
|
114
114
|
@ib.send_message :RequestContractData, :id => 135, :contract => @contract
|
115
|
-
@ib.wait_for
|
115
|
+
@ib.wait_for :ContractDataEnd, 3 # sec
|
116
116
|
end
|
117
117
|
|
118
118
|
after(:all) { clean_connection } # Clear logs and message collector
|
@@ -155,7 +155,7 @@ describe "Request Contract Info", :connected => true, :integration => true do
|
|
155
155
|
before(:all) do
|
156
156
|
@contract = IB::Symbols::Futures[:ym] # Mini Dow Jones Industrial
|
157
157
|
@ib.send_message :RequestContractData, :id => 147, :contract => @contract
|
158
|
-
@ib.wait_for
|
158
|
+
@ib.wait_for :ContractDataEnd, 3 # sec
|
159
159
|
end
|
160
160
|
|
161
161
|
after(:all) { clean_connection } # Clear logs and message collector
|
@@ -10,7 +10,7 @@ describe 'Request Depth of Market Data', :connected => true,
|
|
10
10
|
@ib.send_message :RequestMarketDepth, :id => 456, :num_rows => 3,
|
11
11
|
:contract => IB::Symbols::Forex[:eurusd]
|
12
12
|
|
13
|
-
@ib.wait_for
|
13
|
+
@ib.wait_for [:MarketDepth, 4], 6 # sec
|
14
14
|
end
|
15
15
|
|
16
16
|
after(:all) do
|
@@ -20,7 +20,7 @@ describe 'Request Depth of Market Data', :connected => true,
|
|
20
20
|
|
21
21
|
subject { @ib.received[:MarketDepth].last }
|
22
22
|
|
23
|
-
it { @ib.received[:MarketDepth].should have_at_least(
|
23
|
+
it { @ib.received[:MarketDepth].should have_at_least(4).depth_data }
|
24
24
|
|
25
25
|
it { should be_an IB::Messages::Incoming::MarketDepth }
|
26
26
|
its(:request_id) { should == 456 }
|
@@ -30,7 +30,7 @@ describe 'Request Depth of Market Data', :connected => true,
|
|
30
30
|
|
31
31
|
it 'has position field reflecting the row Id of this market depth entry' do
|
32
32
|
subject.position.should be_an Integer
|
33
|
-
subject.position.should be >=
|
33
|
+
subject.position.should be >= 0
|
34
34
|
subject.position.should be <= 3
|
35
35
|
end
|
36
36
|
|
@@ -37,7 +37,7 @@ describe 'Request Historic Data', :connected => true, :integration => true do
|
|
37
37
|
before(:all) do
|
38
38
|
# No historical data for GBP/CASH@IDEALPRO
|
39
39
|
@ib.send_message :RequestHistoricalData, CORRECT_OPTS
|
40
|
-
@ib.wait_for
|
40
|
+
@ib.wait_for :HistoricalData, 5 # sec
|
41
41
|
end
|
42
42
|
|
43
43
|
subject { @ib.received[:HistoricalData].last }
|
@@ -19,7 +19,7 @@ describe 'Request Market Data', :connected => true, :integration => true do
|
|
19
19
|
:description => "Apple"
|
20
20
|
)
|
21
21
|
@ib.send_message :RequestMarketData, :id => 456, :contract => @contract
|
22
|
-
@ib.wait_for
|
22
|
+
@ib.wait_for :TickSize, :TickString, 3 # sec
|
23
23
|
end
|
24
24
|
|
25
25
|
after(:all) do
|
@@ -48,12 +48,12 @@ describe 'Request Market Data', :connected => true, :integration => true do
|
|
48
48
|
before(:all) do
|
49
49
|
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
|
50
50
|
|
51
|
-
##TODO consider a follow the sun market lookup for
|
51
|
+
##TODO consider a follow the sun market lookup for widening the types tested
|
52
52
|
@ib.subscribe(:Alert, :TickPrice, :TickSize) {}
|
53
53
|
@ib.send_message :RequestMarketData, :id => 456,
|
54
54
|
:contract => IB::Symbols::Forex[:eurusd]
|
55
55
|
|
56
|
-
@ib.wait_for
|
56
|
+
@ib.wait_for :TickPrice, :TickSize, 3 # sec
|
57
57
|
end
|
58
58
|
|
59
59
|
after(:all) do
|
@@ -84,7 +84,7 @@ describe 'Request Market Data', :connected => true, :integration => true do
|
|
84
84
|
|
85
85
|
@ib.send_message :RequestMarketData, :id => 456,
|
86
86
|
:contract => IB::Symbols::Forex[:eurusd]
|
87
|
-
@ib.wait_for
|
87
|
+
@ib.wait_for :TickPrice, :TickSize, 3 # sec
|
88
88
|
end
|
89
89
|
|
90
90
|
after(:all) do
|
@@ -9,7 +9,7 @@ describe 'Request Market Data for Options', :if => :us_trading_hours,
|
|
9
9
|
|
10
10
|
@ib.send_message :RequestMarketData, :id => 456,
|
11
11
|
:contract => IB::Symbols::Options[:aapl500]
|
12
|
-
@ib.wait_for
|
12
|
+
@ib.wait_for :TickPrice, :TickSize, :TickString, :TickOption, 5 # sec
|
13
13
|
end
|
14
14
|
|
15
15
|
after(:all) do
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'order_helper'
|
2
|
+
|
3
|
+
#OPTS[:silent] = false
|
4
|
+
|
5
|
+
def butterfly symbol, expiry, right, *strikes
|
6
|
+
raise 'No Connection!' unless @ib && @ib.connected?
|
7
|
+
|
8
|
+
legs = strikes.zip([1, -2, 1]).map do |strike, weight|
|
9
|
+
# Create contract
|
10
|
+
contract = IB::Models::Contract::Option.new :symbol => symbol,
|
11
|
+
:expiry => expiry,
|
12
|
+
:right => right,
|
13
|
+
:strike => strike
|
14
|
+
# Find out contract's con_id
|
15
|
+
@ib.clear_received :ContractData, :ContractDataEnd
|
16
|
+
@ib.send_message :RequestContractData, :id => strike, :contract => contract
|
17
|
+
@ib.wait_for :ContractDataEnd, 3
|
18
|
+
con_id = @ib.received[:ContractData].last.contract.con_id
|
19
|
+
|
20
|
+
# Create Comboleg from con_id and weight
|
21
|
+
IB::Models::ComboLeg.new :con_id => con_id, :weight => weight
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create new Combo contract
|
25
|
+
IB::Models::Contract::Bag.new :symbol => symbol,
|
26
|
+
:currency => "USD", # Only US options in combo Contracts
|
27
|
+
:exchange => "SMART",
|
28
|
+
:legs => legs
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "Combo Order", :connected => true, :integration => true do
|
32
|
+
|
33
|
+
before(:all) { verify_account }
|
34
|
+
|
35
|
+
context "Limit" do # , :if => :us_trading_hours
|
36
|
+
before(:all) do
|
37
|
+
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
|
38
|
+
@ib.wait_for :NextValidId
|
39
|
+
@ib.clear_received # to avoid conflict with pre-existing Orders
|
40
|
+
|
41
|
+
@combo = butterfly 'GOOG', '201301', 'CALL', 500, 510, 520
|
42
|
+
|
43
|
+
place_order @combo, :limit_price => 0.01 #, :what_if => true
|
44
|
+
@ib.wait_for [:OpenOrder, 3], [:OrderStatus, 2], 5
|
45
|
+
end
|
46
|
+
|
47
|
+
after(:all) { close_connection }
|
48
|
+
|
49
|
+
it_behaves_like 'Placed Order'
|
50
|
+
end # Limit
|
51
|
+
end # Combo Orders
|
@@ -1,5 +1,6 @@
|
|
1
|
-
require '
|
1
|
+
require 'order_helper'
|
2
2
|
|
3
|
+
#OPTS[:silent] = false
|
3
4
|
describe "Trades", :connected => true, :integration => true, :slow => true do
|
4
5
|
|
5
6
|
before(:all) { verify_account }
|
@@ -21,9 +22,15 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
21
22
|
:total_quantity => 20000,
|
22
23
|
:limit_price => 2,
|
23
24
|
:action => 'BUY'
|
25
|
+
#:all_or_none => 1,
|
26
|
+
#:fa_profile => 2,
|
27
|
+
#:percent_offset => 3,
|
28
|
+
#:clearing_account => 'z',
|
29
|
+
#:what_if => true
|
24
30
|
|
25
31
|
@ib.wait_for(5, :ExecutionData, :OpenOrder) do
|
26
|
-
@ib.received[:OpenOrder].last
|
32
|
+
@ib.received[:OpenOrder].last &&
|
33
|
+
@ib.received[:OpenOrder].last.order.commission
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
@@ -43,7 +50,7 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
43
50
|
it { @ib.received[:ExecutionDataEnd].should be_empty }
|
44
51
|
|
45
52
|
it 'receives filled OpenOrder' do
|
46
|
-
|
53
|
+
order_should_be 'Filled', -1
|
47
54
|
msg = @ib.received[:OpenOrder].last
|
48
55
|
msg.order.commission.should == 2.5
|
49
56
|
end
|
@@ -53,7 +60,7 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
53
60
|
end
|
54
61
|
|
55
62
|
it 'receives OrderStatus with fill details' do
|
56
|
-
|
63
|
+
status_should_be 'Filled', -1
|
57
64
|
end
|
58
65
|
end # Placing BUY
|
59
66
|
|
@@ -65,7 +72,7 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
65
72
|
:limit_price => 1,
|
66
73
|
:action => 'SELL'
|
67
74
|
|
68
|
-
@ib.wait_for(
|
75
|
+
@ib.wait_for(:ExecutionData, :OpenOrder, 5) do
|
69
76
|
@ib.received[:OpenOrder].last.order.commission
|
70
77
|
end
|
71
78
|
end
|
@@ -85,7 +92,7 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
85
92
|
it { @ib.received[:ExecutionData].should have_exactly(1).execution_data }
|
86
93
|
|
87
94
|
it 'receives filled OpenOrder' do
|
88
|
-
|
95
|
+
order_should_be 'Filled', -1
|
89
96
|
msg = @ib.received[:OpenOrder].last
|
90
97
|
msg.order.commission.should == 2.5
|
91
98
|
end
|
@@ -95,7 +102,7 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
95
102
|
end
|
96
103
|
|
97
104
|
it 'receives OrderStatus with fill details' do
|
98
|
-
|
105
|
+
status_should_be 'Filled', -1
|
99
106
|
end
|
100
107
|
end # Placing SELL
|
101
108
|
|
@@ -107,7 +114,7 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
107
114
|
:request_id => 456,
|
108
115
|
:client_id => OPTS[:connection][:client_id],
|
109
116
|
:time => (Time.now-10).to_ib # Time zone problems possible
|
110
|
-
@ib.wait_for
|
117
|
+
@ib.wait_for :ExecutionData, 3 # sec
|
111
118
|
end
|
112
119
|
|
113
120
|
#after(:all) { clean_connection }
|
@@ -1,19 +1,19 @@
|
|
1
|
-
require '
|
1
|
+
require 'order_helper'
|
2
2
|
|
3
|
-
|
3
|
+
#OPTS[:silent] = false
|
4
|
+
describe 'Orders', :connected => true, :integration => true do
|
4
5
|
|
5
6
|
before(:all) { verify_account }
|
6
7
|
|
7
|
-
context
|
8
|
+
context 'Placing wrong order', :slow => true do
|
8
9
|
|
9
10
|
before(:all) do
|
10
11
|
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
|
11
|
-
|
12
12
|
@ib.wait_for :NextValidId
|
13
13
|
|
14
14
|
place_order IB::Symbols::Stocks[:wfc],
|
15
15
|
:limit_price => 9.131313 # Weird non-acceptable price
|
16
|
-
@ib.wait_for 1
|
16
|
+
@ib.wait_for 1 # sec
|
17
17
|
end
|
18
18
|
|
19
19
|
after(:all) { close_connection }
|
@@ -39,88 +39,62 @@ describe "Orders", :connected => true, :integration => true do
|
|
39
39
|
|
40
40
|
end # Placing wrong order
|
41
41
|
|
42
|
-
context
|
42
|
+
context 'What-if order' do
|
43
43
|
before(:all) do
|
44
44
|
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
|
45
45
|
@ib.wait_for :NextValidId
|
46
46
|
|
47
47
|
place_order IB::Symbols::Stocks[:wfc],
|
48
|
-
:limit_price => 9.13 # Set acceptable price
|
49
|
-
|
48
|
+
:limit_price => 9.13, # Set acceptable price
|
49
|
+
:what_if => true # Hypothetical
|
50
|
+
@ib.wait_for 1
|
50
51
|
end
|
51
52
|
|
52
53
|
after(:all) { close_connection }
|
53
54
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@order_id_placed.should == @order_id_before
|
59
|
-
@ib.next_order_id.should == @order_id_before + 1
|
60
|
-
end
|
61
|
-
|
62
|
-
it { @ib.received[:OpenOrder].should have_at_least(1).open_order_message }
|
63
|
-
it { @ib.received[:OrderStatus].should have_at_least(1).status_message }
|
64
|
-
|
65
|
-
it 'receives confirmation of Order submission' do
|
66
|
-
open_order_should_be /Submitted/ # ()Pre)Submitted
|
67
|
-
order_status_should_be /Submitted/
|
68
|
-
end
|
69
|
-
end # Placing
|
70
|
-
|
71
|
-
context "Retrieving placed orders" do
|
72
|
-
before(:all) do
|
73
|
-
@ib.send_message :RequestOpenOrders
|
74
|
-
@ib.wait_for :OpenOrderEnd
|
75
|
-
end
|
76
|
-
|
77
|
-
after(:all) { clean_connection } # Clear logs and message collector
|
78
|
-
|
79
|
-
it 'does not increase client`s next_order_id further' do
|
80
|
-
@ib.next_order_id.should == @order_id_after
|
81
|
-
end
|
82
|
-
|
83
|
-
it { @ib.received[:OpenOrder].should have_exactly(1).open_order_message }
|
84
|
-
it { @ib.received[:OrderStatus].should have_exactly(1).status_message }
|
85
|
-
it { @ib.received[:OpenOrderEnd].should have_exactly(1).order_end_message }
|
86
|
-
it { @ib.received[:Alert].should have_exactly(0).alert_messages }
|
87
|
-
|
88
|
-
it 'receives OpenOrder and OrderStatus for placed order' do
|
89
|
-
open_order_should_be /Submitted/
|
90
|
-
order_status_should_be /Submitted/
|
91
|
-
end
|
92
|
-
end # Retrieving
|
93
|
-
|
94
|
-
context "Cancelling placed order" do
|
95
|
-
before(:all) do
|
96
|
-
@ib.cancel_order @order_id_placed
|
97
|
-
|
98
|
-
@ib.wait_for :OrderStatus, :Alert
|
99
|
-
end
|
55
|
+
it 'changes client`s next_order_id' do
|
56
|
+
@order_id_placed.should == @order_id_before
|
57
|
+
@ib.next_order_id.should == @order_id_before + 1
|
58
|
+
end
|
100
59
|
|
101
|
-
|
60
|
+
it { @ib.received[:OpenOrder].should have_at_least(1).open_order_message }
|
61
|
+
it { @ib.received[:OrderStatus].should have_exactly(0).status_messages }
|
62
|
+
|
63
|
+
it 'returns as what-if Order with margin and commission info' do
|
64
|
+
order_should_be /PreSubmitted/
|
65
|
+
order = @ib.received[:OpenOrder].first.order
|
66
|
+
order.what_if.should == true
|
67
|
+
order.equity_with_loan.should be_a Float
|
68
|
+
order.init_margin.should be_a Float
|
69
|
+
order.maint_margin.should be_a Float
|
70
|
+
order.commission.should be_a Float
|
71
|
+
order.equity_with_loan.should be > 0
|
72
|
+
order.init_margin.should be > 0
|
73
|
+
order.maint_margin.should be > 0
|
74
|
+
order.commission.should be > 1
|
75
|
+
end
|
102
76
|
|
103
|
-
|
104
|
-
|
105
|
-
|
77
|
+
it 'is not actually opened though' do
|
78
|
+
@ib.clear_received
|
79
|
+
@ib.send_message :RequestOpenOrders
|
80
|
+
@ib.wait_for :OpenOrderEnd
|
81
|
+
@ib.received[:OpenOrder].should have_exactly(0).order_message
|
82
|
+
end
|
83
|
+
end
|
106
84
|
|
107
|
-
|
108
|
-
|
109
|
-
|
85
|
+
context 'Off-market stock order' do
|
86
|
+
before(:all) do
|
87
|
+
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
|
88
|
+
@ib.wait_for :NextValidId
|
110
89
|
|
111
|
-
|
112
|
-
|
90
|
+
place_order IB::Symbols::Stocks[:wfc],
|
91
|
+
:limit_price => 9.13 # Set acceptable price
|
92
|
+
@ib.wait_for [:OpenOrder, 3], [:OrderStatus, 2]
|
93
|
+
end
|
113
94
|
|
114
|
-
|
115
|
-
order_status_should_be /Cancel/ # Cancelled / PendingCancel
|
116
|
-
end
|
95
|
+
after(:all) { close_connection }
|
117
96
|
|
118
|
-
|
119
|
-
alert = @ib.received[:Alert].first
|
120
|
-
alert.should be_an IB::Messages::Incoming::Alert
|
121
|
-
alert.message.should =~ /Order Canceled - reason:/
|
122
|
-
end
|
123
|
-
end # Cancelling
|
97
|
+
it_behaves_like 'Placed Order'
|
124
98
|
|
125
99
|
context "Cancelling wrong order" do
|
126
100
|
before(:all) do
|