ib-api 10.33.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.
Files changed (161) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +52 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CLAUDE.md +131 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +17 -0
  8. data/Gemfile.lock +120 -0
  9. data/Guardfile +24 -0
  10. data/LICENSE +674 -0
  11. data/LLM_GUIDE.md +388 -0
  12. data/README.md +114 -0
  13. data/Rakefile +11 -0
  14. data/VERSION +1 -0
  15. data/api.gemspec +50 -0
  16. data/bin/console +96 -0
  17. data/bin/console.yml +3 -0
  18. data/bin/setup +8 -0
  19. data/bin/simple +91 -0
  20. data/changelog.md +32 -0
  21. data/conditions/ib/execution_condition.rb +31 -0
  22. data/conditions/ib/margin_condition.rb +28 -0
  23. data/conditions/ib/order_condition.rb +29 -0
  24. data/conditions/ib/percent_change_condition.rb +34 -0
  25. data/conditions/ib/price_condition.rb +44 -0
  26. data/conditions/ib/time_condition.rb +42 -0
  27. data/conditions/ib/volume_condition.rb +36 -0
  28. data/lib/class_extensions.rb +167 -0
  29. data/lib/ib/base.rb +109 -0
  30. data/lib/ib/base_properties.rb +178 -0
  31. data/lib/ib/connection.rb +573 -0
  32. data/lib/ib/constants.rb +402 -0
  33. data/lib/ib/contract.rb +30 -0
  34. data/lib/ib/errors.rb +52 -0
  35. data/lib/ib/messages/abstract_message.rb +68 -0
  36. data/lib/ib/messages/incoming/abstract_message.rb +116 -0
  37. data/lib/ib/messages/incoming/abstract_tick.rb +25 -0
  38. data/lib/ib/messages/incoming/account_message.rb +26 -0
  39. data/lib/ib/messages/incoming/alert.rb +34 -0
  40. data/lib/ib/messages/incoming/contract_data.rb +105 -0
  41. data/lib/ib/messages/incoming/contract_message.rb +13 -0
  42. data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
  43. data/lib/ib/messages/incoming/execution_data.rb +50 -0
  44. data/lib/ib/messages/incoming/histogram_data.rb +30 -0
  45. data/lib/ib/messages/incoming/historical_data.rb +65 -0
  46. data/lib/ib/messages/incoming/historical_data_update.rb +50 -0
  47. data/lib/ib/messages/incoming/managed_accounts.rb +21 -0
  48. data/lib/ib/messages/incoming/market_depth.rb +34 -0
  49. data/lib/ib/messages/incoming/market_depth_l2.rb +15 -0
  50. data/lib/ib/messages/incoming/next_valid_id.rb +19 -0
  51. data/lib/ib/messages/incoming/open_order.rb +290 -0
  52. data/lib/ib/messages/incoming/order_status.rb +85 -0
  53. data/lib/ib/messages/incoming/portfolio_value.rb +47 -0
  54. data/lib/ib/messages/incoming/position_data.rb +21 -0
  55. data/lib/ib/messages/incoming/positions_multi.rb +15 -0
  56. data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
  57. data/lib/ib/messages/incoming/receive_fa.rb +30 -0
  58. data/lib/ib/messages/incoming/scanner_data.rb +54 -0
  59. data/lib/ib/messages/incoming/tick_by_tick.rb +77 -0
  60. data/lib/ib/messages/incoming/tick_efp.rb +18 -0
  61. data/lib/ib/messages/incoming/tick_generic.rb +12 -0
  62. data/lib/ib/messages/incoming/tick_option.rb +60 -0
  63. data/lib/ib/messages/incoming/tick_price.rb +60 -0
  64. data/lib/ib/messages/incoming/tick_size.rb +55 -0
  65. data/lib/ib/messages/incoming/tick_string.rb +13 -0
  66. data/lib/ib/messages/incoming.rb +292 -0
  67. data/lib/ib/messages/outgoing/abstract_message.rb +84 -0
  68. data/lib/ib/messages/outgoing/bar_request_message.rb +247 -0
  69. data/lib/ib/messages/outgoing/new-place-order.rb +193 -0
  70. data/lib/ib/messages/outgoing/old-place-order.rb +147 -0
  71. data/lib/ib/messages/outgoing/place_order.rb +149 -0
  72. data/lib/ib/messages/outgoing/request_account_summary.rb +79 -0
  73. data/lib/ib/messages/outgoing/request_historical_data.rb +182 -0
  74. data/lib/ib/messages/outgoing/request_market_data.rb +102 -0
  75. data/lib/ib/messages/outgoing/request_market_depth.rb +57 -0
  76. data/lib/ib/messages/outgoing/request_real_time_bars.rb +48 -0
  77. data/lib/ib/messages/outgoing/request_scanner_subscription.rb +73 -0
  78. data/lib/ib/messages/outgoing/request_tick_by_tick_data.rb +21 -0
  79. data/lib/ib/messages/outgoing.rb +410 -0
  80. data/lib/ib/messages.rb +139 -0
  81. data/lib/ib/order_condition.rb +26 -0
  82. data/lib/ib/plugins.rb +27 -0
  83. data/lib/ib/prepare_data.rb +61 -0
  84. data/lib/ib/raw_message_parser.rb +99 -0
  85. data/lib/ib/socket.rb +83 -0
  86. data/lib/ib/support.rb +236 -0
  87. data/lib/ib/version.rb +6 -0
  88. data/lib/ib-api.rb +44 -0
  89. data/lib/server_versions.rb +145 -0
  90. data/lib/support/array_function.rb +28 -0
  91. data/lib/support/logging.rb +45 -0
  92. data/models/ib/account.rb +72 -0
  93. data/models/ib/account_value.rb +33 -0
  94. data/models/ib/bag.rb +55 -0
  95. data/models/ib/bar.rb +31 -0
  96. data/models/ib/combo_leg.rb +127 -0
  97. data/models/ib/contract.rb +411 -0
  98. data/models/ib/contract_detail.rb +118 -0
  99. data/models/ib/execution.rb +67 -0
  100. data/models/ib/forex.rb +12 -0
  101. data/models/ib/future.rb +64 -0
  102. data/models/ib/index.rb +14 -0
  103. data/models/ib/option.rb +149 -0
  104. data/models/ib/option_detail.rb +84 -0
  105. data/models/ib/order.rb +720 -0
  106. data/models/ib/order_state.rb +155 -0
  107. data/models/ib/portfolio_value.rb +86 -0
  108. data/models/ib/spread.rb +176 -0
  109. data/models/ib/stock.rb +25 -0
  110. data/models/ib/underlying.rb +32 -0
  111. data/plugins/ib/advanced-account.rb +442 -0
  112. data/plugins/ib/alerts/base-alert.rb +125 -0
  113. data/plugins/ib/alerts/gateway-alerts.rb +15 -0
  114. data/plugins/ib/alerts/order-alerts.rb +73 -0
  115. data/plugins/ib/auto-adjust.rb +0 -0
  116. data/plugins/ib/connection-tools.rb +122 -0
  117. data/plugins/ib/eod.rb +326 -0
  118. data/plugins/ib/greeks.rb +102 -0
  119. data/plugins/ib/managed-accounts.rb +274 -0
  120. data/plugins/ib/market-price.rb +150 -0
  121. data/plugins/ib/option-chain.rb +167 -0
  122. data/plugins/ib/order-flow.rb +157 -0
  123. data/plugins/ib/order-prototypes/abstract.rb +67 -0
  124. data/plugins/ib/order-prototypes/adaptive.rb +40 -0
  125. data/plugins/ib/order-prototypes/all-in-one.rb +46 -0
  126. data/plugins/ib/order-prototypes/combo.rb +46 -0
  127. data/plugins/ib/order-prototypes/forex.rb +40 -0
  128. data/plugins/ib/order-prototypes/limit.rb +193 -0
  129. data/plugins/ib/order-prototypes/market.rb +116 -0
  130. data/plugins/ib/order-prototypes/pegged.rb +169 -0
  131. data/plugins/ib/order-prototypes/premarket.rb +31 -0
  132. data/plugins/ib/order-prototypes/stop.rb +202 -0
  133. data/plugins/ib/order-prototypes/volatility.rb +39 -0
  134. data/plugins/ib/order-prototypes.rb +118 -0
  135. data/plugins/ib/probability-of-expiring.rb +109 -0
  136. data/plugins/ib/process-orders.rb +155 -0
  137. data/plugins/ib/roll.rb +86 -0
  138. data/plugins/ib/spread-prototypes/butterfly.rb +77 -0
  139. data/plugins/ib/spread-prototypes/calendar.rb +97 -0
  140. data/plugins/ib/spread-prototypes/stock-spread.rb +56 -0
  141. data/plugins/ib/spread-prototypes/straddle.rb +70 -0
  142. data/plugins/ib/spread-prototypes/strangle.rb +93 -0
  143. data/plugins/ib/spread-prototypes/vertical.rb +83 -0
  144. data/plugins/ib/spread-prototypes.rb +70 -0
  145. data/plugins/ib/symbols/abstract.rb +136 -0
  146. data/plugins/ib/symbols/bonds.rb +28 -0
  147. data/plugins/ib/symbols/cfd.rb +19 -0
  148. data/plugins/ib/symbols/combo.rb +46 -0
  149. data/plugins/ib/symbols/commodity.rb +17 -0
  150. data/plugins/ib/symbols/forex.rb +41 -0
  151. data/plugins/ib/symbols/futures.rb +127 -0
  152. data/plugins/ib/symbols/index.rb +43 -0
  153. data/plugins/ib/symbols/options.rb +99 -0
  154. data/plugins/ib/symbols/stocks.rb +44 -0
  155. data/plugins/ib/symbols/version.rb +5 -0
  156. data/plugins/ib/symbols.rb +118 -0
  157. data/plugins/ib/verify.rb +226 -0
  158. data/symbols/w20.yml +210 -0
  159. data/t.txt +20 -0
  160. data/update.md +71 -0
  161. metadata +327 -0
@@ -0,0 +1,155 @@
1
+ module IB
2
+
3
+ # OrderState represents dynamic (changeable) info about a single Order,
4
+ # isolating these changes and making Order essentially immutable
5
+ class OrderState < IB::Base
6
+ include BaseProperties
7
+
8
+ #p column_names
9
+ belongs_to :order
10
+
11
+ # Properties arriving via OpenOrder message
12
+ prop :init_margin_after, # Float: The impact the order would have on your initial margin.
13
+ :maint_margin_after, # Float: The impact the order would have on your maintenance margin.
14
+ :equity_with_loan_after, # Float: The impact the order would have on your equity
15
+ :init_margin_before, :maint_margin_before, :equity_with_loan_before,
16
+ :init_margin_change, :maint_margin_change, :equity_with_loan_change,
17
+ :commission, # double: Shows the commission amount on the order.
18
+ :min_commission, # The possible min range of the actual order commission.
19
+ :max_commission, # The possible max range of the actual order commission.
20
+
21
+ :commission_currency, # String: Shows the currency of the commission.
22
+ :warning_text, # String: Displays a warning message if warranted.
23
+
24
+ :market_cap_price # messages#incomming#orderstae#vers. 11
25
+
26
+ # Properties arriving via OrderStatus message:
27
+ prop :filled, # int
28
+ :remaining, # int
29
+ [:price, :last_fill_price,], # double
30
+ [:average_price, :average_fill_price], # double
31
+ :why_held # String: comma-separated list of reasons for order to be held.
32
+
33
+ # Properties arriving in both messages:
34
+ prop :local_id, # int: Order id associated with client (volatile).
35
+ :perm_id, # int: TWS permanent id, remains the same over TWS sessions.
36
+ :client_id, # int: The id of the client that placed this order.
37
+ :parent_id, # int: The order ID of the parent (original) order, used
38
+ :status => :s # String: one of
39
+ # ApiCancelled, PreSubmitted, PendingCancel, Cancelled, Submitted, Filled,
40
+ # Inactive, PendingSubmit, Unknown, ApiPending,
41
+ #
42
+ # Displays the order status. Possible values include:
43
+ # - PendingSubmit - indicates that you have transmitted the order, but
44
+ # have not yet received confirmation that it has been accepted by the
45
+ # order destination. NOTE: This order status is NOT sent back by TWS
46
+ # and should be explicitly set by YOU when an order is submitted.
47
+ # - PendingCancel - indicates that you have sent a request to cancel
48
+ # the order but have not yet received cancel confirmation from the
49
+ # order destination. At this point, your order cancel is not confirmed.
50
+ # You may still receive an execution while your cancellation request
51
+ # is pending. NOTE: This order status is not sent back by TWS and
52
+ # should be explicitly set by YOU when an order is canceled.
53
+ # - PreSubmitted - indicates that a simulated order type has been
54
+ # accepted by the IB system and that this order has yet to be elected.
55
+ # The order is held in the IB system until the election criteria are
56
+ # met. At that time the order is transmitted to the order destination
57
+ # as specified.
58
+ # - Submitted - indicates that your order has been accepted at the order
59
+ # destination and is working.
60
+ # - Cancelled - indicates that the balance of your order has been
61
+ # confirmed canceled by the IB system. This could occur unexpectedly
62
+ # when IB or the destination has rejected your order.
63
+ # - ApiCancelled - canceled via API
64
+ # - Filled - indicates that the order has been completely filled.
65
+ # - Inactive - indicates that the order has been accepted by the system
66
+ # (simulated orders) or an exchange (native orders) but that currently
67
+ # the order is inactive due to system, exchange or other issues.
68
+ #
69
+
70
+ validates_format_of :status, :without => /\A\z/, :message => 'must not be empty'
71
+ validates_numericality_of :price, :average_price, :allow_nil => true
72
+ validates_numericality_of :local_id, :perm_id, :client_id, :parent_id, :filled,
73
+ :remaining, :only_integer => true, :allow_nil => true
74
+
75
+ def self.valid_status? the_message
76
+ valid_stati = %w( ApiCancelled PreSubmitted PendingCancel Cancelled Submitted Filled
77
+ Inactive PendingSubmit Unknown ApiPending)
78
+ valid_stati.include?( the_message )
79
+ end
80
+
81
+ ## Testing Order state:
82
+
83
+ def new?
84
+ status.empty? || status == 'New'
85
+ end
86
+
87
+ # Order is in a valid, working state on TWS side
88
+ def submitted?
89
+ status =~ /Submit/
90
+ end
91
+
92
+ # Order is in a valid, working state on TWS side
93
+ def pending?
94
+ submitted? || status =~ /Pending/
95
+ end
96
+
97
+ # Order is in invalid state
98
+ def inactive?
99
+ new? || pending? || status =~ /Cancel/
100
+ end
101
+
102
+ def active?
103
+ !inactive? # status == 'Inactive'
104
+ end
105
+
106
+ def complete_fill?
107
+ status == 'Filled' && remaining == 0 # filled >= total_quantity # Manually corrected
108
+ end
109
+
110
+ # Comparison
111
+ def == other
112
+
113
+ super(other) ||
114
+ other.is_a?(self.class) &&
115
+ status == other.status &&
116
+ local_id == other.local_id &&
117
+ perm_id == other.perm_id &&
118
+ client_id == other.client_id &&
119
+ filled == other.filled &&
120
+ remaining == other.remaining &&
121
+ last_fill_price == other.last_fill_price &&
122
+ init_margin_after == other.init_margin_after &&
123
+ maint_margin_after == other.maint_margin_after &&
124
+ equity_with_loan_after == other.equity_with_loan_after &&
125
+ why_held == other.why_held &&
126
+ warning_text == other.warning_text &&
127
+ commission == other.commission
128
+ end
129
+
130
+ def to_human
131
+ "<OrderState: #{status} ##{local_id}/#{perm_id} from #{client_id}" +
132
+ (filled ? " filled #{filled}/#{remaining}" : '') +
133
+ (last_fill_price ? " at #{last_fill_price}/#{average_fill_price}" : '') +
134
+ (init_margin_after ? " margin #{init_margin_after}/#{maint_margin_after}" : '') +
135
+ (equity_with_loan_after ? " equity #{equity_with_loan_after}" : '') +
136
+ (commission && commission > 0 ? " fee #{commission}" : "") +
137
+ (why_held ? " why_held #{why_held}" : '') +
138
+ ((warning_text && warning_text != '') ? " warning #{warning_text}" : '') + ">"
139
+ end
140
+
141
+ alias to_s to_human
142
+ =begin
143
+ If an Order is submitted with the :what_if-Flag set, commission and margin are returned
144
+ via the order_state-Object.
145
+ =end
146
+ def forcast
147
+ { :init_margin => init_margin_after,
148
+ :maint_margin => maint_margin_after,
149
+ :equity_with_loan => equity_with_loan_after ,
150
+ :commission => commission,
151
+ :commission_currency=> commission_currency,
152
+ :warning => warning_text }
153
+ end
154
+ end # class Order
155
+ end # module IB
@@ -0,0 +1,86 @@
1
+ module IB
2
+ class PortfolioValue < IB::Base
3
+ include BaseProperties
4
+ # belongs_to :currency
5
+ belongs_to :account
6
+ belongs_to :contract
7
+
8
+ # scope :single, ->(key) { where :schluessel => key } rescue nil
9
+
10
+ prop :position,
11
+ :market_price,
12
+ :market_value,
13
+ :average_cost,
14
+ :unrealized_pnl,
15
+ :realized_pnl
16
+
17
+
18
+ # Order comparison
19
+ def == other
20
+ super(other) ||
21
+ other.is_a?(self.class) &&
22
+ market_price == other.market_price &&
23
+ average_cost == other.average_cost &&
24
+ position == other.position &&
25
+ unrealized_pnl == other.unrealized_pnl &&
26
+ realized_pnl == other.realized_pnl &&
27
+ contract == other.contract
28
+ end
29
+ def to_human
30
+ the_account = if account.present?
31
+ if account.is_a?(String)
32
+ account + " "
33
+ else
34
+ account.account+" "
35
+ end
36
+ else
37
+ ""
38
+ end
39
+
40
+ "<PortfolioValue: "+
41
+ the_account +
42
+ "Pos=#{ position.to_i } @ #{market_price.to_f.round(3)};" +
43
+ "Value=#{market_value.to_f.round(2)};PNL=" +
44
+ ( unrealized_pnl.to_i.zero? ? "": "#{unrealized_pnl} unrealized;") +
45
+ ( realized_pnl.to_i.zero? ? "" : "#{realized_pnl} realized;>" ) +
46
+ contract.to_human
47
+ end
48
+ alias to_s to_human
49
+
50
+ def table_header
51
+ if block_given?
52
+ [ '' , yield , 'pos', 'entry', 'market', 'value', 'unrealized', 'realized' ]
53
+ else
54
+ [ '' , '', 'pos', 'entry', 'market', 'value', 'unrealized', 'realized' ]
55
+ end
56
+ end
57
+
58
+ def table_row
59
+ outprice= ->( item ) { { value: item.nil? ? "--" : item , alignment: :right } }
60
+
61
+ the_account = if account.present?
62
+ if account.is_a?(String)
63
+ account + " "
64
+ else
65
+ account.account+" "
66
+ end
67
+ else
68
+ ""
69
+ end
70
+
71
+ entry = average_cost.to_f / (contract.multiplier.to_i.zero? ? 1 : contract.multiplier.to_i)
72
+
73
+ [ the_account,
74
+ contract.to_human[1..-2],
75
+ outprice[position.to_i],
76
+ outprice[entry.to_f.round(3)],
77
+ outprice[market_price.to_f.round(3)],
78
+ outprice[market_value.to_f.round(2)],
79
+ unrealized_pnl.to_i.zero? ? "": outprice[unrealized_pnl],
80
+ realized_pnl.to_i.zero? ? "" : outprice[realized_pnl]
81
+ ]
82
+ end
83
+
84
+
85
+ end # class
86
+ end # module
@@ -0,0 +1,176 @@
1
+ module IB
2
+ class Spread < Bag
3
+ has_many :legs
4
+
5
+ using IB::Support
6
+
7
+ =begin
8
+ Parameters: front: YYYMM(DD)
9
+ back: {n}w, {n}d or YYYYMM(DD)
10
+
11
+ Adds (or substracts) relative (back) measures to the front month, just passes absolute YYYYMM(DD) value
12
+
13
+ front: 201809 back: 2m (-1m) --> 201811 (201808)
14
+ front: 20180908 back: 1w (-1w) --> 20180918 (20180902)
15
+ =end
16
+
17
+ def self.transform_distance front, back
18
+ # Check Format of back: 201809 --> > 200.000
19
+ # 20180989 ---> 20.000.000
20
+ start_date = front.to_i < 20000000 ? Date.strptime(front.to_s,"%Y%m") : Date.strptime(front.to_s,"%Y%m%d")
21
+ nb = if back.to_i > 200000
22
+ back.to_i
23
+ elsif back[-1] == "w" && front.to_i > 20000000
24
+ start_date + (back.to_i * 7) + 1 # +1 to compensate for friday's bank-holiday, target has to be verified through next_expiry
25
+ elsif back[-1] == "m" && front.to_i > 200000
26
+ start_date >> back.to_i
27
+ else
28
+ error "Wrong date #{back} required format YYYMM, YYYYMMDD ord {n}w or {n}m"
29
+ end
30
+ if nb.is_a?(Date)
31
+ if back[-1]=='w'
32
+ nb.strftime("%Y%m%d")
33
+ else
34
+ nb.strftime("%Y%m")
35
+ end
36
+ else
37
+ nb
38
+ end
39
+ end # def
40
+
41
+ def to_human
42
+ self.description
43
+ end
44
+
45
+ def calculate_spread_value( array_of_portfolio_values )
46
+ array_of_portfolio_values.map{|x| x.send yield }.sum if block_given?
47
+ end
48
+
49
+ def fake_portfolio_position( array_of_portfolio_values )
50
+ calculate_spread_value= ->( a_o_p_v, attribute ) do
51
+ a_o_p_v.map{|x| x.send attribute }.sum
52
+ end
53
+ ar=array_of_portfolio_values
54
+ IB::PortfolioValue.new contract: self,
55
+ average_cost: calculate_spread_value[ar, :average_cost],
56
+ market_price: calculate_spread_value[ar, :market_price],
57
+ market_value: calculate_spread_value[ar, :market_value],
58
+ unrealized_pnl: calculate_spread_value[ar, :unrealized_pnl],
59
+ realized_pnl: calculate_spread_value[ar, :realized_pnl],
60
+ position: 0
61
+
62
+ end
63
+
64
+
65
+ # adds a leg to any spread
66
+ #
67
+ # Parameter:
68
+ # contract: Will be verified. Contract.essential is added to legs-array
69
+ # action: :buy or :sell
70
+ # weight:
71
+ # ratio:
72
+ #
73
+ # Default: action: :buy, weight: 1
74
+
75
+ def add_leg contract, **leg_params
76
+ error "need a IB::Contract as first argument" unless contract.is_a? IB::Contract
77
+ self.legs << contract
78
+ error "cannot add leg if no con_id is provided" if contract.con_id.blank?
79
+ # weigth = 1 --> sets Combo.side to buy and overwrites the action statement
80
+ # leg_params[:weight] = 1 unless leg_params.key?(:weight) || leg_params.key?(:ratio)
81
+ leg_description = leg_params.extract!( :description )
82
+ leg_description = "#{leg_params[:action] || 'buy'} #{leg_params[:weight] || "1"} #{contract.to_human}" if leg_description.empty?
83
+ self.combo_legs << ComboLeg.new( contract.attributes.slice( :con_id, :exchange ).merge( leg_params ))
84
+ self.description = "#{description.nil? ? "": description + " / "} #{leg_description}" rescue "Spread: #{contract.to_human}"
85
+
86
+ self # return object to enable chaining
87
+
88
+
89
+ end
90
+
91
+ # removes the contract from the spread definition
92
+ #
93
+ def remove_leg contract_or_position = nil
94
+ contract = if contract_or_position.is_a? (IB::Contract)
95
+ contract_or_position
96
+ elsif contract_or_position.is_a? Numeric
97
+ legs.at contract_or_position
98
+ else
99
+ error "Specify a contract to be removed or the position in the legs-array as parameter to remove a leg"
100
+ end
101
+ the_con_id = contract.verify.first &.con_id
102
+ error "Invalid Contract specified" unless the_con_id.is_a? Numeric
103
+ legs.delete_if { |x| x.con_id == the_con_id }
104
+ combo_legs.delete_if { |x| x.con_id == the_con_id }
105
+ self.description = description + " removed #{contract.to_human}"
106
+ self # make method chainable
107
+ end
108
+
109
+ # essentail
110
+ # effectivley clones the object
111
+ #
112
+ def essential
113
+ the_es = self.class.new invariant_attributes
114
+ the_es.legs = legs.map{|y| IB::Contract.build y.invariant_attributes}
115
+ the_es.combo_legs = combo_legs.map{|y| IB::ComboLeg.new y.invariant_attributes }
116
+ the_es.description = description
117
+ the_es # return
118
+ end
119
+
120
+ def multiplier
121
+ (legs.map(&:multiplier).sum/legs.size).to_i
122
+ end
123
+
124
+ # provide a negative con_id
125
+ def con_id
126
+ if attributes[:con_id].present? && attributes[] < 0
127
+ attributes[:con_id]
128
+ else
129
+ -legs.map{ |x| x.is_a?(String) ? x.expand.con_id : x.con_id}.sum
130
+ end
131
+ end
132
+
133
+
134
+ # def non_guaranteed= x
135
+ # super.merge combo_params: [ ['NonGuaranteed', x] ]
136
+ # end
137
+ #
138
+ #
139
+ # def non_guaranteed
140
+ # combo_params['NonGuaranteed']
141
+ # end
142
+ # optional: specify default order prarmeters for all spreads
143
+ # def order_requirements
144
+ # super.merge symbol: symbol
145
+ # end
146
+
147
+
148
+ def as_table
149
+ t= Terminal::Table.new title: description[1..-2] ,
150
+ headings: table_header,
151
+
152
+ style: { border: :unicode }
153
+
154
+ t.add_row table_row
155
+ legs.each{ |y| t.add_row y.table_row }
156
+ t.render
157
+
158
+ end
159
+
160
+ def self.build_from_json container
161
+ read_leg = ->(a) do
162
+ IB::ComboLeg.new :con_id => a.read_int,
163
+ :ratio => a.read_int,
164
+ :action => a.read_string,
165
+ :exchange => a.read_string
166
+
167
+ end
168
+ object= self.new container['Spread'].clone.read_contract
169
+ object.legs = container['legs'].map{|x| IB::Contract.build x.clone.read_contract}
170
+ object.combo_legs = container['combo_legs'].map{ |x| read_leg[ x.clone ] }
171
+ object.description = container['misc'].clone.read_string
172
+ object
173
+
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,25 @@
1
+ module IB
2
+ class Stock < IB::Contract
3
+ validates_format_of :sec_type, :with => /\Astock\z/,
4
+ :message => "should be a Stock"
5
+ validates_format_of :symbol, with: /\A.*\z/,
6
+ message: 'should not be blank'
7
+ def default_attributes
8
+ super.merge :sec_type => :stock, currency:'USD', exchange:'SMART'
9
+ end
10
+
11
+ def merge **new_attributes
12
+ super( **{ trading_class: '', primary_exchange: '' }.merge(new_attributes) )
13
+ end
14
+
15
+ def to_human
16
+ att = [ symbol,
17
+ currency, ( exchange == 'SMART' ? nil: exchange ),
18
+ (primary_exchange.present? && !primary_exchange.empty? ? primary_exchange : nil),
19
+ @description.present? ? " (#{@description}) " : nil,
20
+ ].compact
21
+ "<Stock: " + att.join(" ") + ">"
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ module IB
2
+ # Calculated characteristics of underlying Contract (volatile)
3
+ class Underlying < IB::Base
4
+ include BaseProperties
5
+
6
+ has_one :contract
7
+
8
+ prop :con_id, # Id of the Underlying Contract
9
+ :delta, # double: The underlying stock or future delta.
10
+ :price # double: The price of the underlying.
11
+
12
+ validates_numericality_of :con_id, :delta, :price #, :allow_nil => true
13
+
14
+ def default_attributes
15
+ super.merge :con_id => 0
16
+ end
17
+
18
+ # Serialize under_comp parameters
19
+ def serialize
20
+ [con_id, delta, price]
21
+ end
22
+
23
+ # Comparison
24
+ def == other
25
+ super(other) ||
26
+ other.is_a?(self.class) &&
27
+ con_id == other.con_id && delta == other.delta && price == other.price
28
+ end
29
+
30
+ end # class Underlying
31
+ UnderComp = Underlying
32
+ end