ib-ruby 0.7.6 → 0.7.8

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.
Files changed (61) hide show
  1. data/HISTORY +8 -0
  2. data/Rakefile +8 -0
  3. data/VERSION +1 -1
  4. data/bin/fundamental_data +6 -9
  5. data/lib/ib-ruby/connection.rb +16 -19
  6. data/lib/ib-ruby/constants.rb +3 -1
  7. data/lib/ib-ruby/extensions.rb +5 -0
  8. data/lib/ib-ruby/messages/incoming/contract_data.rb +46 -45
  9. data/lib/ib-ruby/messages/incoming/delta_neutral_validation.rb +8 -5
  10. data/lib/ib-ruby/messages/incoming/execution_data.rb +2 -2
  11. data/lib/ib-ruby/messages/incoming/next_valid_id.rb +18 -0
  12. data/lib/ib-ruby/messages/incoming/open_order.rb +23 -16
  13. data/lib/ib-ruby/messages/incoming/order_status.rb +5 -3
  14. data/lib/ib-ruby/messages/incoming/scanner_data.rb +15 -11
  15. data/lib/ib-ruby/messages/incoming.rb +1 -5
  16. data/lib/ib-ruby/messages/outgoing/abstract_message.rb +2 -1
  17. data/lib/ib-ruby/messages/outgoing/place_order.rb +1 -1
  18. data/lib/ib-ruby/messages/outgoing.rb +1 -1
  19. data/lib/ib-ruby/models/bag.rb +59 -0
  20. data/lib/ib-ruby/models/combo_leg.rb +10 -6
  21. data/lib/ib-ruby/models/contract.rb +278 -0
  22. data/lib/ib-ruby/models/contract_detail.rb +70 -0
  23. data/lib/ib-ruby/models/execution.rb +22 -16
  24. data/lib/ib-ruby/models/model.rb +75 -17
  25. data/lib/ib-ruby/models/model_properties.rb +40 -26
  26. data/lib/ib-ruby/models/option.rb +62 -0
  27. data/lib/ib-ruby/models/order.rb +122 -86
  28. data/lib/ib-ruby/models/order_state.rb +11 -12
  29. data/lib/ib-ruby/models/underlying.rb +36 -0
  30. data/lib/ib-ruby/models.rb +1 -4
  31. data/spec/account_helper.rb +2 -1
  32. data/spec/db.rb +1 -1
  33. data/spec/db_helper.rb +105 -0
  34. data/spec/ib-ruby/connection_spec.rb +3 -3
  35. data/spec/ib-ruby/messages/incoming/open_order_spec.rb +5 -5
  36. data/spec/ib-ruby/messages/incoming/order_status_spec.rb +3 -3
  37. data/spec/ib-ruby/models/bag_spec.rb +15 -23
  38. data/spec/ib-ruby/models/bar_spec.rb +0 -5
  39. data/spec/ib-ruby/models/combo_leg_spec.rb +18 -25
  40. data/spec/ib-ruby/models/contract_detail_spec.rb +54 -0
  41. data/spec/ib-ruby/models/contract_spec.rb +25 -37
  42. data/spec/ib-ruby/models/execution_spec.rb +64 -19
  43. data/spec/ib-ruby/models/option_spec.rb +12 -34
  44. data/spec/ib-ruby/models/order_spec.rb +107 -45
  45. data/spec/ib-ruby/models/order_state_spec.rb +12 -12
  46. data/spec/ib-ruby/models/underlying_spec.rb +36 -0
  47. data/spec/integration/contract_info_spec.rb +65 -55
  48. data/spec/integration/fundamental_data_spec.rb +2 -2
  49. data/spec/integration/orders/attached_spec.rb +3 -3
  50. data/spec/integration/orders/combo_spec.rb +3 -3
  51. data/spec/integration/orders/placement_spec.rb +8 -8
  52. data/spec/integration/orders/{execution_spec.rb → trades_spec.rb} +8 -12
  53. data/spec/integration/orders/valid_ids_spec.rb +3 -3
  54. data/spec/message_helper.rb +1 -1
  55. data/spec/model_helper.rb +150 -85
  56. data/spec/order_helper.rb +35 -18
  57. metadata +18 -10
  58. data/lib/ib-ruby/models/contracts/bag.rb +0 -62
  59. data/lib/ib-ruby/models/contracts/contract.rb +0 -320
  60. data/lib/ib-ruby/models/contracts/option.rb +0 -66
  61. data/lib/ib-ruby/models/contracts.rb +0 -27
@@ -0,0 +1,62 @@
1
+ require 'ib-ruby/models/contract'
2
+
3
+ module IB
4
+ module Models
5
+ class Option < Contract
6
+
7
+ validates_numericality_of :strike, :greater_than => 0
8
+ validates_format_of :sec_type, :with => /^option$/,
9
+ :message => "should be an option"
10
+ validates_format_of :local_symbol, :with => /^\w+\s*\d{15}$|^$/,
11
+ :message => "invalid OSI code"
12
+ validates_format_of :right, :with => /^put$|^call$/,
13
+ :message => "should be put or call"
14
+
15
+ # For Options, this is contract's OSI (Option Symbology Initiative) name/code
16
+ alias osi local_symbol
17
+
18
+ def osi= value
19
+ # Normalize to 21 char
20
+ self.local_symbol = value.sub(/ /, ' '*(22-value.size))
21
+ end
22
+
23
+ # Make valid IB Contract definition from OSI (Option Symbology Initiative) code.
24
+ # NB: Simply making a new Contract with *local_symbol* (osi) property set to a
25
+ # valid OSI code works just as well, just do NOT set *expiry*, *right* or
26
+ # *strike* properties in this case.
27
+ # This class method provided as a backup and shows how to analyse OSI codes.
28
+ def self.from_osi osi
29
+
30
+ # Parse contract's OSI (OCC Option Symbology Initiative) code
31
+ args = osi.match(/(\w+)\s?(\d\d)(\d\d)(\d\d)([pcPC])(\d+)/).to_a.drop(1)
32
+ symbol = args.shift
33
+ year = 2000 + args.shift.to_i
34
+ month = args.shift.to_i
35
+ day = args.shift.to_i
36
+ right = args.shift.upcase
37
+ strike = args.shift.to_i/1000.0
38
+
39
+ # Set correct expiry date - IB expiry date differs from OSI if expiry date
40
+ # falls on Saturday (see https://github.com/arvicco/option_mower/issues/4)
41
+ expiry_date = Time.utc(year, month, day)
42
+ expiry_date = Time.utc(year, month, day-1) if expiry_date.wday == 6
43
+
44
+ new :symbol => symbol,
45
+ :exchange => "SMART",
46
+ :expiry => expiry_date.to_ib[2..7], # YYMMDD
47
+ :right => right,
48
+ :strike => strike
49
+ end
50
+
51
+ def default_attributes
52
+ {:sec_type => :option}.merge super
53
+ #self[:description] ||= osi ? osi : "#{symbol} #{strike} #{right} #{expiry}"
54
+ end
55
+
56
+ def to_human
57
+ "<Option: " + [symbol, expiry, right, strike, exchange, currency].join(" ") + ">"
58
+ end
59
+
60
+ end # class Option
61
+ end # module Models
62
+ end # module IB
@@ -12,10 +12,10 @@ module IB
12
12
  # your own Order IDs to avoid conflicts between orders placed from your API application.
13
13
 
14
14
  # Main order fields
15
- prop :order_id, # int: Order id associated with client (volatile).
15
+ prop [:local_id, :order_id], # int: Order id associated with client (volatile).
16
16
  :client_id, # int: The id of the client that placed this order.
17
17
  :perm_id, # int: TWS permanent id, remains the same over TWS sessions.
18
- :total_quantity, # int: The order quantity.
18
+ [:quantity, :total_quantity], # int: The order quantity.
19
19
 
20
20
  :order_type, # String: Order type.
21
21
  # Limit Risk: MTL / MKT PRT / QUOTE / STP / STP LMT / TRAIL / TRAIL LIMIT / TRAIL LIT / TRAIL MIT
@@ -213,9 +213,40 @@ module IB
213
213
  # for ComboLeg compatibility: SAME = 0; OPEN = 1; CLOSE = 2; UNKNOWN = 3;
214
214
  [:side, :action] => PROPS[:side] # String: Action/side: BUY/SELL/SSHORT/SSHORTX
215
215
 
216
- # Some properties received from IB are separated into OrderState object,
217
- # but they are still readable as Order properties through delegation.
216
+ prop :placed_at, :modified_at
217
+
218
+ # TODO: restore!
219
+ ## Returned in OpenOrder for Bag Contracts
220
+ ## public Vector<OrderComboLeg> m_orderComboLegs
221
+ #prop :algo_params, :leg_prices, :combo_params
222
+ #
223
+ #alias order_combo_legs leg_prices
224
+ #alias smart_combo_routing_params combo_params
218
225
  #
226
+ ##serialize :algo_params
227
+ ##serialize :leg_prices
228
+ ##serialize :combo_params
229
+
230
+ has_many :executions
231
+
232
+ # Order has a collection of OrderStates, last one is always current
233
+ has_many :order_states
234
+
235
+ def order_state
236
+ order_states.last
237
+ end
238
+
239
+ def order_state= state
240
+ self.order_states.push case state
241
+ when IB::OrderState
242
+ state
243
+ when Symbol, String
244
+ IB::OrderState.new :status => state
245
+ end
246
+ end
247
+
248
+ # Some properties received from IB are separated into OrderState object,
249
+ # but they are still readable as Order properties through delegation:
219
250
  # Properties arriving via OpenOrder message:
220
251
  [:commission, # double: Shows the commission amount on the order.
221
252
  :commission_currency, # String: Shows the currency of the commission.
@@ -229,78 +260,67 @@ module IB
229
260
  # Properties arriving via OrderStatus message:
230
261
  :filled, # int
231
262
  :remaining, # int
232
- :average_fill_price, # double
263
+ :price, # double
233
264
  :last_fill_price, # double
265
+ :average_price, # double
266
+ :average_fill_price, # double
234
267
  :why_held # String: comma-separated list of reasons for order to be held.
235
268
  ].each { |property| define_method(property) { order_state.send(property) } }
236
269
 
237
- # Returned in OpenOrder for Bag Contracts
238
- # public Vector<OrderComboLeg> m_orderComboLegs
239
- attr_accessor :leg_prices, :combo_params, :order_state
240
- alias order_combo_legs leg_prices
241
- alias smart_combo_routing_params combo_params
242
-
243
- # TODO: :created_at, :placed_at, :modified_at accessors
244
-
245
- # Order is not valid without correct :order_id
246
- validates_numericality_of :order_id, :only_integer => true
247
-
248
- DEFAULT_PROPS = {:aux_price => 0.0,
249
- :discretionary_amount => 0.0,
250
- :parent_id => 0,
251
- :tif => :day,
252
- :order_type => :limit,
253
- :open_close => :open,
254
- :origin => :customer,
255
- :short_sale_slot => :default,
256
- :trigger_method => :default,
257
- :oca_type => :none,
258
- :auction_strategy => :none,
259
- :designated_location => '',
260
- :exempt_code => -1,
261
- :display_size => 0,
262
- :continuous_update => 0,
263
- :delta_neutral_con_id => 0,
264
- :algo_strategy => '',
265
- # TODO: Add simple defaults to prop ?
266
- :transmit => true,
267
- :what_if => false,
268
- :hidden => false,
269
- :etrade_only => false,
270
- :firm_quote_only => false,
271
- :block_order => false,
272
- :all_or_none => false,
273
- :sweep_to_fill => false,
274
- :not_held => false,
275
- :outside_rth => false,
276
- :scale_auto_reset => false,
277
- :scale_random_percent => false,
278
- :opt_out_smart_routing => false,
279
- :override_percentage_constraints => false,
280
- }
281
-
282
- def initialize opts = {}
283
- @leg_prices = []
284
- @algo_params = {}
285
- @combo_params = {}
286
- @order_state = IB::OrderState.new
287
- super opts
270
+ # Order is not valid without correct :local_id (:order_id)
271
+ validates_numericality_of :local_id, :perm_id, :client_id, :parent_id,
272
+ :quantity, :min_quantity, :display_size,
273
+ :only_integer => true, :allow_nil => true
274
+
275
+ validates_numericality_of :limit_price, :aux_price, :allow_nil => true
276
+
277
+
278
+ def default_attributes
279
+ {:aux_price => 0.0,
280
+ :discretionary_amount => 0.0,
281
+ :parent_id => 0,
282
+ :tif => :day,
283
+ :order_type => :limit,
284
+ :open_close => :open,
285
+ :origin => :customer,
286
+ :short_sale_slot => :default,
287
+ :trigger_method => :default,
288
+ :oca_type => :none,
289
+ :auction_strategy => :none,
290
+ :designated_location => '',
291
+ :exempt_code => -1,
292
+ :display_size => 0,
293
+ :continuous_update => 0,
294
+ :delta_neutral_con_id => 0,
295
+ :algo_strategy => '',
296
+ :transmit => true,
297
+ :what_if => false,
298
+ :order_state => IB::OrderState.new(:status => 'New'),
299
+ # TODO: Add simple defaults to prop ?
300
+ }.merge super
288
301
  end
289
302
 
303
+ #after_initialize do #opts = {}
304
+ # #self.leg_prices = []
305
+ # #self.algo_params = {}
306
+ # #self.combo_params = {}
307
+ # #self.order_state ||= IB::OrderState.new :status => 'New'
308
+ #end
309
+
290
310
  # This returns an Array of data from the given order,
291
311
  # mixed with data from associated contract. Ugly mix, indeed.
292
312
  def serialize_with server, contract
293
313
  [contract.serialize_long(:con_id, :sec_id),
294
314
  # main order fields
295
315
  case side
296
- when :short
297
- 'SSHORT'
298
- when :short_exempt
299
- 'SSHORTX'
300
- else
301
- side.to_sup
316
+ when :short
317
+ 'SSHORT'
318
+ when :short_exempt
319
+ 'SSHORTX'
320
+ else
321
+ side.to_sup
302
322
  end,
303
- total_quantity,
323
+ quantity,
304
324
  self[:order_type], # Internal code, 'LMT' instead of :limit
305
325
  limit_price,
306
326
  aux_price,
@@ -312,12 +332,12 @@ module IB
312
332
  order_ref,
313
333
  transmit,
314
334
  parent_id,
315
- block_order,
316
- sweep_to_fill,
335
+ block_order || false,
336
+ sweep_to_fill || false,
317
337
  display_size,
318
338
  self[:trigger_method],
319
- outside_rth, # was: ignore_rth
320
- hidden,
339
+ outside_rth || false, # was: ignore_rth
340
+ hidden || false,
321
341
  contract.serialize_legs(:extended),
322
342
 
323
343
  # This is specific to PlaceOrder v.38, NOT supported by API yet!
@@ -351,11 +371,11 @@ module IB
351
371
  self[:oca_type],
352
372
  rule_80a,
353
373
  settling_firm,
354
- all_or_none,
374
+ all_or_none || false,
355
375
  min_quantity,
356
376
  percent_offset,
357
- etrade_only,
358
- firm_quote_only,
377
+ etrade_only || false,
378
+ firm_quote_only || false,
359
379
  nbbo_price_cap,
360
380
  self[:auction_strategy],
361
381
  starting_price,
@@ -363,7 +383,7 @@ module IB
363
383
  delta,
364
384
  stock_range_lower,
365
385
  stock_range_upper,
366
- override_percentage_constraints,
386
+ override_percentage_constraints || false,
367
387
  volatility, # Volatility orders
368
388
  self[:volatility_type], #
369
389
  self[:delta_neutral_order_type],
@@ -398,28 +418,26 @@ module IB
398
418
  [scale_price_adjust_value,
399
419
  scale_price_adjust_interval,
400
420
  scale_profit_offset,
401
- scale_auto_reset,
421
+ scale_auto_reset || false,
402
422
  scale_init_position,
403
423
  scale_init_fill_qty,
404
- scale_random_percent
424
+ scale_random_percent || false
405
425
  ]
406
426
  else
407
427
  []
408
428
  end,
409
429
 
410
430
  # TODO: Need to add support for hedgeType, not working ATM - beta only
411
- #if (m_serverVersion >= MIN_SERVER_VER_HEDGE_ORDERS) {
431
+ # if (m_serverVersion >= MIN_SERVER_VER_HEDGE_ORDERS) {
412
432
  # send (order.m_hedgeType);
413
- # if (!IsEmpty(order.m_hedgeType)) send (order.m_hedgeParam);
414
- #}
433
+ # if (!IsEmpty(order.m_hedgeType)) send (order.m_hedgeParam); }
415
434
  #
416
- #if (m_serverVersion >= MIN_SERVER_VER_OPT_OUT_SMART_ROUTING) {
417
- # send (order.m_optOutSmartRouting);
418
- #}
435
+ # if (m_serverVersion >= MIN_SERVER_VER_OPT_OUT_SMART_ROUTING) {
436
+ # send (order.m_optOutSmartRouting) ; || false }
419
437
 
420
438
  clearing_account,
421
439
  clearing_intent,
422
- not_held,
440
+ not_held || false,
423
441
  contract.serialize_under_comp,
424
442
  serialize_algo(),
425
443
  what_if]
@@ -435,25 +453,43 @@ module IB
435
453
  end
436
454
  end
437
455
 
456
+ # Placement
457
+ def place contract, connection
458
+ error "Unable to place order, next_local_id not known" unless connection.next_local_id
459
+ self.client_id = connection.server[:client_id]
460
+ self.local_id = connection.next_local_id
461
+ connection.next_local_id += 1
462
+ self.placed_at = Time.now
463
+ modify contract, connection, self.placed_at
464
+ end
465
+
466
+ # Modify Order (convenience wrapper for send_message :PlaceOrder). Returns order_id.
467
+ def modify contract, connection, time=Time.now
468
+ self.modified_at = time
469
+ connection.send_message :PlaceOrder,
470
+ :order => self,
471
+ :contract => contract,
472
+ :local_id => local_id
473
+ local_id
474
+ end
475
+
438
476
  # Order comparison
439
477
  def == other
440
478
  perm_id && other.perm_id && perm_id == other.perm_id ||
441
- order_id == other.order_id && # ((p __LINE__)||true) &&
479
+ local_id == other.local_id && # ((p __LINE__)||true) &&
442
480
  (client_id == other.client_id || client_id == 0 || other.client_id == 0) &&
443
481
  parent_id == other.parent_id &&
444
482
  tif == other.tif &&
445
483
  action == other.action &&
446
484
  order_type == other.order_type &&
447
- total_quantity == other.total_quantity &&
485
+ quantity == other.quantity &&
448
486
  (limit_price == other.limit_price || # TODO Floats should be Decimals!
449
487
  (limit_price - other.limit_price).abs < 0.00001) &&
450
488
  aux_price == other.aux_price &&
451
- outside_rth == other.outside_rth &&
452
489
  origin == other.origin &&
453
490
  designated_location == other.designated_location &&
454
491
  exempt_code == other.exempt_code &&
455
492
  what_if == other.what_if &&
456
- not_held == other.not_held &&
457
493
  algo_strategy == other.algo_strategy &&
458
494
  algo_params == other.algo_params
459
495
 
@@ -469,10 +505,10 @@ module IB
469
505
 
470
506
  def to_human
471
507
  "<Order: " + ((order_ref && order_ref != '') ? "#{order_ref} " : '') +
472
- "#{self[:order_type]} #{self[:tif]} #{side} #{total_quantity} " +
508
+ "#{self[:order_type]} #{self[:tif]} #{side} #{quantity} " +
473
509
  "#{status} " + (limit_price ? "#{limit_price} " : '') +
474
510
  ((aux_price && aux_price != 0) ? "/#{aux_price}" : '') +
475
- "##{order_id}/#{perm_id} from #{client_id}" +
511
+ "##{local_id}/#{perm_id} from #{client_id}" +
476
512
  (account ? "/#{account}" : '') +
477
513
  (commission ? " fee #{commission}" : '') + ">"
478
514
  end
@@ -7,7 +7,7 @@ module IB
7
7
  include ModelProperties
8
8
 
9
9
  #p column_names
10
- # has_one :order
10
+ belongs_to :order
11
11
 
12
12
  # Properties arriving via OpenOrder message
13
13
  prop :init_margin, # Float: The impact the order would have on your initial margin.
@@ -22,16 +22,16 @@ module IB
22
22
  # Properties arriving via OrderStatus message:
23
23
  prop :filled, # int
24
24
  :remaining, # int
25
- :average_fill_price, # double
26
- :last_fill_price, # double
25
+ [:price, :last_fill_price,], # double
26
+ [:average_price, :average_fill_price], # double
27
27
  :why_held # String: comma-separated list of reasons for order to be held.
28
28
 
29
29
  # Properties arriving in both messages:
30
- prop :order_id, # int: Order id associated with client (volatile).
30
+ prop [:local_id, :order_id], # int: Order id associated with client (volatile).
31
31
  :perm_id, # int: TWS permanent id, remains the same over TWS sessions.
32
32
  :client_id, # int: The id of the client that placed this order.
33
33
  :parent_id, # int: The order ID of the parent (original) order, used
34
- :status # String: Displays the order status. Possible values include:
34
+ :status => :s # String: Displays the order status. Possible values include:
35
35
  # - PendingSubmit - indicates that you have transmitted the order, but
36
36
  # have not yet received confirmation that it has been accepted by the
37
37
  # order destination. NOTE: This order status is NOT sent back by TWS
@@ -58,17 +58,16 @@ module IB
58
58
  # (simulated orders) or an exchange (native orders) but that currently
59
59
  # the order is inactive due to system, exchange or other issues.
60
60
 
61
- prop :tester
62
-
63
- validates_numericality_of :order_id, :perm_id, :client_id, :only_integer => true
64
-
65
- DEFAULT_PROPS = {:status => 'New'} # Starting new Orders with this status
61
+ validates_format_of :status, :without => /^$/, :message => 'must not be empty'
62
+ validates_numericality_of :price, :average_price, :allow_nil => true
63
+ validates_numericality_of :local_id, :perm_id, :client_id, :parent_id, :filled,
64
+ :remaining, :only_integer => true, :allow_nil => true
66
65
 
67
66
  # Comparison
68
67
  def == other
69
68
  other && other.is_a?(OrderState) &&
70
69
  status == other.status &&
71
- order_id == other.order_id &&
70
+ local_id == other.local_id &&
72
71
  perm_id == other.perm_id &&
73
72
  client_id == other.client_id &&
74
73
  filled == other.filled &&
@@ -83,7 +82,7 @@ module IB
83
82
  end
84
83
 
85
84
  def to_human
86
- "<OrderState: #{status} ##{order_id}/#{perm_id} from #{client_id}" +
85
+ "<OrderState: #{status} ##{local_id}/#{perm_id} from #{client_id}" +
87
86
  (filled ? " filled #{filled}/#{remaining}" : '') +
88
87
  (last_fill_price ? " at #{last_fill_price}/#{average_fill_price}" : '') +
89
88
  (init_margin ? " margin #{init_margin}/#{maint_margin}" : '') +
@@ -0,0 +1,36 @@
1
+ require 'ib-ruby/models/contract_detail'
2
+
3
+ module IB
4
+ module Models
5
+
6
+ # Calculated characteristics of underlying Contract (volatile)
7
+ class Underlying < Model.for(:underlying)
8
+ include ModelProperties
9
+
10
+ has_one :contract
11
+
12
+ prop :con_id, # Id of the Underlying Contract
13
+ :delta, # double: The underlying stock or future delta.
14
+ :price # double: The price of the underlying.
15
+
16
+ validates_numericality_of :con_id, :delta, :price #, :allow_nil => true
17
+
18
+ def default_attributes
19
+ {:con_id => 0}.merge super
20
+ end
21
+
22
+ # Serialize under_comp parameters
23
+ def serialize
24
+ [true, con_id, delta, price]
25
+ end
26
+
27
+ # Comparison
28
+ def == other
29
+ con_id == other.con_id && delta == other.delta && price == other.price
30
+ end
31
+
32
+ end # class Contract
33
+ UnderComp = Underlying
34
+
35
+ end # module Models
36
+ end # module IB
@@ -3,16 +3,13 @@ module IB
3
3
 
4
4
  require 'ib-ruby/models/model_properties'
5
5
  require 'ib-ruby/models/model'
6
- require 'ib-ruby/models/contracts'
7
- # Flatten namespace (IB::Models::Option instead of IB::Models::Contracts::Option)
8
- include Contracts
9
6
 
7
+ require 'ib-ruby/models/contract'
10
8
  require 'ib-ruby/models/order_state'
11
9
  require 'ib-ruby/models/order'
12
10
  require 'ib-ruby/models/combo_leg'
13
11
  require 'ib-ruby/models/execution'
14
12
  require 'ib-ruby/models/bar'
15
-
16
13
  end
17
14
  end
18
15
 
@@ -1,3 +1,5 @@
1
+ require 'message_helper'
2
+
1
3
  # Make sure integration tests are only run against the pre-configured PAPER ACCOUNT
2
4
  def verify_account
3
5
  return OPTS[:account_verified] if OPTS[:account_verified]
@@ -16,7 +18,6 @@ def verify_account
16
18
  @ib = IB::Connection.new OPTS[:connection].merge(:logger => mock_logger)
17
19
 
18
20
  @ib.wait_for :ManagedAccounts, 5
19
- puts @ib.received.map { |type, msg| [" #{type}:", msg.map(&:to_human)] }
20
21
  raise "Unable to verify IB PAPER ACCOUNT" unless @ib.received?(:ManagedAccounts)
21
22
 
22
23
  received = @ib.received[:ManagedAccounts].first.accounts_list
data/spec/db.rb CHANGED
@@ -10,7 +10,7 @@ require 'database_cleaner'
10
10
  db_file = Pathname.new(__FILE__).realpath.dirname + '../db/config.yml'
11
11
  raise "Unable to find DB config file: #{db_file}" unless db_file.exist?
12
12
 
13
- env = RUBY_PLATFORM =~ /java/ ? 'test-jruby' : 'test'
13
+ env = RUBY_PLATFORM =~ /java/ ? 'test' : 'test-mri'
14
14
  db_config = YAML::load_file(db_file)[env]
15
15
 
16
16
  # Establish connection to test DB
data/spec/db_helper.rb ADDED
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'Valid DB-backed Model' do
4
+
5
+ context 'with DB backend', :db => true do
6
+ after(:all) { DatabaseCleaner.clean }
7
+
8
+ it_behaves_like 'Model with associations'
9
+
10
+ it 'is saved' do
11
+ subject.save.should be_true
12
+ @saved = subject
13
+ end
14
+
15
+ it 'does not set created and updated properties to SAVED model' do
16
+ subject.created_at.should be_a Time
17
+ subject.updated_at.should be_a Time
18
+ end
19
+
20
+ it 'saves a single model' do
21
+ all_models = described_class.find(:all)
22
+ all_models.should have_exactly(1).model
23
+ end
24
+
25
+ it 'loads back in the same valid state as saved' do
26
+ model = described_class.find(:first)
27
+ model.object_id.should_not == subject.object_id
28
+ #model.valid?
29
+ #p model.errors
30
+ model.should be_valid
31
+ model.should == subject
32
+ end
33
+
34
+ it 'and with the same properties' do
35
+ model = described_class.find(:first)
36
+ #p model.attributes
37
+ #p model.content_attributes
38
+ props.each do |name, value|
39
+ model.send(name).should == value
40
+ end
41
+ end
42
+
43
+ it 'updates timestamps when saving the model' do
44
+ model = described_class.find(:first)
45
+ model.created_at.usec.should_not == subject.created_at.utc.usec #be_a Time
46
+ model.updated_at.usec.should_not == subject.updated_at.utc.usec #be_a Time
47
+ end
48
+
49
+ it 'is loads back with associations, if any' do
50
+ if defined? associations
51
+ end
52
+ end
53
+
54
+ end # DB
55
+ end
56
+
57
+ shared_examples_for 'Invalid DB-backed Model' do
58
+
59
+ context 'with DB backend', :db => true do
60
+ after(:all) { DatabaseCleaner.clean }
61
+
62
+ it_behaves_like 'Model with associations'
63
+
64
+ it 'is not saved' do
65
+ subject.save.should be_false
66
+ end
67
+
68
+ it 'is not loaded' do
69
+ models = described_class.find(:all)
70
+ models.should have_exactly(0).model
71
+ end
72
+ end # DB
73
+ end
74
+
75
+ shared_examples_for 'Model with associations' do
76
+ it 'works with associations, if any' do
77
+ if defined? associations
78
+ associations.each do |assoc, items|
79
+ proxy = subject.association(assoc).reflection
80
+ #pp proxy
81
+
82
+ owner_name = described_class.to_s.demodulize.tableize.singularize
83
+ [items].flatten.each do |item|
84
+ if proxy.collection?
85
+ association = subject.send("#{assoc}")
86
+ association << item
87
+
88
+ p 'collection'
89
+ association.should include item
90
+ #p association.first.send(owner_name)
91
+ #.should include item
92
+ #association.
93
+ #association.size.should == items.size # Not for Order, +1 OrderState
94
+ else
95
+ subject.send "#{assoc}=", item
96
+ association = subject.send("#{assoc}")
97
+ p 'not a collection'
98
+ association.should == item
99
+ end
100
+ end
101
+
102
+ end
103
+ end
104
+ end
105
+ end
@@ -21,7 +21,7 @@ shared_examples_for 'Connected Connection without receiver' do
21
21
  its(:server) { should be_a Hash }
22
22
  its(:server) { should have_key :reader }
23
23
  its(:subscribers) { should have_at_least(1).item } # :NextValidId and empty Hashes
24
- its(:next_order_id) { should be_a Fixnum } # Not before :NextValidId arrives
24
+ its(:next_local_id) { should be_a Fixnum } # Not before :NextValidId arrives
25
25
  end
26
26
 
27
27
  # Need top level method to access instance var (@received) in nested context
@@ -192,7 +192,7 @@ describe IB::Connection do
192
192
  its(:server) { should_not have_key :reader }
193
193
  its(:received) { should be_empty }
194
194
  its(:subscribers) { should be_empty }
195
- its(:next_order_id) { should be_nil }
195
+ its(:next_local_id) { should be_nil }
196
196
 
197
197
  describe 'connecting idle conection' do
198
198
  before(:all) do
@@ -219,7 +219,7 @@ describe IB::Connection do
219
219
  its(:server) { should_not have_key :reader }
220
220
  its(:received) { should be_empty }
221
221
  its(:subscribers) { should be_empty }
222
- its(:next_order_id) { should be_nil }
222
+ its(:next_local_id) { should be_nil }
223
223
 
224
224
  describe 'connecting idle conection' do
225
225
  before(:all) do