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.
- checksums.yaml +7 -0
- data/.gitignore +52 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CLAUDE.md +131 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +120 -0
- data/Guardfile +24 -0
- data/LICENSE +674 -0
- data/LLM_GUIDE.md +388 -0
- data/README.md +114 -0
- data/Rakefile +11 -0
- data/VERSION +1 -0
- data/api.gemspec +50 -0
- data/bin/console +96 -0
- data/bin/console.yml +3 -0
- data/bin/setup +8 -0
- data/bin/simple +91 -0
- data/changelog.md +32 -0
- data/conditions/ib/execution_condition.rb +31 -0
- data/conditions/ib/margin_condition.rb +28 -0
- data/conditions/ib/order_condition.rb +29 -0
- data/conditions/ib/percent_change_condition.rb +34 -0
- data/conditions/ib/price_condition.rb +44 -0
- data/conditions/ib/time_condition.rb +42 -0
- data/conditions/ib/volume_condition.rb +36 -0
- data/lib/class_extensions.rb +167 -0
- data/lib/ib/base.rb +109 -0
- data/lib/ib/base_properties.rb +178 -0
- data/lib/ib/connection.rb +573 -0
- data/lib/ib/constants.rb +402 -0
- data/lib/ib/contract.rb +30 -0
- data/lib/ib/errors.rb +52 -0
- data/lib/ib/messages/abstract_message.rb +68 -0
- data/lib/ib/messages/incoming/abstract_message.rb +116 -0
- data/lib/ib/messages/incoming/abstract_tick.rb +25 -0
- data/lib/ib/messages/incoming/account_message.rb +26 -0
- data/lib/ib/messages/incoming/alert.rb +34 -0
- data/lib/ib/messages/incoming/contract_data.rb +105 -0
- data/lib/ib/messages/incoming/contract_message.rb +13 -0
- data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
- data/lib/ib/messages/incoming/execution_data.rb +50 -0
- data/lib/ib/messages/incoming/histogram_data.rb +30 -0
- data/lib/ib/messages/incoming/historical_data.rb +65 -0
- data/lib/ib/messages/incoming/historical_data_update.rb +50 -0
- data/lib/ib/messages/incoming/managed_accounts.rb +21 -0
- data/lib/ib/messages/incoming/market_depth.rb +34 -0
- data/lib/ib/messages/incoming/market_depth_l2.rb +15 -0
- data/lib/ib/messages/incoming/next_valid_id.rb +19 -0
- data/lib/ib/messages/incoming/open_order.rb +290 -0
- data/lib/ib/messages/incoming/order_status.rb +85 -0
- data/lib/ib/messages/incoming/portfolio_value.rb +47 -0
- data/lib/ib/messages/incoming/position_data.rb +21 -0
- data/lib/ib/messages/incoming/positions_multi.rb +15 -0
- data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
- data/lib/ib/messages/incoming/receive_fa.rb +30 -0
- data/lib/ib/messages/incoming/scanner_data.rb +54 -0
- data/lib/ib/messages/incoming/tick_by_tick.rb +77 -0
- data/lib/ib/messages/incoming/tick_efp.rb +18 -0
- data/lib/ib/messages/incoming/tick_generic.rb +12 -0
- data/lib/ib/messages/incoming/tick_option.rb +60 -0
- data/lib/ib/messages/incoming/tick_price.rb +60 -0
- data/lib/ib/messages/incoming/tick_size.rb +55 -0
- data/lib/ib/messages/incoming/tick_string.rb +13 -0
- data/lib/ib/messages/incoming.rb +292 -0
- data/lib/ib/messages/outgoing/abstract_message.rb +84 -0
- data/lib/ib/messages/outgoing/bar_request_message.rb +247 -0
- data/lib/ib/messages/outgoing/new-place-order.rb +193 -0
- data/lib/ib/messages/outgoing/old-place-order.rb +147 -0
- data/lib/ib/messages/outgoing/place_order.rb +149 -0
- data/lib/ib/messages/outgoing/request_account_summary.rb +79 -0
- data/lib/ib/messages/outgoing/request_historical_data.rb +182 -0
- data/lib/ib/messages/outgoing/request_market_data.rb +102 -0
- data/lib/ib/messages/outgoing/request_market_depth.rb +57 -0
- data/lib/ib/messages/outgoing/request_real_time_bars.rb +48 -0
- data/lib/ib/messages/outgoing/request_scanner_subscription.rb +73 -0
- data/lib/ib/messages/outgoing/request_tick_by_tick_data.rb +21 -0
- data/lib/ib/messages/outgoing.rb +410 -0
- data/lib/ib/messages.rb +139 -0
- data/lib/ib/order_condition.rb +26 -0
- data/lib/ib/plugins.rb +27 -0
- data/lib/ib/prepare_data.rb +61 -0
- data/lib/ib/raw_message_parser.rb +99 -0
- data/lib/ib/socket.rb +83 -0
- data/lib/ib/support.rb +236 -0
- data/lib/ib/version.rb +6 -0
- data/lib/ib-api.rb +44 -0
- data/lib/server_versions.rb +145 -0
- data/lib/support/array_function.rb +28 -0
- data/lib/support/logging.rb +45 -0
- data/models/ib/account.rb +72 -0
- data/models/ib/account_value.rb +33 -0
- data/models/ib/bag.rb +55 -0
- data/models/ib/bar.rb +31 -0
- data/models/ib/combo_leg.rb +127 -0
- data/models/ib/contract.rb +411 -0
- data/models/ib/contract_detail.rb +118 -0
- data/models/ib/execution.rb +67 -0
- data/models/ib/forex.rb +12 -0
- data/models/ib/future.rb +64 -0
- data/models/ib/index.rb +14 -0
- data/models/ib/option.rb +149 -0
- data/models/ib/option_detail.rb +84 -0
- data/models/ib/order.rb +720 -0
- data/models/ib/order_state.rb +155 -0
- data/models/ib/portfolio_value.rb +86 -0
- data/models/ib/spread.rb +176 -0
- data/models/ib/stock.rb +25 -0
- data/models/ib/underlying.rb +32 -0
- data/plugins/ib/advanced-account.rb +442 -0
- data/plugins/ib/alerts/base-alert.rb +125 -0
- data/plugins/ib/alerts/gateway-alerts.rb +15 -0
- data/plugins/ib/alerts/order-alerts.rb +73 -0
- data/plugins/ib/auto-adjust.rb +0 -0
- data/plugins/ib/connection-tools.rb +122 -0
- data/plugins/ib/eod.rb +326 -0
- data/plugins/ib/greeks.rb +102 -0
- data/plugins/ib/managed-accounts.rb +274 -0
- data/plugins/ib/market-price.rb +150 -0
- data/plugins/ib/option-chain.rb +167 -0
- data/plugins/ib/order-flow.rb +157 -0
- data/plugins/ib/order-prototypes/abstract.rb +67 -0
- data/plugins/ib/order-prototypes/adaptive.rb +40 -0
- data/plugins/ib/order-prototypes/all-in-one.rb +46 -0
- data/plugins/ib/order-prototypes/combo.rb +46 -0
- data/plugins/ib/order-prototypes/forex.rb +40 -0
- data/plugins/ib/order-prototypes/limit.rb +193 -0
- data/plugins/ib/order-prototypes/market.rb +116 -0
- data/plugins/ib/order-prototypes/pegged.rb +169 -0
- data/plugins/ib/order-prototypes/premarket.rb +31 -0
- data/plugins/ib/order-prototypes/stop.rb +202 -0
- data/plugins/ib/order-prototypes/volatility.rb +39 -0
- data/plugins/ib/order-prototypes.rb +118 -0
- data/plugins/ib/probability-of-expiring.rb +109 -0
- data/plugins/ib/process-orders.rb +155 -0
- data/plugins/ib/roll.rb +86 -0
- data/plugins/ib/spread-prototypes/butterfly.rb +77 -0
- data/plugins/ib/spread-prototypes/calendar.rb +97 -0
- data/plugins/ib/spread-prototypes/stock-spread.rb +56 -0
- data/plugins/ib/spread-prototypes/straddle.rb +70 -0
- data/plugins/ib/spread-prototypes/strangle.rb +93 -0
- data/plugins/ib/spread-prototypes/vertical.rb +83 -0
- data/plugins/ib/spread-prototypes.rb +70 -0
- data/plugins/ib/symbols/abstract.rb +136 -0
- data/plugins/ib/symbols/bonds.rb +28 -0
- data/plugins/ib/symbols/cfd.rb +19 -0
- data/plugins/ib/symbols/combo.rb +46 -0
- data/plugins/ib/symbols/commodity.rb +17 -0
- data/plugins/ib/symbols/forex.rb +41 -0
- data/plugins/ib/symbols/futures.rb +127 -0
- data/plugins/ib/symbols/index.rb +43 -0
- data/plugins/ib/symbols/options.rb +99 -0
- data/plugins/ib/symbols/stocks.rb +44 -0
- data/plugins/ib/symbols/version.rb +5 -0
- data/plugins/ib/symbols.rb +118 -0
- data/plugins/ib/verify.rb +226 -0
- data/symbols/w20.yml +210 -0
- data/t.txt +20 -0
- data/update.md +71 -0
- metadata +327 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Support
|
|
2
|
+
module ArrayFunction
|
|
3
|
+
def save_insert item, key, overwrite = true
|
|
4
|
+
member = find { |entry| entry[ key ] == item[ key] }
|
|
5
|
+
if member
|
|
6
|
+
self[ index( member ) ] = item if overwrite
|
|
7
|
+
else
|
|
8
|
+
self << item
|
|
9
|
+
end
|
|
10
|
+
self # always returns the array
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# performs [ [ array ] & [ array ] & [..] ].first
|
|
14
|
+
def intercept
|
|
15
|
+
a = self.dup
|
|
16
|
+
s = a.pop
|
|
17
|
+
while a.present?
|
|
18
|
+
s = s & a.pop
|
|
19
|
+
end
|
|
20
|
+
s.first unless s.nil? # return_value (or nil)
|
|
21
|
+
end
|
|
22
|
+
end # module
|
|
23
|
+
end # module
|
|
24
|
+
|
|
25
|
+
class Array
|
|
26
|
+
include Support::ArrayFunction
|
|
27
|
+
end
|
|
28
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#module Kernel
|
|
2
|
+
# private
|
|
3
|
+
# def this_method_name
|
|
4
|
+
# caller[0] =~ /`([^']*)'/ and $1
|
|
5
|
+
# end
|
|
6
|
+
# see also __method__ and __callee__
|
|
7
|
+
#end
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
module Support
|
|
12
|
+
module Logging
|
|
13
|
+
def self.included(base)
|
|
14
|
+
base.extend ClassMethods
|
|
15
|
+
base.send :define_method, :logger do
|
|
16
|
+
base.logger
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module ClassMethods
|
|
21
|
+
def logger
|
|
22
|
+
@logger
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def logger=(logger)
|
|
26
|
+
@logger = logger
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def configure_logger(log=nil)
|
|
30
|
+
if log
|
|
31
|
+
@logger = log
|
|
32
|
+
else
|
|
33
|
+
@logger = ::Logger.new(STDOUT)
|
|
34
|
+
@logger.level = ::Logger::INFO
|
|
35
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
|
36
|
+
# "#{datetime.strftime("%d.%m.(%X)")}#{"%5s" % severity}->#{msg}\n"
|
|
37
|
+
"#{"%1s" % severity[0]}: #{msg}\n"
|
|
38
|
+
end
|
|
39
|
+
@logger.debug "------------------------------ start logging ----------------------------"
|
|
40
|
+
end # branch
|
|
41
|
+
end # def
|
|
42
|
+
end # module ClassMethods
|
|
43
|
+
end # module Logging
|
|
44
|
+
end # module Support
|
|
45
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
class Account < IB::Base
|
|
3
|
+
include BaseProperties
|
|
4
|
+
# attr_accessible :alias, :account, :connected
|
|
5
|
+
|
|
6
|
+
prop :account, # String
|
|
7
|
+
:alias, #
|
|
8
|
+
:type,
|
|
9
|
+
:last_updated,
|
|
10
|
+
:connected => :bool
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
validates_format_of :account, :with => /\A[D]?[UF]{1}\d{5,8}\z/ , :message => 'should be (X)X00000'
|
|
15
|
+
|
|
16
|
+
# in tableless mode the scope is ignored
|
|
17
|
+
|
|
18
|
+
has_many :account_values
|
|
19
|
+
has_many :portfolio_values
|
|
20
|
+
has_many :contracts
|
|
21
|
+
has_many :orders
|
|
22
|
+
has_many :focuses
|
|
23
|
+
|
|
24
|
+
def default_attributes
|
|
25
|
+
super.merge account: 'X000000'
|
|
26
|
+
super.merge alias: ''
|
|
27
|
+
super.merge type: 'Account'
|
|
28
|
+
super.merge connected: false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def logger #nodoc#
|
|
32
|
+
Connection.logger
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def print_type #nodoc#
|
|
36
|
+
(test_environment? ? "demo_" : "") + ( user? ? "user" : "advisor" )
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def advisor?
|
|
40
|
+
!!(type =~ /Advisor/ || account =~ /\A[D]?[F]{1}/)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def user?
|
|
44
|
+
!!(type =~ /User/ || account =~ /\A[D]?[U]{1}/)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_environment?
|
|
48
|
+
!!(account =~ /^[D]{1}/)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def == other
|
|
52
|
+
super(other) ||
|
|
53
|
+
other.is_a?(self.class) && account == other.account
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def to_human
|
|
57
|
+
a = if self.alias.present? && self.alias != account
|
|
58
|
+
" alias: "+ self.alias
|
|
59
|
+
else
|
|
60
|
+
""
|
|
61
|
+
end
|
|
62
|
+
"<#{print_type} #{account}#{a}>"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def name #nodoc#
|
|
66
|
+
self.alias.present? ? self.alias : account
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# alias :id :account
|
|
70
|
+
end # class
|
|
71
|
+
|
|
72
|
+
end # module
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
# Instantiate with a Hash of attributes, to be auto-set via initialize in Model.
|
|
3
|
+
class AccountValue < IB::Base
|
|
4
|
+
include BaseProperties
|
|
5
|
+
|
|
6
|
+
belongs_to :account
|
|
7
|
+
|
|
8
|
+
prop :key,
|
|
9
|
+
:value,
|
|
10
|
+
:currency
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# comparison
|
|
14
|
+
def == other
|
|
15
|
+
super(other) ||
|
|
16
|
+
other.is_a?(self.class) &&
|
|
17
|
+
key == other.key &&
|
|
18
|
+
currency == other.currency &&
|
|
19
|
+
value == other.value
|
|
20
|
+
end
|
|
21
|
+
def default_attributes
|
|
22
|
+
super.merge key: 'AccountValue',
|
|
23
|
+
value: 0,
|
|
24
|
+
currency: 'USD'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_human
|
|
28
|
+
"<#{key}=#{value} #{currency}>"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
alias to_s to_human
|
|
32
|
+
end # class
|
|
33
|
+
end # module IB
|
data/models/ib/bag.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
|
|
3
|
+
# "BAG" is not really a contract, but a combination (combo) of securities.
|
|
4
|
+
# AKA basket or bag of securities. Individual securities in combo are represented
|
|
5
|
+
# by ComboLeg objects.
|
|
6
|
+
class Bag < Contract
|
|
7
|
+
# General Notes:
|
|
8
|
+
# 1. :exchange for the leg definition must match that of the combination order.
|
|
9
|
+
# The exception is for a STK legs, which must specify the SMART exchange.
|
|
10
|
+
# 2. :symbol => "USD" For combo Contract, this is an arbitrary value (like "USD")
|
|
11
|
+
|
|
12
|
+
prop :combo_params # bags carry "non_guarantieed: true/false" in combo_params
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
validates_format_of :sec_type, :with => /\Abag\z/, :message => "should be a bag"
|
|
16
|
+
validates_format_of :right, :with => /\Anone\z/, :message => "should be none"
|
|
17
|
+
validates_format_of :expiry, :with => /\A\z/, :message => "should be blank"
|
|
18
|
+
|
|
19
|
+
def default_attributes
|
|
20
|
+
super.merge :sec_type => :bag #,:legs => Array.new,
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# def description
|
|
24
|
+
# self[:description] || to_human
|
|
25
|
+
# end
|
|
26
|
+
|
|
27
|
+
def to_human
|
|
28
|
+
"<Bag: #{[symbol, exchange, currency].join(' ')} legs: #{legs_description} >"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def con_id= arg
|
|
32
|
+
# dont' update con_id
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
### Leg-related methods
|
|
36
|
+
|
|
37
|
+
# IB-equivalent leg description.
|
|
38
|
+
def legs_description
|
|
39
|
+
self[:legs_description] || combo_legs.map { |the_leg| "#{the_leg.con_id}|#{the_leg.weight}" }.join(',')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Check if two Contracts have same legs (maybe in different order)
|
|
43
|
+
def same_legs? other
|
|
44
|
+
combo_legs == other.combo_legs ||
|
|
45
|
+
legs_description.split(',').sort == other.legs_description.split(',').sort
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Contract comparison
|
|
50
|
+
def == other
|
|
51
|
+
super && same_legs?(other)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end # class Bag
|
|
55
|
+
end # IB
|
data/models/ib/bar.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
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::Base
|
|
5
|
+
include BaseProperties
|
|
6
|
+
|
|
7
|
+
has_one :contract # The bar represents timeseries info for this Contract
|
|
8
|
+
|
|
9
|
+
prop :open, # The bar opening price.
|
|
10
|
+
:high, # The high price during the time covered by the bar.
|
|
11
|
+
:low, # The low price during the time covered by the bar.
|
|
12
|
+
:close, # The bar closing price.
|
|
13
|
+
:volume, # Volume
|
|
14
|
+
:wap, # Weighted average price during the time covered by the bar.
|
|
15
|
+
:trades, # int: When TRADES data history is returned, represents number
|
|
16
|
+
# of trades that occurred during the time period the bar covers
|
|
17
|
+
:time #DateTime
|
|
18
|
+
# The date-time stamp of the start of the bar. The format is
|
|
19
|
+
# determined by the reqHistoricalData() formatDate parameter.
|
|
20
|
+
# :has_gaps => :bool # Whether or not there are gaps in the data. ## omitted since ServerVersion 124
|
|
21
|
+
|
|
22
|
+
validates_numericality_of :open, :high, :low, :close, :volume
|
|
23
|
+
|
|
24
|
+
def to_human
|
|
25
|
+
"<Bar: #{time.strftime("(%d.%m.%y)%X")} wap #{wap.round(3)} OHLC #{open} #{high} #{low} #{close} " +
|
|
26
|
+
(trades ? "trades #{trades}" : "") + " vol #{volume}>"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
alias to_s to_human
|
|
30
|
+
end # class Bar
|
|
31
|
+
end # module IB
|
|
@@ -0,0 +1,127 @@
|
|
|
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::Base
|
|
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
|
+
:exchange, # String: exchange to which the complete combo order will be routed.
|
|
23
|
+
#
|
|
24
|
+
# For institutional customers only! For stock legs when doing short sale
|
|
25
|
+
:short_sale_slot, # int: 0 - retail(default),
|
|
26
|
+
# 1 = clearing broker, 2 = third party
|
|
27
|
+
:designated_location, # String: Only for shortSaleSlot == 2.
|
|
28
|
+
# Otherwise leave blank or orders will be rejected.
|
|
29
|
+
:exempt_code, # int: (-1)
|
|
30
|
+
[:side, :action] => PROPS[:side], # String: Action/side: BUY/SELL/SSHORT/SSHORTX
|
|
31
|
+
:open_close => PROPS[:open_close]
|
|
32
|
+
# int: Whether the order is an open or close order. Values:
|
|
33
|
+
# SAME = 0 Same as the parent security. The only option for retail customers.
|
|
34
|
+
# OPEN = 1 Open. This value is only valid for institutional customers.
|
|
35
|
+
# CLOSE = 2 Close. This value is only valid for institutional customers.
|
|
36
|
+
# UNKNOWN = 3
|
|
37
|
+
:price # support for pet leg prices
|
|
38
|
+
|
|
39
|
+
# Extra validations
|
|
40
|
+
validates_numericality_of :ratio, :con_id
|
|
41
|
+
validates_format_of :designated_location, :with => /\A\z/,
|
|
42
|
+
:message => "should be blank or orders will be rejected"
|
|
43
|
+
|
|
44
|
+
def default_attributes
|
|
45
|
+
super.merge :con_id => 0,
|
|
46
|
+
:ratio => 1,
|
|
47
|
+
:side => :buy,
|
|
48
|
+
:open_close => :same, # The only option for retail customers.
|
|
49
|
+
:short_sale_slot => :default,
|
|
50
|
+
:designated_location => '',
|
|
51
|
+
:exchange => 'SMART', # Unless SMART, Order modification fails
|
|
52
|
+
:exempt_code => -1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Leg's weight is a combination of action and ratio
|
|
56
|
+
def weight
|
|
57
|
+
side == :buy ? ratio : -ratio
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def weight= value
|
|
61
|
+
value = value.to_i
|
|
62
|
+
if value > 0
|
|
63
|
+
self.side = :buy
|
|
64
|
+
self.ratio = value
|
|
65
|
+
else
|
|
66
|
+
self.side = :sell
|
|
67
|
+
self.ratio = -value
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Some messages include open_close, some don't. wtf.
|
|
72
|
+
def serialize *fields
|
|
73
|
+
[con_id,
|
|
74
|
+
ratio,
|
|
75
|
+
side.to_sup,
|
|
76
|
+
exchange,
|
|
77
|
+
(fields.include?(:extended) ?
|
|
78
|
+
[self[:open_close],
|
|
79
|
+
self[:short_sale_slot],
|
|
80
|
+
designated_location,
|
|
81
|
+
exempt_code] :
|
|
82
|
+
[])
|
|
83
|
+
].flatten
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# fields are generated by serialize(:extended)
|
|
88
|
+
# i.e.
|
|
89
|
+
# z= Strangle.build from: Symbols::Index.stoxx, p: 3700, c: 4000, expiry: 202106
|
|
90
|
+
# zc= z.combo_legs.serialize :extended
|
|
91
|
+
# => [[321584786, 1, "BUY", "DTB", 0, 0, "", -1], [321584637, 1, "BUY", "DTB", 0, 0, "", -1]]
|
|
92
|
+
# nz = zc.map{|o| ComboLeg.build *o }
|
|
93
|
+
# zc.map{|o| ComboLeg.build o } => # is equivalent
|
|
94
|
+
# => [#<IB::ComboLeg:0x0000000001c36bc0 @attributes={:con_id=>321584786, :ratio=>1, :side=>"B", :exchange=>"DTB", ...
|
|
95
|
+
# nz.first == z.combo_legs.first => true
|
|
96
|
+
#
|
|
97
|
+
def self.build *fields
|
|
98
|
+
self.new Hash[[:con_id,
|
|
99
|
+
:ratio,
|
|
100
|
+
:side, # reverse to_sup?
|
|
101
|
+
:exchange,
|
|
102
|
+
:open_close,
|
|
103
|
+
:short_sale_slot,
|
|
104
|
+
:designated_location,
|
|
105
|
+
:exempt_code].zip fields]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def to_human
|
|
109
|
+
"<ComboLeg: #{side} #{ratio} con_id #{con_id} at #{exchange}>"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Order comparison
|
|
113
|
+
def == other
|
|
114
|
+
super(other) ||
|
|
115
|
+
other.is_a?(self.class) &&
|
|
116
|
+
con_id == other.con_id &&
|
|
117
|
+
ratio == other.ratio &&
|
|
118
|
+
open_close == other.open_close &&
|
|
119
|
+
short_sale_slot == other.short_sale_slot &&
|
|
120
|
+
exempt_code == other.exempt_code &&
|
|
121
|
+
side == other.side &&
|
|
122
|
+
exchange == other.exchange &&
|
|
123
|
+
designated_location == other.designated_location
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
end # ComboLeg
|
|
127
|
+
end # module IB
|