ib-ruby 0.7.12 → 0.8.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 +44 -21
- data/VERSION +1 -1
- data/lib/ib-ruby/messages/incoming/contract_data.rb +1 -1
- data/lib/ib-ruby/messages/incoming/execution_data.rb +1 -1
- data/lib/ib-ruby/messages/incoming/open_order.rb +1 -2
- data/lib/ib-ruby/messages/incoming.rb +5 -5
- data/lib/ib-ruby/messages/outgoing/place_order.rb +137 -3
- data/lib/ib-ruby/models/order.rb +19 -153
- data/spec/README.md +47 -12
- data/spec/account_helper.rb +12 -3
- data/spec/ib-ruby/messages/incoming/open_order_spec.rb +15 -4
- data/spec/integration/contract_info_spec.rb +6 -0
- data/spec/integration/orders/attached_spec.rb +18 -19
- data/spec/integration/orders/combo_spec.rb +1 -0
- data/spec/integration/orders/trades_spec.rb +81 -1
- data/spec/order_helper.rb +0 -23
- data/spec/spec_helper.rb +4 -2
- data/spec/tws.rb +4 -0
- metadata +523 -299
@@ -203,3 +203,9 @@ describe "Request Contract Info", :connected => true, :integration => true do
|
|
203
203
|
end
|
204
204
|
end # Request Forex data
|
205
205
|
end # Contract Data
|
206
|
+
|
207
|
+
|
208
|
+
__END__
|
209
|
+
ContractData messages v.6 and 8 identical:
|
210
|
+
10-8-500-GOOG-OPT-20130118-500-C-SMART-USD-GOOG 130119C00500000-GOOG-GOOG-81032967-0.05-100-ACTIVETIM,ADJUST,ALERT,ALGO,ALLOC,AON,AVGCOST,BASKET,COND,CONDORDER,DAY,DEACT,DEACTDIS,DEACTEOD,FOK,GAT,GTC,GTD,GTT,HID,ICE,IOC,LIT,LMT,MIT,MKT,MTL,NONALGO,OCA,PAON,POSTONLY,RELSTK,SCALE,SCALERST,SMARTSTG,STP,STPLMT,TRAIL,TRAILLIT,TRAILLMT,TRAILMIT,VOLAT,WHATIF,-SMART,AMEX,BATS,BOX,CBOE,CBOE2,IBSX,ISE,MIBSX,NASDAQOM,PHLX,PSE-1-30351181-GOOGLE INC-CL A--201301-Communications-Internet-Web Portals/ISP-EST-20120428:CLOSED;20120430:0930-1600-20120428:CLOSED;20120430:0930-1600-
|
211
|
+
10-6-500-GOOG-OPT-20130118-500-C-SMART-USD-GOOG 130119C00500000-GOOG-GOOG-81032967-0.01-100-ACTIVETIM,ADJUST,ALERT,ALGO,ALLOC,AON,AVGCOST,BASKET,COND,CONDORDER,DAY,DEACT,DEACTDIS,DEACTEOD,FOK,GAT,GTC,GTD,GTT,HID,ICE,IOC,LIT,LMT,MIT,MKT,MTL,NONALGO,OCA,PAON,POSTONLY,RELSTK,SCALE,SCALERST,SMARTSTG,STP,STPLMT,TRAIL,TRAILLIT,TRAILLMT,TRAILMIT,VOLAT,WHATIF,-SMART,AMEX,BATS,BOX,CBOE,CBOE2,IBSX,ISE,MIBSX,NASDAQOM,PHLX,PSE-1-30351181-GOOGLE INC-CL A--201301-Communications-Internet-Web Portals/ISP-EST-20120428:CLOSED;20120430:0930-1600-20120428:CLOSED;20120430:0930-1600-
|
@@ -4,8 +4,8 @@ require 'combo_helper'
|
|
4
4
|
def define_contracts
|
5
5
|
@ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
|
6
6
|
@contracts = {
|
7
|
-
|
8
|
-
|
7
|
+
:stock => IB::Symbols::Stocks[:wfc],
|
8
|
+
:butterfly => butterfly('GOOG', '201301', 'CALL', 500, 510, 520)
|
9
9
|
}
|
10
10
|
close_connection
|
11
11
|
end
|
@@ -19,13 +19,12 @@ describe 'Attached Orders', :connected => true, :integration => true do
|
|
19
19
|
|
20
20
|
# Testing different combinations of Parent + Attached Orders:
|
21
21
|
[
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
[:stock, 100, 'DAY', 'LMT', 9.13, 20.0], # Parent + takeprofit target
|
23
|
+
#[:stock, 100, 'DAY', 'STP', 9.13, 0.0, 8.0], # Parent + stoploss
|
24
|
+
#[:stock, 100, 'GTC', 'LMT', 9.13, 20.0], # GTC Parent + target
|
25
|
+
[:butterfly, 10, 'DAY', 'LMT', 0.05, 1.0], # Combo Parent + target
|
26
|
+
#[:butterfly, 10, 'GTC', 'LMT', 0.05, 1.0], # GTC Combo Parent + target
|
27
|
+
#[:butterfly, 100, 'GTC', 'STPLMT', 0.05, 0.05, 1.0], # GTC Combo Parent + stoplimit target
|
29
28
|
].each do |(contract, qty, tif, attach_type, limit_price, attach_price, aux_price)|
|
30
29
|
context "#{tif} BUY (#{contract}) limit order with attached #{attach_type} SELL" do
|
31
30
|
let(:contract_type) { contract }
|
@@ -38,10 +37,10 @@ describe 'Attached Orders', :connected => true, :integration => true do
|
|
38
37
|
#p [contract, qty, tif, attach_type, limit_price, attach_price, aux_price]
|
39
38
|
@contract = @contracts[contract]
|
40
39
|
place_order @contract,
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
:total_quantity => qty,
|
41
|
+
:limit_price => limit_price,
|
42
|
+
:tif => tif,
|
43
|
+
:transmit => false
|
45
44
|
|
46
45
|
@ib.wait_for :OpenOrder, :OrderStatus, 2
|
47
46
|
end
|
@@ -56,12 +55,12 @@ describe 'Attached Orders', :connected => true, :integration => true do
|
|
56
55
|
context "Attaching #{attach_type} order" do
|
57
56
|
before(:all) do
|
58
57
|
@attached_order = IB::Order.new :limit_price => attach_price,
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
58
|
+
:aux_price => aux_price || 0,
|
59
|
+
:total_quantity => qty,
|
60
|
+
:side => :sell,
|
61
|
+
:tif => tif,
|
62
|
+
:order_type => attach_type,
|
63
|
+
:parent_id => @local_id_placed
|
65
64
|
|
66
65
|
@local_id_attached = @ib.place_order @attached_order, @contract
|
67
66
|
@local_id_after = @ib.next_local_id
|
@@ -1,5 +1,48 @@
|
|
1
1
|
require 'order_helper'
|
2
2
|
|
3
|
+
def commission_report_should_be status=:with_pnl, exec=@ib.received[:ExecutionData].last.execution
|
4
|
+
msg = @ib.received[:CommissionReport].find { |msg| msg.exec_id == exec.exec_id }
|
5
|
+
msg.should_not be_nil
|
6
|
+
msg.should be_an IB::Messages::Incoming::CommissionReport
|
7
|
+
|
8
|
+
msg.exec_id.should == exec.exec_id
|
9
|
+
msg.commission.should == 2.5 # Fixed commission for Forex
|
10
|
+
msg.currency.should == 'USD'
|
11
|
+
msg.yield.should be_nil
|
12
|
+
msg.yield_redemption_date.should == 0 # no date, YYYYMMDD format for bonds
|
13
|
+
|
14
|
+
if status == :with_pnl
|
15
|
+
msg.realized_pnl.should be_a Float
|
16
|
+
msg.realized_pnl.should be < 10000 # Not Double.MAX_VALUE
|
17
|
+
else
|
18
|
+
msg.realized_pnl.should be_nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def execution_should_be side, opts={}
|
23
|
+
msg = @ib.received[:ExecutionData][opts[:index] || -1]
|
24
|
+
msg.request_id.should == (opts[:request_id] || -1)
|
25
|
+
msg.contract.should == @contract
|
26
|
+
|
27
|
+
exec = msg.execution
|
28
|
+
exec.perm_id.should be_an Integer
|
29
|
+
exec.perm_id.should == @ib.received[:OpenOrder].last.order.perm_id if @ib.received?(:OpenOrder)
|
30
|
+
exec.client_id.should == OPTS[:connection][:client_id]
|
31
|
+
exec.local_id.should be_an Integer
|
32
|
+
exec.local_id.should == @order.local_id if @order
|
33
|
+
exec.exec_id.should be_a String
|
34
|
+
exec.time.should =~ /\d\d:\d\d:\d\d/
|
35
|
+
exec.account_name.should == OPTS[:connection][:account_name]
|
36
|
+
exec.exchange.should == 'IDEALPRO'
|
37
|
+
exec.side.should == side
|
38
|
+
exec.shares.should == 20000
|
39
|
+
exec.cumulative_quantity.should == 20000
|
40
|
+
exec.price.should be > 1
|
41
|
+
exec.price.should be < 2
|
42
|
+
exec.price.should == exec.average_price
|
43
|
+
exec.liquidation.should == false
|
44
|
+
end
|
45
|
+
|
3
46
|
describe "Trades", :connected => true, :integration => true, :slow => true do
|
4
47
|
|
5
48
|
before(:all) { verify_account }
|
@@ -21,7 +64,7 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
21
64
|
:total_quantity => 20000,
|
22
65
|
:limit_price => 2,
|
23
66
|
:action => 'BUY'
|
24
|
-
|
67
|
+
#:what_if => true
|
25
68
|
|
26
69
|
@ib.wait_for(5, :ExecutionData, :OpenOrder) do
|
27
70
|
@ib.received[:OpenOrder].last &&
|
@@ -57,6 +100,12 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
57
100
|
it 'receives OrderStatus with fill details' do
|
58
101
|
status_should_be 'Filled'
|
59
102
|
end
|
103
|
+
|
104
|
+
it 'also receives Commission Reports' do
|
105
|
+
@ib.received[:CommissionReport].should have_exactly(1).report
|
106
|
+
|
107
|
+
commission_report_should_be :no_pnl, @ib.received[:ExecutionData].last.execution
|
108
|
+
end
|
60
109
|
end # Placing BUY
|
61
110
|
|
62
111
|
context "Placing SELL order" do
|
@@ -99,6 +148,12 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
99
148
|
it 'receives OrderStatus with fill details' do
|
100
149
|
status_should_be 'Filled'
|
101
150
|
end
|
151
|
+
|
152
|
+
it 'also receives Commission Reports' do
|
153
|
+
@ib.received[:CommissionReport].should have_exactly(1).report
|
154
|
+
|
155
|
+
commission_report_should_be :with_pnl, @ib.received[:ExecutionData].last.execution
|
156
|
+
end
|
102
157
|
end # Placing SELL
|
103
158
|
|
104
159
|
context "Request executions" do
|
@@ -124,9 +179,34 @@ describe "Trades", :connected => true, :integration => true, :slow => true do
|
|
124
179
|
end
|
125
180
|
|
126
181
|
it 'receives Execution Data' do
|
182
|
+
execution_should_be :buy, :index => 0, :request_id => 456
|
127
183
|
execution_should_be :sell, :request_id => 456
|
128
184
|
end
|
185
|
+
|
186
|
+
it 'also receives Commission Reports' do
|
187
|
+
@ib.received[:CommissionReport].should have_exactly(2).reports
|
188
|
+
|
189
|
+
commission_report_should_be :no_pnl, @ib.received[:ExecutionData].first.execution
|
190
|
+
commission_report_should_be :with_pnl, @ib.received[:ExecutionData].last.execution
|
191
|
+
end
|
192
|
+
|
129
193
|
end # Request executions
|
130
194
|
end # Forex order
|
131
195
|
|
132
196
|
end # Trades
|
197
|
+
|
198
|
+
__END__
|
199
|
+
|
200
|
+
Actual message exchange for "Request executions" (TWS 925):
|
201
|
+
10:57:41:348 <- 7-3-456-1111--20120430 10:57:31-----
|
202
|
+
10:57:41:349 -> 11-9-456-1-12087792-EUR-CASH--0.0--IDEALPRO-USD-EUR.USD-0001f4e8.4f9dbb0b.01.01-20120430 10:57:36-DU118180-IDEALPRO-BOT-20000-1.32540-474073463-1111-0-20000-1.32540--
|
203
|
+
10:57:41:350 -> 11-9-456-2-12087792-EUR-CASH--0.0--IDEALPRO-USD-EUR.USD-0001f4e8.4f9dbb0c.01.01-20120430 10:57:36-DU118180-IDEALPRO-SLD-20000-1.32540-474073464-1111-0-20000-1.32540--
|
204
|
+
10:57:41:350 -> 59-1-0001f4e8.4f9dbb0b.01.01-2.5-USD-1.7976931348623157E308-1.7976931348623157E308--
|
205
|
+
10:57:41:350 -> 59-1-0001f4e8.4f9dbb0c.01.01-2.5-USD-27.7984-1.7976931348623157E308--
|
206
|
+
10:57:41:351 -> 55-1-456-
|
207
|
+
|
208
|
+
Actual message exchange for "Request executions" (TWS 923):
|
209
|
+
11:11:45:436 <- 7-3-456-1111--20120430 11:11:36-----
|
210
|
+
11:11:45:439 -> 11-8-456-1-12087792-EUR-CASH--0.0--IDEALPRO-USD-EUR.USD-0001f4e8.4f9dbc96.01.01-20120430 11:11:43-DU118180-IDEALPRO-BOT-20000-1.32485-308397342-1111-0-20000-1.32485--
|
211
|
+
11:11:45:439 -> 11-8-456-2-12087792-EUR-CASH--0.0--IDEALPRO-USD-EUR.USD-0001f4e8.4f9dbc9a.01.01-20120430 11:11:45-DU118180-IDEALPRO-SLD-20000-1.32480-308397343-1111-0-20000-1.32480--
|
212
|
+
11:11:45:439 -> 55-1-456-
|
data/spec/order_helper.rb
CHANGED
@@ -219,26 +219,3 @@ def order_should_be status, order=@order
|
|
219
219
|
msg.contract.should == @contract
|
220
220
|
end
|
221
221
|
|
222
|
-
def execution_should_be side, opts={}
|
223
|
-
msg = @ib.received[:ExecutionData][opts[:index] || -1]
|
224
|
-
msg.request_id.should == (opts[:request_id] || -1)
|
225
|
-
msg.contract.should == @contract
|
226
|
-
|
227
|
-
exec = msg.execution
|
228
|
-
exec.perm_id.should be_an Integer
|
229
|
-
exec.perm_id.should == @ib.received[:OpenOrder].last.order.perm_id if @ib.received?(:OpenOrder)
|
230
|
-
exec.client_id.should == OPTS[:connection][:client_id]
|
231
|
-
exec.local_id.should be_an Integer
|
232
|
-
exec.local_id.should == @order.local_id if @order
|
233
|
-
exec.exec_id.should be_a String
|
234
|
-
exec.time.should =~ /\d\d:\d\d:\d\d/
|
235
|
-
exec.account_name.should == OPTS[:connection][:account_name]
|
236
|
-
exec.exchange.should == 'IDEALPRO'
|
237
|
-
exec.side.should == side
|
238
|
-
exec.shares.should == 20000
|
239
|
-
exec.cumulative_quantity.should == 20000
|
240
|
-
exec.price.should be > 1
|
241
|
-
exec.price.should be < 2
|
242
|
-
exec.price.should == exec.average_price
|
243
|
-
exec.liquidation.should == false
|
244
|
-
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'rspec'
|
2
2
|
require 'ib-ruby'
|
3
3
|
|
4
|
-
|
4
|
+
PORT ||= 4001 # 4001 for Gateway, 7496 for TWS GUI
|
5
|
+
|
6
|
+
# Top (test suite) level metadata
|
5
7
|
OPTS ||= {
|
6
8
|
:verbose => false, #true, # Verbosity of test outputs
|
7
9
|
:brokertron => false, # Use mock (Brokertron) instead of paper account
|
@@ -20,7 +22,7 @@ else
|
|
20
22
|
{:account_name => 'DU118180', # Your IB PAPER ACCOUNT, tests will only run against it
|
21
23
|
:client_id => 1111, # Just an arbitrary id
|
22
24
|
:host => '10.211.55.2', # Where your TWS/gateway is located, likely '127.0.0.1'
|
23
|
-
:port =>
|
25
|
+
:port => PORT,
|
24
26
|
:reuters => true # Subscription to Reuters data enabled ?
|
25
27
|
}
|
26
28
|
end
|
data/spec/tws.rb
ADDED