ib-ruby 0.8.0 → 0.8.1
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/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
|
-
|