ib-ruby 0.8.1 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. data/.gitignore +0 -1
  2. data/HISTORY +5 -0
  3. data/README.md +47 -53
  4. data/Rakefile +2 -1
  5. data/VERSION +1 -1
  6. data/app/assets/javascripts/ib/application.js +15 -0
  7. data/app/assets/javascripts/ib/underlyings.js +2 -0
  8. data/app/assets/stylesheets/ib/application.css +13 -0
  9. data/app/assets/stylesheets/ib/underlyings.css +4 -0
  10. data/app/assets/stylesheets/scaffold.css +56 -0
  11. data/app/controllers/ib/application_controller.rb +5 -0
  12. data/app/controllers/ib/underlyings_controller.rb +87 -0
  13. data/app/helpers/ib/application_helper.rb +4 -0
  14. data/app/helpers/ib/underlyings_helper.rb +4 -0
  15. data/app/models/ib/underlying.rb +5 -0
  16. data/app/views/ib/underlyings/_form.html.erb +33 -0
  17. data/app/views/ib/underlyings/edit.html.erb +6 -0
  18. data/app/views/ib/underlyings/index.html.erb +29 -0
  19. data/app/views/ib/underlyings/new.html.erb +5 -0
  20. data/app/views/ib/underlyings/show.html.erb +25 -0
  21. data/app/views/layouts/ib/application.html.erb +14 -0
  22. data/config/routes.rb +6 -0
  23. data/db/config.yml +19 -0
  24. data/db/migrate/{101_add_executions.rb → 101_add_ib_executions.rb} +2 -2
  25. data/db/migrate/{111_add_bars.rb → 111_add_ib_bars.rb} +2 -2
  26. data/db/migrate/{121_add_order_states.rb → 121_add_ib_order_states.rb} +2 -2
  27. data/db/migrate/{131_add_orders.rb → 131_add_ib_orders.rb} +2 -2
  28. data/db/migrate/{141_add_combo_legs.rb → 141_add_ib_combo_legs.rb} +2 -2
  29. data/db/migrate/{151_add_underlyings.rb → 151_add_ib_underlyings.rb} +2 -2
  30. data/db/migrate/{161_add_contract_details.rb → 161_add_ib_contract_details.rb} +2 -2
  31. data/db/migrate/{171_add_contracts.rb → 171_add_ib_contracts.rb} +2 -2
  32. data/db/schema.rb +245 -0
  33. data/lib/ib/base.rb +97 -0
  34. data/lib/ib/base_properties.rb +140 -0
  35. data/lib/{ib-ruby → ib}/connection.rb +2 -2
  36. data/lib/{ib-ruby → ib}/constants.rb +0 -0
  37. data/lib/{ib-ruby → ib}/db.rb +9 -5
  38. data/lib/ib/engine.rb +35 -0
  39. data/lib/{ib-ruby → ib}/errors.rb +0 -0
  40. data/lib/{ib-ruby → ib}/extensions.rb +2 -2
  41. data/lib/{ib-ruby → ib}/logger.rb +0 -0
  42. data/lib/{ib-ruby → ib}/messages/abstract_message.rb +0 -0
  43. data/lib/{ib-ruby → ib}/messages/incoming/abstract_message.rb +1 -1
  44. data/lib/{ib-ruby → ib}/messages/incoming/alert.rb +0 -0
  45. data/lib/{ib-ruby → ib}/messages/incoming/contract_data.rb +0 -0
  46. data/lib/{ib-ruby → ib}/messages/incoming/delta_neutral_validation.rb +0 -0
  47. data/lib/{ib-ruby → ib}/messages/incoming/execution_data.rb +0 -0
  48. data/lib/{ib-ruby → ib}/messages/incoming/historical_data.rb +0 -0
  49. data/lib/{ib-ruby → ib}/messages/incoming/market_depths.rb +0 -0
  50. data/lib/{ib-ruby → ib}/messages/incoming/next_valid_id.rb +0 -0
  51. data/lib/{ib-ruby → ib}/messages/incoming/open_order.rb +0 -0
  52. data/lib/{ib-ruby → ib}/messages/incoming/order_status.rb +0 -0
  53. data/lib/{ib-ruby → ib}/messages/incoming/portfolio_value.rb +0 -0
  54. data/lib/{ib-ruby → ib}/messages/incoming/real_time_bar.rb +0 -0
  55. data/lib/{ib-ruby → ib}/messages/incoming/scanner_data.rb +0 -0
  56. data/lib/{ib-ruby → ib}/messages/incoming/ticks.rb +0 -0
  57. data/lib/{ib-ruby → ib}/messages/incoming.rb +14 -14
  58. data/lib/{ib-ruby → ib}/messages/outgoing/abstract_message.rb +1 -1
  59. data/lib/{ib-ruby → ib}/messages/outgoing/bar_requests.rb +0 -0
  60. data/lib/{ib-ruby → ib}/messages/outgoing/place_order.rb +0 -0
  61. data/lib/{ib-ruby → ib}/messages/outgoing.rb +5 -5
  62. data/lib/ib/messages.rb +8 -0
  63. data/lib/ib/model.rb +8 -0
  64. data/lib/ib/models.rb +10 -0
  65. data/lib/ib/requires.rb +9 -0
  66. data/lib/{ib-ruby → ib}/socket.rb +0 -0
  67. data/lib/{ib-ruby → ib}/symbols/forex.rb +1 -1
  68. data/lib/{ib-ruby → ib}/symbols/futures.rb +2 -2
  69. data/lib/{ib-ruby → ib}/symbols/options.rb +1 -1
  70. data/lib/{ib-ruby → ib}/symbols/stocks.rb +1 -1
  71. data/lib/ib/symbols.rb +9 -0
  72. data/lib/{ib-ruby → ib}/version.rb +0 -0
  73. data/lib/ib-ruby.rb +2 -24
  74. data/lib/ib.rb +23 -0
  75. data/lib/models/ib/bag.rb +51 -0
  76. data/lib/models/ib/bar.rb +41 -0
  77. data/lib/models/ib/combo_leg.rb +102 -0
  78. data/lib/models/ib/contract.rb +287 -0
  79. data/lib/models/ib/contract_detail.rb +68 -0
  80. data/lib/models/ib/execution.rb +62 -0
  81. data/lib/models/ib/option.rb +60 -0
  82. data/lib/models/ib/order.rb +389 -0
  83. data/lib/models/ib/order_state.rb +126 -0
  84. data/lib/models/ib/underlying.rb +35 -0
  85. data/spec/README.md +34 -2
  86. data/spec/TODO +5 -1
  87. data/spec/comb.rb +13 -0
  88. data/spec/db.rb +1 -1
  89. data/spec/db_helper.rb +3 -3
  90. data/spec/dummy.rb +13 -0
  91. data/spec/gw.rb +4 -0
  92. data/spec/{ib-ruby → ib}/connection_spec.rb +0 -0
  93. data/spec/{ib-ruby → ib}/messages/incoming/alert_spec.rb +0 -0
  94. data/spec/{ib-ruby → ib}/messages/incoming/open_order_spec.rb +0 -0
  95. data/spec/{ib-ruby → ib}/messages/incoming/order_status_spec.rb +16 -17
  96. data/spec/{ib-ruby → ib}/messages/outgoing/account_data_spec.rb +0 -0
  97. data/spec/{ib-ruby → ib}/messages/outgoing/market_data_type_spec.rb +0 -0
  98. data/spec/integration/historic_data_spec.rb +3 -3
  99. data/spec/integration/orders/trades_spec.rb +1 -1
  100. data/spec/{ib-ruby/models → models/ib}/bag_spec.rb +2 -7
  101. data/spec/{ib-ruby/models → models/ib}/bar_spec.rb +1 -6
  102. data/spec/{ib-ruby/models → models/ib}/combo_leg_spec.rb +2 -12
  103. data/spec/{ib-ruby/models → models/ib}/contract_detail_spec.rb +3 -8
  104. data/spec/{ib-ruby/models → models/ib}/contract_spec.rb +4 -12
  105. data/spec/{ib-ruby/models → models/ib}/execution_spec.rb +2 -7
  106. data/spec/{ib-ruby/models → models/ib}/option_spec.rb +1 -6
  107. data/spec/{ib-ruby/models → models/ib}/order_spec.rb +5 -10
  108. data/spec/{ib-ruby/models → models/ib}/order_state_spec.rb +2 -7
  109. data/spec/{ib-ruby/models → models/ib}/underlying_spec.rb +3 -7
  110. data/spec/my.rb +5 -0
  111. data/spec/spec_helper.rb +62 -36
  112. metadata +417 -544
  113. data/lib/ib-ruby/messages.rb +0 -8
  114. data/lib/ib-ruby/models/bag.rb +0 -54
  115. data/lib/ib-ruby/models/bar.rb +0 -43
  116. data/lib/ib-ruby/models/combo_leg.rb +0 -104
  117. data/lib/ib-ruby/models/contract.rb +0 -287
  118. data/lib/ib-ruby/models/contract_detail.rb +0 -70
  119. data/lib/ib-ruby/models/execution.rb +0 -64
  120. data/lib/ib-ruby/models/model.rb +0 -105
  121. data/lib/ib-ruby/models/model_properties.rb +0 -146
  122. data/lib/ib-ruby/models/option.rb +0 -62
  123. data/lib/ib-ruby/models/order.rb +0 -389
  124. data/lib/ib-ruby/models/order_state.rb +0 -128
  125. data/lib/ib-ruby/models/underlying.rb +0 -36
  126. data/lib/ib-ruby/models.rb +0 -15
  127. data/lib/ib-ruby/symbols.rb +0 -9
  128. data/spec/test.rb +0 -61
@@ -1,4 +1,4 @@
1
- require 'ib-ruby/messages/outgoing/abstract_message'
1
+ require 'ib/messages/outgoing/abstract_message'
2
2
 
3
3
  # TODO: Don't instantiate messages, use their classes as just namespace for .encode/decode
4
4
 
@@ -108,7 +108,7 @@ module IB
108
108
  :side)
109
109
 
110
110
  # data = { :id => ticker_id (int),
111
- # :contract => Contract,
111
+ # :contract => IB::Contract,
112
112
  # :exercise_action => int, 1 = exercise, 2 = lapse
113
113
  # :exercise_quantity => int, The number of contracts to be exercised
114
114
  # :account => string,
@@ -130,7 +130,7 @@ module IB
130
130
 
131
131
  # @data={:id => int: ticker_id - Must be a unique value. When the market data
132
132
  # returns, it will be identified by this tag,
133
- # :contract => Models::Contract, requested contract.
133
+ # :contract => IB::Contract, requested contract.
134
134
  # :tick_list => String: comma delimited list of requested tick groups:
135
135
  # Group ID - Description - Requested Tick Types
136
136
  # 100 - Option Volume (currently for stocks) - 29, 30
@@ -268,8 +268,8 @@ module IB
268
268
  :scanner_setting_pairs,
269
269
  :stock_type_filter)
270
270
 
271
- require 'ib-ruby/messages/outgoing/place_order'
272
- require 'ib-ruby/messages/outgoing/bar_requests'
271
+ require 'ib/messages/outgoing/place_order'
272
+ require 'ib/messages/outgoing/bar_requests'
273
273
 
274
274
  end # module Outgoing
275
275
  end # module Messages
@@ -0,0 +1,8 @@
1
+ module IB
2
+ module Messages
3
+ end
4
+ end
5
+
6
+ require 'ib/messages/outgoing'
7
+ require 'ib/messages/incoming'
8
+
data/lib/ib/model.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'ib/base_properties'
2
+ require 'ib/base'
3
+
4
+ module IB
5
+ # IB Models can be either lightweight (tableless) or database-backed.
6
+ # require 'ib/db' - to make all IB models database-backed
7
+ Model = IB.db_backed? ? ActiveRecord::Base : IB::Base
8
+ end
data/lib/ib/models.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'ib/model'
2
+
3
+ require 'models/ib/contract_detail'
4
+ require 'models/ib/underlying'
5
+ require 'models/ib/contract'
6
+ require 'models/ib/order_state'
7
+ require 'models/ib/order'
8
+ require 'models/ib/combo_leg'
9
+ require 'models/ib/execution'
10
+ require 'models/ib/bar'
@@ -0,0 +1,9 @@
1
+ require 'ib/version'
2
+ require 'ib/extensions'
3
+ require 'ib/errors'
4
+ require 'ib/constants'
5
+ require 'ib/connection'
6
+
7
+ require 'ib/models'
8
+ require 'ib/messages'
9
+ require 'ib/symbols'
File without changes
@@ -1,4 +1,4 @@
1
- # Note that the :description field is particular to ib-ruby, and is NOT part of the
1
+ # Note that the :description field is particular to ib, and is NOT part of the
2
2
  # standard TWS API. It is never transmitted to IB. It's purely used clientside, and
3
3
  # you can store any arbitrary string that you may find useful there.
4
4
  module IB
@@ -4,7 +4,7 @@
4
4
  # takes over as the volume leader.
5
5
  #
6
6
  #
7
- # Note that the :description field is particular to ib-ruby, and is NOT part of the standard TWS API.
7
+ # Note that the :description field is particular to ib, and is NOT part of the standard TWS API.
8
8
  # It is never transmitted to IB. It's purely used clientside, and you can store any arbitrary string that
9
9
  # you may find useful there.
10
10
  #
@@ -39,7 +39,7 @@ module IB
39
39
  "#{ next_quarter_year(time) }#{ sprintf("%02d", next_quarter_month(time)) }"
40
40
  end
41
41
 
42
- # Convenience method; generates a Models::Contract instance for a futures
42
+ # Convenience method; generates an IB::Contract instance for a futures
43
43
  # contract with the given parameters.
44
44
  #
45
45
  # If expiry is nil, it will use the end month of the current
@@ -1,6 +1,6 @@
1
1
  # Stock contracts definitions
2
2
  #
3
- # Note that the :description field is particular to ib-ruby, and is NOT part of the
3
+ # Note that the :description field is particular to ib, and is NOT part of the
4
4
  # standard TWS API. It is never transmitted to IB. It's purely used clientside, and
5
5
  # you can store any arbitrary string that you may find useful there.
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Stock contracts definitions
2
2
  #
3
- # Note that the :description field is particular to ib-ruby, and is NOT part of the
3
+ # Note that the :description field is particular to ib, and is NOT part of the
4
4
  # standard TWS API. It is never transmitted to IB. It's purely used clientside, and
5
5
  # you can store any arbitrary string that you may find useful there.
6
6
 
data/lib/ib/symbols.rb ADDED
@@ -0,0 +1,9 @@
1
+ module IB
2
+ module Symbols
3
+ end
4
+ end
5
+
6
+ require 'ib/symbols/forex'
7
+ require 'ib/symbols/futures'
8
+ require 'ib/symbols/stocks'
9
+ require 'ib/symbols/options'
File without changes
data/lib/ib-ruby.rb CHANGED
@@ -1,24 +1,2 @@
1
- module IB
2
- # IB Models can be either database-backed, or not
3
- # By default there is no DB backend, unless specifically requested
4
- # require 'ib-ruby/db' # to make all IB models database-backed
5
- DB ||= false
6
-
7
- require 'ib-ruby/version'
8
- require 'ib-ruby/extensions'
9
- require 'ib-ruby/errors'
10
- require 'ib-ruby/constants'
11
- require 'ib-ruby/connection'
12
-
13
- require 'ib-ruby/models'
14
- Datatypes = Models # Flatten namespace (IB::Contract instead of IB::Models::Contract)
15
- include Models # Legacy alias
16
-
17
- require 'ib-ruby/messages'
18
- IncomingMessages = Messages::Incoming # Legacy alias
19
- OutgoingMessages = Messages::Outgoing # Legacy alias
20
-
21
- require 'ib-ruby/symbols'
22
- end
23
- IbRuby = IB
24
-
1
+ require 'ib'
2
+
data/lib/ib.rb ADDED
@@ -0,0 +1,23 @@
1
+ module IB
2
+ def self.db_backed?
3
+ !!defined?(IB::DB)
4
+ end
5
+
6
+ def self.rails?
7
+ !!defined?(Rails) && Rails.respond_to?('env')
8
+ end
9
+
10
+ end # module IB
11
+
12
+ IbRuby = IB
13
+ Ib = IB
14
+
15
+ # IB Models can be either lightweight (tableless) or database-backed.
16
+ # By default there is no DB backend, unless specifically requested
17
+ # require 'ib/db' # to make all IB models database-backed
18
+
19
+ if IB.rails?
20
+ require 'ib/engine'
21
+ else
22
+ require 'ib/requires'
23
+ end
@@ -0,0 +1,51 @@
1
+ require 'models/ib/contract'
2
+
3
+ module IB
4
+
5
+ # "BAG" is not really a contract, but a combination (combo) of securities.
6
+ # AKA basket or bag of securities. Individual securities in combo are represented
7
+ # by ComboLeg objects.
8
+ class Bag < Contract
9
+ # General Notes:
10
+ # 1. :exchange for the leg definition must match that of the combination order.
11
+ # The exception is for a STK legs, which must specify the SMART exchange.
12
+ # 2. :symbol => "USD" For combo Contract, this is an arbitrary value (like "USD")
13
+
14
+ validates_format_of :sec_type, :with => /^bag$/, :message => "should be a bag"
15
+ validates_format_of :right, :with => /^none$/, :message => "should be none"
16
+ validates_format_of :expiry, :with => /^$/, :message => "should be blank"
17
+
18
+ def default_attributes
19
+ super.merge :sec_type => :bag #,:legs => Array.new,
20
+ end
21
+
22
+ def description
23
+ self[:description] || to_human
24
+ end
25
+
26
+ def to_human
27
+ "<Bag: #{[symbol, exchange, currency].join(' ')} legs: #{legs_description} >"
28
+ end
29
+
30
+ ### Leg-related methods
31
+
32
+ # TODO: Rewrite with legs and legs_description being strictly in sync...
33
+ # TODO: Find a way to serialize legs without references...
34
+ # IB-equivalent leg description.
35
+ def legs_description
36
+ self[:legs_description] || legs.map { |leg| "#{leg.con_id}|#{leg.weight}" }.join(',')
37
+ end
38
+
39
+ # Check if two Contracts have same legs (maybe in different order)
40
+ def same_legs? other
41
+ legs == other.legs ||
42
+ legs_description.split(',').sort == other.legs_description.split(',').sort
43
+ end
44
+
45
+ # Contract comparison
46
+ def == other
47
+ super && same_legs?(other)
48
+ end
49
+
50
+ end # class Bag
51
+ end # IB
@@ -0,0 +1,41 @@
1
+ module IB
2
+ # This is a single data point delivered by HistoricData or RealTimeBar messages.
3
+ # Instantiate with a Hash of attributes, to be auto-set via initialize in Model.
4
+ class Bar < IB::Model
5
+ include BaseProperties
6
+
7
+ prop :open, # The bar opening price.
8
+ :high, # The high price during the time covered by the bar.
9
+ :low, # The low price during the time covered by the bar.
10
+ :close, # The bar closing price.
11
+ :volume, # Volume
12
+ :wap, # Weighted average price during the time covered by the bar.
13
+ :trades, # int: When TRADES data history is returned, represents number
14
+ # of trades that occurred during the time period the bar covers
15
+ :time, # TODO: convert into Time object?
16
+ # The date-time stamp of the start of the bar. The format is
17
+ # determined by the reqHistoricalData() formatDate parameter.
18
+ :has_gaps => :bool # Whether or not there are gaps in the data.
19
+
20
+ validates_numericality_of :open, :high, :low, :close, :volume
21
+
22
+ # Order comparison
23
+ def == other
24
+ time == other.time &&
25
+ open == other.open &&
26
+ high == other.high &&
27
+ low == other.low &&
28
+ close == other.close &&
29
+ wap == other.wap &&
30
+ trades == other.trades &&
31
+ volume == other.volume
32
+ end
33
+
34
+ def to_human
35
+ "<Bar: #{time} wap #{wap} OHLC #{open} #{high} #{low} #{close} " +
36
+ (trades ? "trades #{trades}" : "") + " vol #{volume} gaps #{has_gaps}>"
37
+ end
38
+
39
+ alias to_s to_human
40
+ end # class Bar
41
+ end # module IB
@@ -0,0 +1,102 @@
1
+ module IB
2
+
3
+ # ComboLeg is essentially a join Model between Combo (BAG) Contract and
4
+ # individual Contracts (securities) that this BAG contains.
5
+ class ComboLeg < IB::Model
6
+ include BaseProperties
7
+
8
+ # BAG Combo Contract that contains this Leg
9
+ belongs_to :combo, :class_name => 'Contract'
10
+ # Contract that constitutes this Leg
11
+ belongs_to :leg_contract, :class_name => 'Contract', :foreign_key => :leg_contract_id
12
+
13
+ # General Notes:
14
+ # 1. The exchange for the leg definition must match that of the combination order.
15
+ # The exception is for a STK leg definition, which must specify the SMART exchange.
16
+
17
+ prop :con_id, # int: The unique contract identifier specifying the security.
18
+ :ratio, # int: Select the relative number of contracts for the leg you
19
+ # are constructing. To help determine the ratio for a
20
+ # specific combination order, refer to the Interactive
21
+ # Analytics section of the User's Guide.
22
+
23
+ :exchange, # String: exchange to which the complete combo order will be routed.
24
+ # For institutional customers only! For stock legs when doing short sale
25
+ :short_sale_slot, # int: 0 - retail(default), 1 = clearing broker, 2 = third party
26
+ :designated_location, # String: Only for shortSaleSlot == 2.
27
+ # Otherwise leave blank or orders will be rejected.
28
+ :exempt_code, # int: ?
29
+ [:side, :action] => PROPS[:side], # String: Action/side: BUY/SELL/SSHORT/SSHORTX
30
+ :open_close => PROPS[:open_close]
31
+ # int: Whether the order is an open or close order. Values:
32
+ # SAME = 0 Same as the parent security. The only option for retail customers.
33
+ # OPEN = 1 Open. This value is only valid for institutional customers.
34
+ # CLOSE = 2 Close. This value is only valid for institutional customers.
35
+ # UNKNOWN = 3
36
+
37
+ # Extra validations
38
+ validates_numericality_of :ratio, :con_id
39
+ validates_format_of :designated_location, :with => /^$/,
40
+ :message => "should be blank or orders will be rejected"
41
+
42
+ def default_attributes
43
+ super.merge :con_id => 0,
44
+ :ratio => 1,
45
+ :side => :buy,
46
+ :open_close => :same, # The only option for retail customers.
47
+ :short_sale_slot => :default,
48
+ :designated_location => '',
49
+ :exchange => 'SMART', # Unless SMART, Order modification fails
50
+ :exempt_code => -1
51
+ end
52
+
53
+ # Leg's weight is a combination of action and ratio
54
+ def weight
55
+ side == :buy ? ratio : -ratio
56
+ end
57
+
58
+ def weight= value
59
+ value = value.to_i
60
+ if value > 0
61
+ self.side = :buy
62
+ self.ratio = value
63
+ else
64
+ self.side = :sell
65
+ self.ratio = -value
66
+ end
67
+ end
68
+
69
+ # Some messages include open_close, some don't. wtf.
70
+ def serialize *fields
71
+ [con_id,
72
+ ratio,
73
+ side.to_sup,
74
+ exchange,
75
+ (fields.include?(:extended) ?
76
+ [self[:open_close],
77
+ self[:short_sale_slot],
78
+ designated_location,
79
+ exempt_code] :
80
+ [])
81
+ ].flatten
82
+ end
83
+
84
+ def to_human
85
+ "<ComboLeg: #{side} #{ratio} con_id #{con_id} at #{exchange}>"
86
+ end
87
+
88
+ # Order comparison
89
+ def == other
90
+ other && other.is_a?(ComboLeg) &&
91
+ con_id == other.con_id &&
92
+ ratio == other.ratio &&
93
+ open_close == other.open_close &&
94
+ short_sale_slot == other.short_sale_slot&&
95
+ exempt_code == other.exempt_code &&
96
+ side == other.side &&
97
+ exchange == other.exchange &&
98
+ designated_location == other.designated_location
99
+ end
100
+
101
+ end # ComboLeg
102
+ end # module IB
@@ -0,0 +1,287 @@
1
+ require 'models/ib/contract_detail'
2
+ require 'models/ib/underlying'
3
+
4
+ module IB
5
+ class Contract < IB::Model
6
+ include BaseProperties
7
+
8
+ # Fields are Strings unless noted otherwise
9
+ prop :con_id, # int: The unique contract identifier.
10
+ :currency, # Only needed if there is an ambiguity, e.g. when SMART exchange
11
+ # and IBM is being requested (IBM can trade in GBP or USD).
12
+
13
+ :legs_description, # received in OpenOrder for all combos
14
+
15
+ :sec_type, # Security type. Valid values are: SECURITY_TYPES
16
+
17
+ :sec_id, # Unique identifier of the given secIdType.
18
+
19
+ :sec_id_type => :sup, # Security identifier, when querying contract details or
20
+ # when placing orders. Supported identifiers are:
21
+ # - ISIN (Example: Apple: US0378331005)
22
+ # - CUSIP (Example: Apple: 037833100)
23
+ # - SEDOL (6-AN + check digit. Example: BAE: 0263494)
24
+ # - RIC (exchange-independent RIC Root and exchange-
25
+ # identifying suffix. Ex: AAPL.O for Apple on NASDAQ.)
26
+
27
+ :symbol => :s, # This is the symbol of the underlying asset.
28
+
29
+ :local_symbol => :s, # Local exchange symbol of the underlying asset
30
+
31
+ # Future/option contract multiplier (only needed when multiple possibilities exist)
32
+ :multiplier => {:set => :i},
33
+
34
+ :strike => :f, # double: The strike price.
35
+ :expiry => :s, # The expiration date. Use the format YYYYMM or YYYYMMDD
36
+ :exchange => :sup, # The order destination, such as Smart.
37
+ :primary_exchange => :sup, # Non-SMART exchange where the contract trades.
38
+ :include_expired => :bool, # When true, contract details requests and historical
39
+ # data queries can be performed pertaining to expired contracts.
40
+ # Note: Historical data queries on expired contracts are
41
+ # limited to the last year of the contracts life, and are
42
+ # only supported for expired futures contracts.
43
+ # This field can NOT be set to true for orders.
44
+
45
+
46
+ # Specifies a Put or Call. Valid input values are: P, PUT, C, CALL
47
+ :right => {
48
+ :set => proc { |val|
49
+ self[:right] =
50
+ case val.to_s.upcase
51
+ when 'NONE', '', '0', '?'
52
+ ''
53
+ when 'PUT', 'P'
54
+ 'P'
55
+ when 'CALL', 'C'
56
+ 'C'
57
+ else
58
+ val
59
+ end
60
+ },
61
+ :validate => {:format => {:with => /^put$|^call$|^none$/,
62
+ :message => "should be put, call or none"}}
63
+ }
64
+
65
+ attr_accessor :description # NB: local to ib, not part of TWS.
66
+
67
+ ### Associations
68
+
69
+ has_many :orders # Placed for this Contract
70
+
71
+ has_one :contract_detail # Volatile info about this Contract
72
+
73
+ # For Contracts that are part of BAG
74
+ has_one :leg, :class_name => 'ComboLeg', :foreign_key => :leg_contract_id
75
+ has_one :combo, :class_name => 'Contract', :through => :leg
76
+
77
+ # for Combo/BAG Contracts that contain ComboLegs
78
+ has_many :combo_legs, :foreign_key => :combo_id
79
+ has_many :leg_contracts, :class_name => 'Contract', :through => :combo_legs
80
+ alias legs combo_legs
81
+ alias legs= combo_legs=
82
+
83
+ alias combo_legs_description legs_description
84
+ alias combo_legs_description= legs_description=
85
+
86
+ # for Delta-Neutral Combo Contracts
87
+ has_one :underlying
88
+ alias under_comp underlying
89
+ alias under_comp= underlying=
90
+
91
+
92
+ ### Extra validations
93
+ validates_inclusion_of :sec_type, :in => CODES[:sec_type].keys,
94
+ :message => "should be valid security type"
95
+
96
+ validates_format_of :expiry, :with => /^\d{6}$|^\d{8}$|^$/,
97
+ :message => "should be YYYYMM or YYYYMMDD"
98
+
99
+ validates_format_of :primary_exchange, :without => /SMART/,
100
+ :message => "should not be SMART"
101
+
102
+ validates_format_of :sec_id_type, :with => /ISIN|SEDOL|CUSIP|RIC|^$/,
103
+ :message => "should be valid security identifier"
104
+
105
+ validates_numericality_of :multiplier, :strike, :allow_nil => true
106
+
107
+ def default_attributes
108
+ super.merge :con_id => 0,
109
+ :strike => 0.0,
110
+ :right => :none, # Not an option
111
+ :exchange => 'SMART',
112
+ :include_expired => false
113
+ end
114
+
115
+ # This returns an Array of data from the given contract.
116
+ # Different messages serialize contracts differently. Go figure.
117
+ # Note that it does NOT include the combo legs.
118
+ # serialize [:option, :con_id, :include_expired, :sec_id]
119
+ def serialize *fields
120
+ [(fields.include?(:con_id) ? [con_id] : []),
121
+ symbol,
122
+ self[:sec_type],
123
+ (fields.include?(:option) ?
124
+ [expiry,
125
+ strike,
126
+ self[:right],
127
+ multiplier] : []),
128
+ exchange,
129
+ (fields.include?(:primary_exchange) ? [primary_exchange] : []),
130
+ currency,
131
+ local_symbol,
132
+ (fields.include?(:sec_id) ? [sec_id_type, sec_id] : []),
133
+ (fields.include?(:include_expired) ? [include_expired] : []),
134
+ ].flatten
135
+ end
136
+
137
+ def serialize_long *fields
138
+ serialize :option, :primary_exchange, *fields
139
+ end
140
+
141
+ def serialize_short *fields
142
+ serialize :option, *fields
143
+ end
144
+
145
+ # Serialize under_comp parameters: EClientSocket.java, line 471
146
+ def serialize_under_comp *args
147
+ under_comp ? under_comp.serialize : [false]
148
+ end
149
+
150
+ # Defined in Contract, not BAG subclass to keep code DRY
151
+ def serialize_legs *fields
152
+ case
153
+ when !bag?
154
+ []
155
+ when legs.empty?
156
+ [0]
157
+ else
158
+ [legs.size, legs.map { |leg| leg.serialize *fields }].flatten
159
+ end
160
+ end
161
+
162
+ # This produces a string uniquely identifying this contract, in the format used
163
+ # for command line arguments in the IB-Ruby examples. The format is:
164
+ #
165
+ # symbol:sec_type:expiry:strike:right:multiplier:exchange:primary_exchange:currency:local_symbol
166
+ #
167
+ # Fields not needed for a particular security should be left blank
168
+ # (e.g. strike and right are only relevant for options.)
169
+ #
170
+ # For example, to query the British pound futures contract trading on Globex
171
+ # expiring in September, 2008, the string is:
172
+ #
173
+ # GBP:FUT:200809:::62500:GLOBEX::USD:
174
+ def serialize_ib_ruby
175
+ serialize_long.join(":")
176
+ end
177
+
178
+ # Contract comparison
179
+ def == other
180
+ return false unless other.is_a?(self.class)
181
+
182
+ # Different sec_id_type
183
+ return false if sec_id_type && other.sec_id_type && sec_id_type != other.sec_id_type
184
+
185
+ # Different sec_id
186
+ return false if sec_id && other.sec_id && sec_id != other.sec_id
187
+
188
+ # Different symbols
189
+ return false if symbol && other.symbol && symbol != other.symbol
190
+
191
+ # Different currency
192
+ return false if currency && other.currency && currency != other.currency
193
+
194
+ # Same con_id for all Bags, but unknown for new Contracts...
195
+ # 0 or nil con_id matches any
196
+ return false if con_id != 0 && other.con_id != 0 &&
197
+ con_id && other.con_id && con_id != other.con_id
198
+
199
+ # SMART or nil exchange matches any
200
+ return false if exchange != 'SMART' && other.exchange != 'SMART' &&
201
+ exchange && other.exchange && exchange != other.exchange
202
+
203
+ # Comparison for Bonds and Options
204
+ if bond? || option?
205
+ return false if right != other.right || strike != other.strike
206
+ return false if multiplier && other.multiplier &&
207
+ multiplier != other.multiplier
208
+ return false if expiry && expiry[0..5] != other.expiry[0..5]
209
+ return false unless expiry && (expiry[6..7] == other.expiry[6..7] ||
210
+ expiry[6..7].empty? || other.expiry[6..7].empty?)
211
+ end
212
+
213
+ # All else being equal...
214
+ sec_type == other.sec_type
215
+ end
216
+
217
+ def to_s
218
+ "<Contract: " + instance_variables.map do |key|
219
+ value = send(key[1..-1])
220
+ " #{key}=#{value}" unless value.nil? || value == '' || value == 0
221
+ end.compact.join(',') + " >"
222
+ end
223
+
224
+ def to_human
225
+ "<Contract: " +
226
+ [symbol,
227
+ sec_type,
228
+ (expiry == '' ? nil : expiry),
229
+ (right == :none ? nil : right),
230
+ (strike == 0 ? nil : strike),
231
+ exchange,
232
+ currency
233
+ ].compact.join(" ") + ">"
234
+ end
235
+
236
+ def to_short
237
+ "#{symbol}#{expiry}#{strike}#{right}#{exchange}#{currency}"
238
+ end
239
+
240
+ # Testing for type of contract:
241
+
242
+ def bag?
243
+ self[:sec_type] == 'BAG'
244
+ end
245
+
246
+ def bond?
247
+ self[:sec_type] == 'BOND'
248
+ end
249
+
250
+ def stock?
251
+ self[:sec_type] == 'STK'
252
+ end
253
+
254
+ def option?
255
+ self[:sec_type] == 'OPT'
256
+ end
257
+
258
+ end # class Contract
259
+
260
+
261
+ ### Now let's deal with Contract subclasses
262
+
263
+ require 'models/ib/option'
264
+ require 'models/ib/bag'
265
+
266
+ class Contract
267
+ # Specialized Contract subclasses representing different security types
268
+ Subclasses = Hash.new(Contract)
269
+ Subclasses[:bag] = IB::Bag
270
+ Subclasses[:option] = IB::Option
271
+
272
+ # This returns a Contract initialized from the serialize_ib_ruby format string.
273
+ def self.build opts = {}
274
+ subclass = VALUES[:sec_type][opts[:sec_type]] || opts[:sec_type].to_sym
275
+ Contract::Subclasses[subclass].new opts
276
+ end
277
+
278
+ # This returns a Contract initialized from the serialize_ib_ruby format string.
279
+ def self.from_ib_ruby string
280
+ keys = [:symbol, :sec_type, :expiry, :strike, :right, :multiplier,
281
+ :exchange, :primary_exchange, :currency, :local_symbol]
282
+ props = Hash[keys.zip(string.split(":"))]
283
+ props.delete_if { |k, v| v.nil? || v.empty? }
284
+ Contract.build props
285
+ end
286
+ end # class Contract
287
+ end # module IB