ib-ruby 0.6.1 → 0.7.0
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/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
|