ib-ruby 0.7.12 → 0.8.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 +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