ib-ruby 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +4 -0
- data/VERSION +1 -1
- data/db/migrate/131_add_orders.rb +3 -3
- data/lib/ib-ruby/models/model.rb +1 -0
- data/lib/ib-ruby/models/order.rb +5 -12
- data/spec/ib-ruby/connection_spec.rb +1 -1
- data/spec/ib-ruby/models/order_spec.rb +135 -99
- data/tasks/common.rake +1 -14
- metadata +31 -10
- data/TODO +0 -43
data/HISTORY
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.8.
|
1
|
+
0.8.1
|
@@ -83,9 +83,9 @@ class AddOrders < ActiveRecord::Migration
|
|
83
83
|
t.float :basis_points_type # double: EFP orders only
|
84
84
|
t.string :algo_strategy
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
86
|
+
t.text :leg_prices # Vector<OrderComboLeg> m_orderComboLegs
|
87
|
+
t.text :algo_params # public Vector<TagValue> m_algoParams; ?!
|
88
|
+
t.text :combo_params # not used yet
|
89
89
|
|
90
90
|
t.integer :scale_init_level_size # int: Size of the first (initial) order component.
|
91
91
|
t.integer :scale_subs_level_size # int: Order size of the subsequent scale order
|
data/lib/ib-ruby/models/model.rb
CHANGED
data/lib/ib-ruby/models/order.rb
CHANGED
@@ -222,9 +222,9 @@ module IB
|
|
222
222
|
alias order_combo_legs leg_prices
|
223
223
|
alias smart_combo_routing_params combo_params
|
224
224
|
|
225
|
-
|
226
|
-
|
227
|
-
|
225
|
+
serialize :leg_prices
|
226
|
+
serialize :algo_params, Hash
|
227
|
+
serialize :combo_params
|
228
228
|
|
229
229
|
# Order is always placed for a contract. Here, we explicitly set this link.
|
230
230
|
belongs_to :contract
|
@@ -306,8 +306,8 @@ module IB
|
|
306
306
|
:transmit => true,
|
307
307
|
:what_if => false,
|
308
308
|
:leg_prices => [],
|
309
|
-
:algo_params =>
|
310
|
-
:combo_params =>
|
309
|
+
:algo_params => HashWithIndifferentAccess.new, #{},
|
310
|
+
:combo_params => HashWithIndifferentAccess.new, #{},
|
311
311
|
:order_state => IB::OrderState.new(:status => 'New',
|
312
312
|
:filled => 0,
|
313
313
|
:remaining => 0,
|
@@ -315,13 +315,6 @@ module IB
|
|
315
315
|
:average_price => 0)
|
316
316
|
end
|
317
317
|
|
318
|
-
#after_initialize do #opts = {}
|
319
|
-
# #self.leg_prices = []
|
320
|
-
# #self.algo_params = {}
|
321
|
-
# #self.combo_params = {}
|
322
|
-
# #self.order_state ||= IB::OrderState.new :status => 'New'
|
323
|
-
#end
|
324
|
-
|
325
318
|
def serialize_algo
|
326
319
|
if algo_strategy.nil? || algo_strategy.empty?
|
327
320
|
''
|
@@ -103,7 +103,7 @@ describe IB::Connection do
|
|
103
103
|
|
104
104
|
before(:all) do
|
105
105
|
@ib.send_message :RequestAccountData
|
106
|
-
@ib.wait_for :AccountDownloadEnd
|
106
|
+
@ib.wait_for :AccountDownloadEnd, 3
|
107
107
|
end
|
108
108
|
|
109
109
|
after(:all) { @ib.send_message :RequestAccountData, :subscribe => false }
|
@@ -1,87 +1,87 @@
|
|
1
1
|
require 'model_helper'
|
2
2
|
|
3
3
|
describe IB::Models::Order,
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
4
|
+
:props =>
|
5
|
+
{:local_id => 23,
|
6
|
+
:order_ref => 'Test',
|
7
|
+
:client_id => 1111,
|
8
|
+
:perm_id => 173276893,
|
9
|
+
:parent_id => 0,
|
10
|
+
:side => :buy,
|
11
|
+
:order_type => :market_if_touched,
|
12
|
+
:limit_price => 0.1,
|
13
|
+
:quantity => 100,
|
14
|
+
:tif => :good_till_cancelled,
|
15
|
+
:open_close => :close,
|
16
|
+
:oca_group => '',
|
17
|
+
:oca_type => :reduce_no_block,
|
18
|
+
:origin => :firm,
|
19
|
+
:designated_location => "WHATEVER",
|
20
|
+
:exempt_code => 123,
|
21
|
+
:delta_neutral_order_type => :market,
|
22
|
+
:transmit => false,
|
23
|
+
:outside_rth => true,
|
24
|
+
:what_if => true,
|
25
|
+
:not_held => true},
|
26
|
+
|
27
|
+
# TODO: :presents => { Object => "Formatted"}
|
28
|
+
:human => "<Order: Test MIT GTC buy 100 New 0.1 #23/173276893 from 1111>",
|
29
|
+
|
30
|
+
:errors => {:side =>["should be buy/sell/short"]},
|
31
|
+
|
32
|
+
:assigns =>
|
33
|
+
{[:order_type, :delta_neutral_order_type] => codes_and_values_for(:order_type),
|
34
|
+
|
35
|
+
:open_close =>
|
36
|
+
{[42, nil, 'Foo', :bar] => /should be same.open.close.unknown/,
|
37
|
+
['SAME', 'same', 'S', 's', :same, 0, '0'] => :same,
|
38
|
+
['OPEN', 'open', 'O', 'o', :open, 1, '1'] => :open,
|
39
|
+
['CLOSE', 'close', 'C', 'c', :close, 2, '2'] => :close,
|
40
|
+
['UNKNOWN', 'unknown', 'U', 'u', :unknown, 3, '3'] => :unknown,
|
41
|
+
},
|
42
|
+
|
43
|
+
[:what_if, :not_held, :outside_rth, :hidden, :transmit, :block_order,
|
44
|
+
:sweep_to_fill, :override_percentage_constraints, :all_or_none,
|
45
|
+
:etrade_only, :firm_quote_only, :opt_out_smart_routing, :scale_auto_reset,
|
46
|
+
:scale_random_percent] => boolean_assigns,
|
47
|
+
|
48
|
+
[:local_id, :perm_id, :parent_id] => numeric_or_nil_assigns,
|
49
|
+
},
|
50
|
+
|
51
|
+
:aliases =>
|
52
|
+
{[:side, :action] => buy_sell_short_assigns,
|
53
|
+
[:quantity, :total_quantity] => numeric_or_nil_assigns,
|
54
|
+
},
|
55
|
+
|
56
|
+
:collections =>
|
57
|
+
{:order_states =>[{:status => :Foo},
|
58
|
+
{:status => 'Bar'},],
|
59
|
+
|
60
|
+
:executions =>
|
61
|
+
[{:local_id => 23,
|
62
|
+
:client_id => 1111,
|
63
|
+
:perm_id => 173276893,
|
64
|
+
:exchange => "IDEALPRO",
|
65
|
+
:exec_id => "0001f4e8.4f5d48f1.01.01",
|
66
|
+
:price => 0.1,
|
67
|
+
:average_price => 0.1,
|
68
|
+
:shares => 40,
|
69
|
+
:cumulative_quantity => 40,
|
70
|
+
:side => :buy,
|
71
|
+
:time => "20120312 15:41:09"},
|
72
|
+
|
73
|
+
{:local_id => 23,
|
74
|
+
:client_id => 1111,
|
75
|
+
:perm_id => 173276893,
|
76
|
+
:exchange => "IDEALPRO",
|
77
|
+
:exec_id => "0001f4e8.4f5d48f1.01.02",
|
78
|
+
:price => 0.1,
|
79
|
+
:average_price => 0.1,
|
80
|
+
:shares => 60,
|
81
|
+
:cumulative_quantity => 100,
|
82
|
+
:side => :buy,
|
83
|
+
:time => "20120312 15:41:10"}]
|
84
|
+
} do
|
85
85
|
|
86
86
|
it_behaves_like 'Self-equal Model'
|
87
87
|
it_behaves_like 'Model with invalid defaults'
|
@@ -173,53 +173,89 @@ describe IB::Models::Order,
|
|
173
173
|
|
174
174
|
it 'is not equal for Orders with different limit price' do
|
175
175
|
order1 = IB::Order.new :quantity => 100,
|
176
|
-
|
177
|
-
|
176
|
+
:limit_price => 1,
|
177
|
+
:action => 'BUY'
|
178
178
|
|
179
179
|
order2 = IB::Order.new :total_quantity => 100,
|
180
|
-
|
181
|
-
|
180
|
+
:limit_price => 2,
|
181
|
+
:action => 'BUY'
|
182
182
|
order1.should_not == order2
|
183
183
|
order2.should_not == order1
|
184
184
|
end
|
185
185
|
|
186
186
|
it 'is not equal for Orders with different total_quantity' do
|
187
187
|
order1 = IB::Order.new :quantity => 20000,
|
188
|
-
|
189
|
-
|
188
|
+
:limit_price => 1,
|
189
|
+
:action => 'BUY'
|
190
190
|
|
191
191
|
order2 = IB::Order.new :total_quantity => 100,
|
192
|
-
|
193
|
-
|
192
|
+
:action => 'BUY',
|
193
|
+
:limit_price => 1
|
194
194
|
order1.should_not == order2
|
195
195
|
order2.should_not == order1
|
196
196
|
end
|
197
197
|
|
198
198
|
it 'is not equal for Orders with different action/side' do
|
199
199
|
order1 = IB::Order.new :quantity => 100,
|
200
|
-
|
201
|
-
|
200
|
+
:limit_price => 1,
|
201
|
+
:action => 'SELL'
|
202
202
|
|
203
203
|
order2 = IB::Order.new :quantity => 100,
|
204
|
-
|
205
|
-
|
204
|
+
:action => 'BUY',
|
205
|
+
:limit_price => 1
|
206
206
|
order1.should_not == order2
|
207
207
|
order2.should_not == order1
|
208
208
|
end
|
209
209
|
|
210
210
|
it 'is not equal for Orders with different order_type' do
|
211
211
|
order1 = IB::Order.new :quantity => 100,
|
212
|
-
|
213
|
-
|
214
|
-
|
212
|
+
:limit_price => 1,
|
213
|
+
:action => 'BUY',
|
214
|
+
:order_type => 'LMT'
|
215
215
|
|
216
216
|
order2 = IB::Order.new :quantity => 100,
|
217
|
-
|
218
|
-
|
219
|
-
|
217
|
+
:action => 'BUY',
|
218
|
+
:limit_price => 1,
|
219
|
+
:order_type => 'MKT'
|
220
220
|
order1.should_not == order2
|
221
221
|
order2.should_not == order1
|
222
222
|
end
|
223
223
|
end
|
224
224
|
|
225
|
+
context 'DB-backed serialization of properties', :db => true do
|
226
|
+
let(:serializable_props) do
|
227
|
+
{
|
228
|
+
:algo_strategy => "ArrivalPx",
|
229
|
+
:algo_params => { "maxPctVol" => "0.01",
|
230
|
+
"riskAversion" => "Passive",
|
231
|
+
"startTime" => "9:00:00 EST",
|
232
|
+
"endTime" => "15:00:00 EST",
|
233
|
+
"forceCompletion" => "0",
|
234
|
+
"allowPastEndTime" => "1"},
|
235
|
+
:combo_params => {"NonGuaranteed" => "1",
|
236
|
+
"LeginPrio" => "0"},
|
237
|
+
:leg_prices => [1,2,3],
|
238
|
+
}
|
239
|
+
end
|
240
|
+
|
241
|
+
subject { IB::Order.new(props.merge(serializable_props)) }
|
242
|
+
|
243
|
+
after(:all) { DatabaseCleaner.clean }
|
244
|
+
|
245
|
+
it 'is saved to DB with serializable_props' do
|
246
|
+
subject.save.should be_true
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'is loaded from DB with serializable_props' do
|
250
|
+
models = described_class.find(:all)
|
251
|
+
models.should have_exactly(1).model
|
252
|
+
order = models.first
|
253
|
+
order.algo_strategy.should == serializable_props[:algo_strategy]
|
254
|
+
order.algo_params.should == serializable_props[:algo_params]
|
255
|
+
order.combo_params.should == serializable_props[:combo_params]
|
256
|
+
order.leg_prices.should == serializable_props[:leg_prices]
|
257
|
+
p order.combo_params
|
258
|
+
end
|
259
|
+
end # DB
|
260
|
+
|
225
261
|
end # describe IB::Order
|
data/tasks/common.rake
CHANGED
@@ -1,18 +1,5 @@
|
|
1
|
-
|
2
|
-
#task 'gem:release' => 'test:run'
|
1
|
+
task :default => 'spec:spec'
|
3
2
|
|
4
3
|
task :notes do
|
5
4
|
puts 'Output annotations (TBD)'
|
6
5
|
end
|
7
|
-
|
8
|
-
#Bundler not ready for prime time just yet
|
9
|
-
#desc 'Bundle dependencies'
|
10
|
-
#task :bundle do
|
11
|
-
# output = `bundle check 2>&1`
|
12
|
-
#
|
13
|
-
# unless $?.to_i == 0
|
14
|
-
# puts output
|
15
|
-
# system "bundle install"
|
16
|
-
# puts
|
17
|
-
# end
|
18
|
-
#end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: ib-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.8.
|
5
|
+
version: 0.8.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Paul Legato
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-06-
|
13
|
+
date: 2012-06-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -34,13 +34,13 @@ dependencies:
|
|
34
34
|
requirements:
|
35
35
|
- - ! '>='
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version:
|
37
|
+
version: 3.2.0
|
38
38
|
none: false
|
39
39
|
requirement: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version:
|
43
|
+
version: 3.2.0
|
44
44
|
none: false
|
45
45
|
prerelease: false
|
46
46
|
type: :runtime
|
@@ -84,13 +84,13 @@ dependencies:
|
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
86
86
|
- !ruby/object:Gem::Version
|
87
|
-
version:
|
87
|
+
version: 0.7.2
|
88
88
|
none: false
|
89
89
|
requirement: !ruby/object:Gem::Requirement
|
90
90
|
requirements:
|
91
91
|
- - ! '>='
|
92
92
|
- !ruby/object:Gem::Version
|
93
|
-
version:
|
93
|
+
version: 0.7.2
|
94
94
|
none: false
|
95
95
|
prerelease: false
|
96
96
|
type: :development
|
@@ -110,6 +110,24 @@ dependencies:
|
|
110
110
|
none: false
|
111
111
|
prerelease: false
|
112
112
|
type: :development
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: standalone_migrations
|
115
|
+
version_requirements: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: !binary |-
|
120
|
+
MA==
|
121
|
+
none: false
|
122
|
+
requirement: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: !binary |-
|
127
|
+
MA==
|
128
|
+
none: false
|
129
|
+
prerelease: false
|
130
|
+
type: :development
|
113
131
|
- !ruby/object:Gem::Dependency
|
114
132
|
name: my_scripts
|
115
133
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -416,8 +434,6 @@ files:
|
|
416
434
|
VkVSU0lPTg==
|
417
435
|
- !binary |-
|
418
436
|
SElTVE9SWQ==
|
419
|
-
- !binary |-
|
420
|
-
VE9ETw==
|
421
437
|
- !binary |-
|
422
438
|
LmdpdGlnbm9yZQ==
|
423
439
|
homepage: https://github.com/pjlegato/ib-ruby
|
@@ -430,6 +446,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
430
446
|
requirements:
|
431
447
|
- - ! '>='
|
432
448
|
- !ruby/object:Gem::Version
|
449
|
+
segments:
|
450
|
+
- 0
|
451
|
+
hash: 2
|
433
452
|
version: !binary |-
|
434
453
|
MA==
|
435
454
|
none: false
|
@@ -437,12 +456,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
437
456
|
requirements:
|
438
457
|
- - ! '>='
|
439
458
|
- !ruby/object:Gem::Version
|
459
|
+
segments:
|
460
|
+
- 0
|
461
|
+
hash: 2
|
440
462
|
version: !binary |-
|
441
463
|
MA==
|
442
464
|
none: false
|
443
465
|
requirements: []
|
444
466
|
rubyforge_project:
|
445
|
-
rubygems_version: 1.8.
|
467
|
+
rubygems_version: 1.8.24
|
446
468
|
signing_key:
|
447
469
|
specification_version: 3
|
448
470
|
summary: Ruby Implementation of the Interactive Brokers TWS API
|
@@ -535,4 +557,3 @@ test_files:
|
|
535
557
|
c3BlYy9pbnRlZ3JhdGlvbi9vcmRlcnMvdHJhZGVzX3NwZWMucmI=
|
536
558
|
- !binary |-
|
537
559
|
c3BlYy9pbnRlZ3JhdGlvbi9vcmRlcnMvdmFsaWRfaWRzX3NwZWMucmI=
|
538
|
-
...
|
data/TODO
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
Plan:
|
2
|
-
|
3
|
-
1. IB#send_message method should accept block, thus compressing subscribe/send_message
|
4
|
-
pair into a single call - to simplify DSL.
|
5
|
-
|
6
|
-
2. IB Connection uses delays to prevent hitting 50 msgs/sec limit:
|
7
|
-
http://finance.groups.yahoo.com/group/TWSAPI/message/25413
|
8
|
-
|
9
|
-
3. IB Connection reconnects gracefully in case if TWS disconnects/reconnects
|
10
|
-
|
11
|
-
4. Messages should be Models as well (audit trail), @received_at timestamp in messages
|
12
|
-
|
13
|
-
5. Detailed message documentation
|
14
|
-
|
15
|
-
6. Move Float values to Decimal (roundoff errors showed in spec!)
|
16
|
-
|
17
|
-
|
18
|
-
Done:
|
19
|
-
|
20
|
-
1. Create integration tests for basic use cases
|
21
|
-
|
22
|
-
2. IB#subscribe should accept regexes.
|
23
|
-
|
24
|
-
3. Compatibility with API v.966, 967
|
25
|
-
|
26
|
-
4. Collect all received messages in Connection#received[:type] by default
|
27
|
-
|
28
|
-
5. Flow handlers: Connection#wait_for / Connection#received?
|
29
|
-
|
30
|
-
6. Add ActiveRecord backend to all Models
|
31
|
-
|
32
|
-
7. Make ActiveModel-like attributes Hash and serialization for tableless Models
|
33
|
-
|
34
|
-
|
35
|
-
Ideas for future:
|
36
|
-
|
37
|
-
1. Decouple Broker-specific Adapter from universal high-level messaging layer
|
38
|
-
(potentially adding other broker adapters)
|
39
|
-
|
40
|
-
2. Tweak IB::Message API for speed (use class methods)
|
41
|
-
|
42
|
-
3. Create integration tests for more use cases (spec/README)
|
43
|
-
|