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
@@ -0,0 +1,68 @@
1
+ module IB
2
+
3
+ # Additional Contract properties (volatile, therefore extracted)
4
+ class ContractDetail < IB::Model
5
+ include BaseProperties
6
+
7
+ # All fields Strings, unless specified otherwise:
8
+ prop :market_name, # The market name for this contract.
9
+ :trading_class, # The trading class name for this contract.
10
+ :min_tick, # double: The minimum price tick.
11
+ :price_magnifier, # int: Allows execution and strike prices to be
12
+ # reported consistently with market data, historical data and the
13
+ # order price: Z on LIFFE is reported in index points, not GBP.
14
+
15
+ :order_types, # The list of valid order types for this contract.
16
+ :valid_exchanges, # The list of exchanges this contract is traded on.
17
+ :under_con_id, # int: The underlying contract ID.
18
+ :long_name, # Descriptive name of the asset.
19
+ :contract_month, # The contract month of the underlying futures contract.
20
+
21
+ # The industry classification of the underlying/product:
22
+ :industry, # Wide industry. For example, Financial.
23
+ :category, # Industry category. For example, InvestmentSvc.
24
+ :subcategory, # Subcategory. For example, Brokerage.
25
+ [:time_zone, :time_zone_id], # Time zone for the trading hours (e.g. EST)
26
+ :trading_hours, # The trading hours of the product. For example:
27
+ # 20090507:0700-1830,1830-2330;20090508:CLOSED.
28
+ :liquid_hours, # The liquid trading hours of the product. For example,
29
+ # 20090507:0930-1600;20090508:CLOSED.
30
+
31
+ # BOND values:
32
+ :cusip, # The nine-character bond CUSIP or the 12-character SEDOL.
33
+ :ratings, # Credit rating of the issuer. Higher rating is less risky investment.
34
+ # Bond ratings are from Moody's and S&P respectively.
35
+ :desc_append, # Additional descriptive information about the bond.
36
+ :bond_type, # The type of bond, such as "CORP."
37
+ :coupon_type, # The type of bond coupon.
38
+ :coupon, # double: The interest rate used to calculate the amount you
39
+ # will receive in interest payments over the year. default 0
40
+ :maturity, # The date on which the issuer must repay bond face value
41
+ :issue_date, # The date the bond was issued.
42
+ :next_option_date, # only if bond has embedded options.
43
+ :next_option_type, # only if bond has embedded options.
44
+ :notes, # Additional notes, if populated for the bond in IB's database
45
+ :callable => :bool, # Can be called by the issuer under certain conditions.
46
+ :puttable => :bool, # Can be sold back to the issuer under certain conditions
47
+ :convertible => :bool, # Can be converted to stock under certain conditions.
48
+ :next_option_partial => :bool # # only if bond has embedded options.
49
+
50
+ # Extra validations
51
+ validates_format_of :time_zone, :with => /^\w{3}$/, :message => 'should be XXX'
52
+
53
+ belongs_to :contract
54
+ alias summary contract
55
+ alias summary= contract=
56
+
57
+ def default_attributes
58
+ super.merge :coupon => 0.0,
59
+ :under_con_id => 0,
60
+ :min_tick => 0,
61
+ :callable => false,
62
+ :puttable => false,
63
+ :convertible => false,
64
+ :next_option_partial => false
65
+ end
66
+
67
+ end # class ContractDetail
68
+ end # module IB
@@ -0,0 +1,62 @@
1
+ module IB
2
+
3
+ # This is IB Order execution report.
4
+ class Execution < IB::Model
5
+ include BaseProperties
6
+
7
+ belongs_to :order
8
+
9
+ prop :local_id, # int: order id. TWS orders have a fixed order id of 0.
10
+ :client_id, # int: client id. TWS orders have a fixed client id of 0.
11
+ :perm_id, # int: TWS id used to identify orders over TWS sessions
12
+ :exec_id, # String: Unique order execution id over TWS sessions.
13
+ :time, # # TODO: convert into Time object?
14
+ # String: The order execution time.
15
+ :exchange, # String: Exchange that executed the order.
16
+ :order_ref, # int: Same order_ref as in corresponding Order
17
+ :price, # double: The order execution price.
18
+ :average_price, # double: Average price. Used in regular trades, combo
19
+ # trades and legs of the combo.
20
+ [:quantity, :shares], # int: The number of shares filled.
21
+ :cumulative_quantity, # int: Cumulative quantity. Used in regular
22
+ # trades, combo trades and legs of the combo
23
+ :liquidation => :bool, # This position is liquidated last should the need arise.
24
+ [:account_name, :account_number] => :s, # The customer account number.
25
+ [:side, :action] => PROPS[:side] # Was the transaction a buy or a sale: BOT|SLD
26
+
27
+ # Extra validations
28
+ validates_numericality_of :quantity, :cumulative_quantity, :price, :average_price
29
+ validates_numericality_of :local_id, :client_id, :perm_id, :only_integer => true
30
+
31
+ def default_attributes
32
+ super.merge :local_id => 0,
33
+ :client_id => 0,
34
+ :quantity => 0,
35
+ :price => 0,
36
+ :perm_id => 0,
37
+ :liquidation => false
38
+ end
39
+
40
+ # Comparison
41
+ def == other
42
+ perm_id == other.perm_id &&
43
+ local_id == other.local_id && # ((p __LINE__)||true) &&
44
+ client_id == other.client_id &&
45
+ exec_id == other.exec_id &&
46
+ time == other.time &&
47
+ exchange == other.exchange &&
48
+ order_ref == other.order_ref &&
49
+ side == other.side
50
+ # TODO: || compare all attributes!
51
+ end
52
+
53
+ def to_human
54
+ "<Execution: #{time} #{side} #{quantity} at #{price} on #{exchange}, " +
55
+ "cumulative #{cumulative_quantity} at #{average_price}, " +
56
+ "ids #{local_id}/#{perm_id}/#{exec_id}>"
57
+ end
58
+
59
+ alias to_s to_human
60
+
61
+ end # Execution
62
+ end # module IB
@@ -0,0 +1,60 @@
1
+ require 'models/ib/contract'
2
+
3
+ module IB
4
+ class Option < Contract
5
+
6
+ validates_numericality_of :strike, :greater_than => 0
7
+ validates_format_of :sec_type, :with => /^option$/,
8
+ :message => "should be an option"
9
+ validates_format_of :local_symbol, :with => /^\w+\s*\d{6}[pcPC]\d{8}$|^$/,
10
+ :message => "invalid OSI code"
11
+ validates_format_of :right, :with => /^put$|^call$/,
12
+ :message => "should be put or call"
13
+
14
+ # For Options, this is contract's OSI (Option Symbology Initiative) name/code
15
+ alias osi local_symbol
16
+
17
+ def osi= value
18
+ # Normalize to 21 char
19
+ self.local_symbol = value.sub(/ /, ' '*(22-value.size))
20
+ end
21
+
22
+ # Make valid IB Contract definition from OSI (Option Symbology Initiative) code.
23
+ # NB: Simply making a new Contract with *local_symbol* (osi) property set to a
24
+ # valid OSI code works just as well, just do NOT set *expiry*, *right* or
25
+ # *strike* properties in this case.
26
+ # This class method provided as a backup and shows how to analyse OSI codes.
27
+ def self.from_osi osi
28
+
29
+ # Parse contract's OSI (OCC Option Symbology Initiative) code
30
+ args = osi.match(/(\w+)\s?(\d\d)(\d\d)(\d\d)([pcPC])(\d+)/).to_a.drop(1)
31
+ symbol = args.shift
32
+ year = 2000 + args.shift.to_i
33
+ month = args.shift.to_i
34
+ day = args.shift.to_i
35
+ right = args.shift.upcase
36
+ strike = args.shift.to_i/1000.0
37
+
38
+ # Set correct expiry date - IB expiry date differs from OSI if expiry date
39
+ # falls on Saturday (see https://github.com/arvicco/option_mower/issues/4)
40
+ expiry_date = Time.utc(year, month, day)
41
+ expiry_date = Time.utc(year, month, day-1) if expiry_date.wday == 6
42
+
43
+ new :symbol => symbol,
44
+ :exchange => "SMART",
45
+ :expiry => expiry_date.to_ib[2..7], # YYMMDD
46
+ :right => right,
47
+ :strike => strike
48
+ end
49
+
50
+ def default_attributes
51
+ super.merge :sec_type => :option
52
+ #self[:description] ||= osi ? osi : "#{symbol} #{strike} #{right} #{expiry}"
53
+ end
54
+
55
+ def to_human
56
+ "<Option: " + [symbol, expiry, right, strike, exchange, currency].join(" ") + ">"
57
+ end
58
+
59
+ end # class Option
60
+ end # module IB
@@ -0,0 +1,389 @@
1
+ require 'models/ib/order_state'
2
+
3
+ module IB
4
+ class Order < IB::Model
5
+ include BaseProperties
6
+
7
+ # General Notes:
8
+ # 1. Placing Orders by con_id - When you place an order by con_id, you must
9
+ # provide the con_id AND the exchange. If you provide extra fields when placing
10
+ # an order by conid, the order may not work.
11
+
12
+ # 2. Order IDs - Each order you place must have a unique Order ID. Increment
13
+ # your own Order IDs to avoid conflicts between orders placed from your API application.
14
+
15
+ # Main order fields
16
+ prop :local_id, # int: Order id associated with client (volatile).
17
+ :client_id, # int: The id of the client that placed this order.
18
+ :perm_id, # int: TWS permanent id, remains the same over TWS sessions.
19
+ [:quantity, :total_quantity], # int: The order quantity.
20
+
21
+ :order_type, # String: Order type.
22
+ # Limit Risk: MTL / MKT PRT / QUOTE / STP / STP LMT / TRAIL / TRAIL LIMIT / TRAIL LIT / TRAIL MIT
23
+ # Speed of Execution: MKT / MIT / MOC / MOO / PEG MKT / REL
24
+ # Price Improvement: BOX TOP / LOC / LOO / LIT / PEG MID / VWAP
25
+ # Advanced Trading: OCA / VOL / SCALE
26
+ # Other (no abbreviation): Bracket, Auction, Discretionary, Sweep-to-Fill,
27
+ # Price Improvement Auction, Block, Hidden, Iceberg/Reserve, All-or-None, Fill-or-Kill
28
+ # See 'ib/constants.rb' ORDER_TYPES for a complete list of valid values.
29
+
30
+ :limit_price, # double: LIMIT price, used for limit, stop-limit and relative
31
+ # orders. In all other cases specify zero. For relative
32
+ # orders with no limit price, also specify zero.
33
+
34
+ :aux_price, # double: STOP price for stop-limit orders, and the OFFSET amount
35
+ # for relative orders. In all other cases, specify zero.
36
+
37
+ :oca_group, # String: Identifies a member of a one-cancels-all group.
38
+ :oca_type, # int: Tells how to handle remaining orders in an OCA group
39
+ # when one order or part of an order executes. Valid values:
40
+ # - 1 = Cancel all remaining orders with block
41
+ # - 2 = Remaining orders are reduced in size with block
42
+ # - 3 = Remaining orders are reduced in size with no block
43
+ # If you use a value "with block" your order has
44
+ # overfill protection. This means that only one order in
45
+ # the group will be routed at a time to remove the
46
+ # possibility of an overfill.
47
+ :parent_id, # int: The order ID of the parent (original) order, used
48
+ # for bracket (STP) and auto trailing stop (TRAIL) orders.
49
+ :display_size, # int: publicly disclosed order size for Iceberg orders.
50
+
51
+ :trigger_method, # Specifies how Simulated Stop, Stop-Limit and Trailing
52
+ # Stop orders are triggered. Valid values are:
53
+ # 0 - Default, "double bid/ask" for OTC/US options, "last" otherswise.
54
+ # 1 - "double bid/ask" method, stop orders are triggered based on
55
+ # two consecutive bid or ask prices.
56
+ # 2 - "last" method, stops are triggered based on the last price.
57
+ # 3 - double last method.
58
+ # 4 - bid/ask method. For a buy order, a single occurrence of the
59
+ # bid price must be at or above the trigger price. For a sell
60
+ # order, a single occurrence of the ask price must be at or
61
+ # below the trigger price.
62
+ # 7 - last or bid/ask method. For a buy order, a single bid price
63
+ # or the last price must be at or above the trigger price.
64
+ # For a sell order, a single ask price or the last price
65
+ # must be at or below the trigger price.
66
+ # 8 - mid-point method, where the midpoint must be at or above
67
+ # (for a buy) or at or below (for a sell) the trigger price,
68
+ # and the spread between the bid and ask must be less than
69
+ # 0.1% of the midpoint
70
+
71
+ :good_after_time, # Indicates that the trade should be submitted after the
72
+ # time and date set, format YYYYMMDD HH:MM:SS (seconds are optional).
73
+ :good_till_date, # Indicates that the trade should remain working until the
74
+ # time and date set, format YYYYMMDD HH:MM:SS (seconds are optional).
75
+ # You must set the :tif to GTD when using this string.
76
+ # Use an empty String if not applicable.
77
+
78
+ :rule_80a, # Individual = 'I', Agency = 'A', AgentOtherMember = 'W',
79
+ # IndividualPTIA = 'J', AgencyPTIA = 'U', AgentOtherMemberPTIA = 'M',
80
+ # IndividualPT = 'K', AgencyPT = 'Y', AgentOtherMemberPT = 'N'
81
+ :min_quantity, # int: Identifies a minimum quantity order type.
82
+ :percent_offset, # double: percent offset amount for relative (REL)orders only
83
+ :trail_stop_price, # double: for TRAILLIMIT orders only
84
+ # As of client v.56, we receive trailing_percent in openOrder
85
+ :trailing_percent,
86
+
87
+ # Financial advisors only - use an empty String if not applicable.
88
+ :fa_group, :fa_profile, :fa_method, :fa_percentage,
89
+
90
+ # Institutional orders only!
91
+ :origin, # 0=Customer, 1=Firm
92
+ :order_ref, # String: Order reference. Customer defined order ID tag.
93
+ :short_sale_slot, # 1 - you hold the shares,
94
+ # 2 - they will be delivered from elsewhere.
95
+ # Only for Action="SSHORT
96
+ :designated_location, # String: set when slot==2 only
97
+ :exempt_code, # int
98
+
99
+ # Clearing info
100
+ :account, # String: The account. For institutional customers only.
101
+ :settling_firm, # String: Institutional only
102
+ :clearing_account, # String: For IBExecution customers: Specifies the
103
+ # true beneficiary of the order. This value is required
104
+ # for FUT/FOP orders for reporting to the exchange.
105
+ :clearing_intent, # IBExecution customers: "", IB, Away, PTA (post trade allocation).
106
+
107
+ # SMART routing only
108
+ :discretionary_amount, # double: The amount off the limit price
109
+ # allowed for discretionary orders.
110
+ :nbbo_price_cap, # double: Maximum Smart order distance from the NBBO.
111
+
112
+ # BOX or VOL ORDERS ONLY
113
+ :auction_strategy, # For BOX exchange only. Valid values:
114
+ # 1=AUCTION_MATCH, 2=AUCTION_IMPROVEMENT, 3=AUCTION_TRANSPARENT
115
+ :starting_price, # double: Starting price. Valid on BOX orders only.
116
+ :stock_ref_price, # double: The stock reference price, used for VOL
117
+ # orders to compute the limit price sent to an exchange (whether or not
118
+ # Continuous Update is selected), and for price range monitoring.
119
+ :delta, # double: Stock delta. Valid on BOX orders only.
120
+
121
+ # Pegged to stock or VOL orders. For price improvement option orders
122
+ # on BOX and VOL orders with dynamic management:
123
+ :stock_range_lower, # double: The lower value for the acceptable
124
+ # underlying stock price range.
125
+ :stock_range_upper, # double The upper value for the acceptable
126
+ # underlying stock price range.
127
+
128
+ # VOLATILITY ORDERS ONLY:
129
+ # http://www.interactivebrokers.com/en/general/education/pdfnotes/PDF-VolTrader.php
130
+ :volatility, # double: What the price is, computed via TWSs Options
131
+ # Analytics. For VOL orders, the limit price sent to an
132
+ # exchange is not editable, as it is the output of a
133
+ # function. Volatility is expressed as a percentage.
134
+ :volatility_type, # int: How the volatility is calculated: 1=daily, 2=annual
135
+ :reference_price_type, # int: For dynamic management of volatility orders:
136
+ # - 1 = Average of National Best Bid or Ask,
137
+ # - 2 = National Best Bid when buying a call or selling a put;
138
+ # and National Best Ask when selling a call or buying a put.
139
+ :continuous_update, # int: Used for dynamic management of volatility orders.
140
+ # Determines whether TWS is supposed to update the order price as the underlying
141
+ # moves. If selected, the limit price sent to an exchange is modified by TWS
142
+ # if the computed price of the option changes enough to warrant doing so. This
143
+ # is helpful in keeping the limit price up to date as the underlying price changes.
144
+ :delta_neutral_order_type, # String: Enter an order type to instruct TWS
145
+ # to submit a delta neutral trade on full or partial execution of the
146
+ # VOL order. For no hedge delta order to be sent, specify NONE.
147
+ # Valid values - LMT, MKT, MTL, REL, MOC
148
+ :delta_neutral_aux_price, # double: Use this field to enter a value if
149
+ # the value in the deltaNeutralOrderType field is an order
150
+ # type that requires an Aux price, such as a REL order.
151
+
152
+ # As of client v.52, we also receive delta... params in openOrder
153
+ :delta_neutral_con_id,
154
+ :delta_neutral_settling_firm,
155
+ :delta_neutral_clearing_account,
156
+ :delta_neutral_clearing_intent,
157
+
158
+ # HEDGE ORDERS ONLY:
159
+ # As of client v.49/50, we can now add hedge orders using the API.
160
+ # Hedge orders are child orders that take additional fields. There are four
161
+ # types of hedging orders supported by the API: Delta, Beta, FX, Pair.
162
+ # All hedge orders must have a parent order submitted first. The hedge order
163
+ # should set its :parent_id. If the hedgeType is Beta, the beta sent in the
164
+ # hedgeParm can be zero, which means it is not used. Delta is only valid
165
+ # if the parent order is an option and the child order is a stock.
166
+
167
+ :hedge_type, # String: D = Delta, B = Beta, F = FX or P = Pair
168
+ :hedge_param, # String; value depends on the hedgeType; sent from the API
169
+ # only if hedge_type is NOT null. It is required for Pair hedge order,
170
+ # optional for Beta hedge orders, and ignored for Delta and FX hedge orders.
171
+
172
+ # COMBO ORDERS ONLY:
173
+ :basis_points, # double: EFP orders only
174
+ :basis_points_type, # double: EFP orders only
175
+
176
+ # ALGO ORDERS ONLY:
177
+ :algo_strategy, # String
178
+ :algo_params, # public Vector<TagValue> m_algoParams; ?!
179
+
180
+ # SCALE ORDERS ONLY:
181
+ :scale_init_level_size, # int: Size of the first (initial) order component.
182
+ :scale_subs_level_size, # int: Order size of the subsequent scale order
183
+ # components. Used in conjunction with scaleInitLevelSize().
184
+ :scale_price_increment, # double: Price increment between scale components.
185
+ # This field is required for Scale orders.
186
+
187
+ # As of client v.54, we can receive additional scale order fields:
188
+ :scale_price_adjust_value,
189
+ :scale_price_adjust_interval,
190
+ :scale_profit_offset,
191
+ :scale_init_position,
192
+ :scale_init_fill_qty,
193
+ :scale_auto_reset => :bool,
194
+ :scale_random_percent => :bool
195
+
196
+ # Properties with complex processing logics
197
+ prop :tif, # String: Time in Force (time to market): DAY/GAT/GTD/GTC/IOC
198
+ :what_if => :bool, # Only return pre-trade commissions and margin info, do not place
199
+ :not_held => :bool, # Not Held
200
+ :outside_rth => :bool, # Order may trigger or fill outside of regular hours. (WAS: ignore_rth)
201
+ :hidden => :bool, # Order will not be visible in market depth. ISLAND only.
202
+ :transmit => :bool, # If false, order will be created but not transmitted.
203
+ :block_order => :bool, # This is an ISE Block order.
204
+ :sweep_to_fill => :bool, # This is a Sweep-to-Fill order.
205
+ :override_percentage_constraints => :bool,
206
+ # TWS Presets page constraints ensure that your price and size order values
207
+ # are reasonable. Orders sent from the API are also validated against these
208
+ # safety constraints, unless this parameter is set to True.
209
+ :all_or_none => :bool, # AON
210
+ :etrade_only => :bool, # Trade with electronic quotes.
211
+ :firm_quote_only => :bool, # Trade with firm quotes.
212
+ :opt_out_smart_routing => :bool, # Australian exchange only, default false
213
+ :open_close => PROPS[:open_close], # Originally String: O=Open, C=Close ()
214
+ # for ComboLeg compatibility: SAME = 0; OPEN = 1; CLOSE = 2; UNKNOWN = 3;
215
+ [:side, :action] => PROPS[:side] # String: Action/side: BUY/SELL/SSHORT/SSHORTX
216
+
217
+ prop :placed_at,
218
+ :modified_at,
219
+ :leg_prices,
220
+ :algo_params,
221
+ :combo_params
222
+
223
+ alias order_combo_legs leg_prices
224
+ alias smart_combo_routing_params combo_params
225
+
226
+ serialize :leg_prices
227
+ serialize :algo_params, Hash
228
+ serialize :combo_params
229
+
230
+ # Order is always placed for a contract. Here, we explicitly set this link.
231
+ belongs_to :contract
232
+
233
+ # Order has a collection of Executions if it was filled
234
+ has_many :executions
235
+
236
+ # Order has a collection of OrderStates, last one is always current
237
+ has_many :order_states
238
+
239
+ def order_state
240
+ order_states.last
241
+ end
242
+
243
+ def order_state= state
244
+ self.order_states.push case state
245
+ when IB::OrderState
246
+ state
247
+ when Symbol, String
248
+ IB::OrderState.new :status => state
249
+ end
250
+ end
251
+
252
+ # Some properties received from IB are separated into OrderState object,
253
+ # but they are still readable as Order properties through delegation:
254
+ # Properties arriving via OpenOrder message:
255
+ [:commission, # double: Shows the commission amount on the order.
256
+ :commission_currency, # String: Shows the currency of the commission.
257
+ :min_commission, # The possible min range of the actual order commission.
258
+ :max_commission, # The possible max range of the actual order commission.
259
+ :warning_text, # String: Displays a warning message if warranted.
260
+ :init_margin, # Float: The impact the order would have on your initial margin.
261
+ :maint_margin, # Float: The impact the order would have on your maintenance margin.
262
+ :equity_with_loan, # Float: The impact the order would have on your equity
263
+ :status, # String: Displays the order status. See OrderState for values
264
+ # Properties arriving via OrderStatus message:
265
+ :filled, # int
266
+ :remaining, # int
267
+ :price, # double
268
+ :last_fill_price, # double
269
+ :average_price, # double
270
+ :average_fill_price, # double
271
+ :why_held, # String: comma-separated list of reasons for order to be held.
272
+ # Testing Order state:
273
+ :new?,
274
+ :submitted?,
275
+ :pending?,
276
+ :active?,
277
+ :inactive?,
278
+ :complete_fill?
279
+ ].each { |property| define_method(property) { order_state.send(property) } }
280
+
281
+ # Order is not valid without correct :local_id
282
+ validates_numericality_of :local_id, :perm_id, :client_id, :parent_id,
283
+ :quantity, :min_quantity, :display_size,
284
+ :only_integer => true, :allow_nil => true
285
+
286
+ validates_numericality_of :limit_price, :aux_price, :allow_nil => true
287
+
288
+
289
+ def default_attributes
290
+ super.merge :aux_price => 0.0,
291
+ :discretionary_amount => 0.0,
292
+ :parent_id => 0,
293
+ :tif => :day,
294
+ :order_type => :limit,
295
+ :open_close => :open,
296
+ :origin => :customer,
297
+ :short_sale_slot => :default,
298
+ :trigger_method => :default,
299
+ :oca_type => :none,
300
+ :auction_strategy => :none,
301
+ :designated_location => '',
302
+ :exempt_code => -1,
303
+ :display_size => 0,
304
+ :continuous_update => 0,
305
+ :delta_neutral_con_id => 0,
306
+ :algo_strategy => '',
307
+ :transmit => true,
308
+ :what_if => false,
309
+ :leg_prices => [],
310
+ :algo_params => HashWithIndifferentAccess.new, #{},
311
+ :combo_params => HashWithIndifferentAccess.new, #{},
312
+ :order_state => IB::OrderState.new(:status => 'New',
313
+ :filled => 0,
314
+ :remaining => 0,
315
+ :price => 0,
316
+ :average_price => 0)
317
+ end
318
+
319
+ def serialize_algo
320
+ if algo_strategy.nil? || algo_strategy.empty?
321
+ ''
322
+ else
323
+ [algo_strategy,
324
+ algo_params.size,
325
+ algo_params.to_a]
326
+ end
327
+ end
328
+
329
+ # Placement
330
+ def place contract, connection
331
+ error "Unable to place order, next_local_id not known" unless connection.next_local_id
332
+ self.client_id = connection.server[:client_id]
333
+ self.local_id = connection.next_local_id
334
+ connection.next_local_id += 1
335
+ self.placed_at = Time.now
336
+ modify contract, connection, self.placed_at
337
+ end
338
+
339
+ # Modify Order (convenience wrapper for send_message :PlaceOrder). Returns local_id.
340
+ def modify contract, connection, time=Time.now
341
+ self.modified_at = time
342
+ connection.send_message :PlaceOrder,
343
+ :order => self,
344
+ :contract => contract,
345
+ :local_id => local_id
346
+ local_id
347
+ end
348
+
349
+ # Order comparison
350
+ def == other
351
+ perm_id && other.perm_id && perm_id == other.perm_id ||
352
+ local_id == other.local_id && # ((p __LINE__)||true) &&
353
+ (client_id == other.client_id || client_id == 0 || other.client_id == 0) &&
354
+ parent_id == other.parent_id &&
355
+ tif == other.tif &&
356
+ action == other.action &&
357
+ order_type == other.order_type &&
358
+ quantity == other.quantity &&
359
+ (limit_price == other.limit_price || # TODO Floats should be Decimals!
360
+ (limit_price - other.limit_price).abs < 0.00001) &&
361
+ aux_price == other.aux_price &&
362
+ origin == other.origin &&
363
+ designated_location == other.designated_location &&
364
+ exempt_code == other.exempt_code &&
365
+ what_if == other.what_if &&
366
+ algo_strategy == other.algo_strategy &&
367
+ algo_params == other.algo_params
368
+
369
+ # TODO: || compare all attributes!
370
+ end
371
+
372
+ def to_s #human
373
+ "<Order:" + instance_variables.map do |key|
374
+ value = instance_variable_get(key)
375
+ " #{key}=#{value}" unless value.nil? || value == '' || value == 0
376
+ end.compact.join(',') + " >"
377
+ end
378
+
379
+ def to_human
380
+ "<Order: " + ((order_ref && order_ref != '') ? "#{order_ref} " : '') +
381
+ "#{self[:order_type]} #{self[:tif]} #{side} #{quantity} " +
382
+ "#{status} " + (limit_price ? "#{limit_price} " : '') +
383
+ ((aux_price && aux_price != 0) ? "/#{aux_price}" : '') +
384
+ "##{local_id}/#{perm_id} from #{client_id}" +
385
+ (account ? "/#{account}" : '') +
386
+ (commission ? " fee #{commission}" : '') + ">"
387
+ end
388
+ end # class Order
389
+ end # module IB