my-ib-api 0.0.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/lib/ib-api.rb +10 -0
- data/lib/ib/base.rb +99 -0
- data/lib/ib/base_properties.rb +154 -0
- data/lib/ib/connection.rb +327 -0
- data/lib/ib/constants.rb +334 -0
- data/lib/ib/db.rb +29 -0
- data/lib/ib/engine.rb +35 -0
- data/lib/ib/errors.rb +40 -0
- data/lib/ib/extensions.rb +72 -0
- data/lib/ib/flex.rb +106 -0
- data/lib/ib/logger.rb +25 -0
- data/lib/ib/messages.rb +88 -0
- data/lib/ib/messages/abstract_message.rb +89 -0
- data/lib/ib/messages/incoming.rb +134 -0
- data/lib/ib/messages/incoming/abstract_message.rb +99 -0
- data/lib/ib/messages/incoming/alert.rb +34 -0
- data/lib/ib/messages/incoming/contract_data.rb +102 -0
- data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
- data/lib/ib/messages/incoming/execution_data.rb +54 -0
- data/lib/ib/messages/incoming/historical_data.rb +55 -0
- data/lib/ib/messages/incoming/market_depths.rb +44 -0
- data/lib/ib/messages/incoming/next_valid_id.rb +18 -0
- data/lib/ib/messages/incoming/open_order.rb +232 -0
- data/lib/ib/messages/incoming/order_status.rb +81 -0
- data/lib/ib/messages/incoming/portfolio_value.rb +39 -0
- data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
- data/lib/ib/messages/incoming/scanner_data.rb +53 -0
- data/lib/ib/messages/incoming/ticks.rb +131 -0
- data/lib/ib/messages/outgoing.rb +331 -0
- data/lib/ib/messages/outgoing/abstract_message.rb +73 -0
- data/lib/ib/messages/outgoing/bar_requests.rb +189 -0
- data/lib/ib/messages/outgoing/place_order.rb +141 -0
- data/lib/ib/model.rb +6 -0
- data/lib/ib/models.rb +10 -0
- data/lib/ib/requires.rb +9 -0
- data/lib/ib/socket.rb +81 -0
- data/lib/ib/symbols.rb +35 -0
- data/lib/ib/symbols/bonds.rb +28 -0
- data/lib/ib/symbols/forex.rb +41 -0
- data/lib/ib/symbols/futures.rb +117 -0
- data/lib/ib/symbols/options.rb +39 -0
- data/lib/ib/symbols/stocks.rb +37 -0
- data/lib/ib/version.rb +6 -0
- data/lib/models/ib/bag.rb +51 -0
- data/lib/models/ib/bar.rb +45 -0
- data/lib/models/ib/combo_leg.rb +103 -0
- data/lib/models/ib/contract.rb +292 -0
- data/lib/models/ib/contract_detail.rb +89 -0
- data/lib/models/ib/execution.rb +65 -0
- data/lib/models/ib/option.rb +60 -0
- data/lib/models/ib/order.rb +391 -0
- data/lib/models/ib/order_state.rb +128 -0
- data/lib/models/ib/underlying.rb +34 -0
- metadata +96 -0
data/lib/ib/symbols.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# These modules are used to facilitate referencing of most popular IB Contracts.
|
2
|
+
# For example, suppose you're explicitly creating such Contract in all your scripts:
|
3
|
+
# wfc = IB::Contract.new(:symbol => "WFC",
|
4
|
+
# :exchange => "NYSE",
|
5
|
+
# :currency => "USD",
|
6
|
+
# :sec_type => :stock,
|
7
|
+
# :description => "Wells Fargo Stock"),
|
8
|
+
#
|
9
|
+
# Instead, you can put this contract definition into 'ib/symbols/stocks' and just reference
|
10
|
+
# it as IB::Symbols::Stock[:wfc] anywhere you need it.
|
11
|
+
#
|
12
|
+
# Note that the :description field is local to ib-ruby, and is NOT part of the standard TWS API.
|
13
|
+
# It is never transmitted to IB. It's purely used clientside, and you can store any arbitrary
|
14
|
+
# string that you may find useful there.
|
15
|
+
|
16
|
+
module IB
|
17
|
+
module Symbols
|
18
|
+
def [] symbol
|
19
|
+
if contracts[symbol]
|
20
|
+
return contracts[symbol]
|
21
|
+
else
|
22
|
+
# symbol probably has not been predefined, tell user about it
|
23
|
+
file = self.to_s.split(/::/).last.downcase
|
24
|
+
msg = "Unknown symbol :#{symbol}, please pre-define it in lib/ib/symbols/#{file}.rb"
|
25
|
+
error msg, :symbol
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'ib/symbols/forex'
|
32
|
+
require 'ib/symbols/futures'
|
33
|
+
require 'ib/symbols/stocks'
|
34
|
+
require 'ib/symbols/options'
|
35
|
+
require 'ib/symbols/bonds'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Sample bond contract definitions
|
2
|
+
module IB
|
3
|
+
module Symbols
|
4
|
+
module Bonds
|
5
|
+
extend Symbols
|
6
|
+
|
7
|
+
def self.contracts
|
8
|
+
@contracts ||= {
|
9
|
+
:abbey => IB::Contract.new(:symbol => "ABBEY",
|
10
|
+
:currency => "USD",
|
11
|
+
:sec_type => :bond,
|
12
|
+
:description => "Any ABBEY bond"),
|
13
|
+
|
14
|
+
:ms => IB::Contract.new(:symbol => "MS",
|
15
|
+
:currency => "USD",
|
16
|
+
:sec_type => :bond,
|
17
|
+
:description => "Any Morgan Stanley bond"),
|
18
|
+
|
19
|
+
:wag => IB::Contract.new(:symbol => "WAG",
|
20
|
+
:currency => "USD",
|
21
|
+
:sec_type => :bond,
|
22
|
+
:description => "Any Wallgreens bond"),
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module IB
|
2
|
+
module Symbols
|
3
|
+
module Forex
|
4
|
+
extend Symbols
|
5
|
+
|
6
|
+
def self.contracts
|
7
|
+
@contracts ||= define_contracts
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# IDEALPRO is for orders over 20,000 and routes to the interbank quote stream.
|
13
|
+
# IDEAL is for smaller orders, and has wider spreads/slower execution... generally
|
14
|
+
# used for smaller currency conversions. IB::Symbols::Forex contracts are pre-defined
|
15
|
+
# on IDEALPRO, if you need something else please define forex contracts manually.
|
16
|
+
def self.define_contracts
|
17
|
+
@contracts = {}
|
18
|
+
|
19
|
+
# use combinations of these currencies for pre-defined forex contracts
|
20
|
+
currencies = [ "aud", "cad", "chf", "eur", "gbp", "hkd", "jpy", "nzd", "usd" ]
|
21
|
+
|
22
|
+
# create pairs list from currency list
|
23
|
+
pairs = currencies.product(currencies).
|
24
|
+
map { |pair| pair.join.upcase unless pair.first == pair.last }.compact
|
25
|
+
|
26
|
+
# now define each contract
|
27
|
+
pairs.each do |pair|
|
28
|
+
@contracts[pair.downcase.to_sym] = IB::Contract.new(
|
29
|
+
:symbol => pair[0..2],
|
30
|
+
:exchange => "IDEALPRO",
|
31
|
+
:currency => pair[3..5],
|
32
|
+
:sec_type => :forex,
|
33
|
+
:description => pair
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
@contracts
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# The Futures module tries to guess the front month future using a crude algorithm that
|
2
|
+
# does not take into account expiry/rollover day. This will be valid most of the time,
|
3
|
+
# but near/after expiry day the next quarter's contract takes over as the volume leader.
|
4
|
+
|
5
|
+
module IB
|
6
|
+
module Symbols
|
7
|
+
module Futures
|
8
|
+
extend Symbols
|
9
|
+
|
10
|
+
# Find the next front month of quarterly futures.
|
11
|
+
# N.B. This will not work as expected during the front month before expiration, as
|
12
|
+
# it will point to the next quarter even though the current month is still valid!
|
13
|
+
def self.next_quarter_month time=Time.now
|
14
|
+
[3, 6, 9, 12].find { |month| month > time.month } || 3 # for December, next March
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.next_quarter_year time=Time.now
|
18
|
+
next_quarter_month(time) < time.month ? time.year + 1 : time.year
|
19
|
+
end
|
20
|
+
|
21
|
+
# WARNING: This is currently broken. It returns the next
|
22
|
+
# quarterly expiration month after the current month. Many futures
|
23
|
+
# instruments have monthly contracts for the near months. This
|
24
|
+
# method will not work for such contracts; it will return the next
|
25
|
+
# quarter after the current month, even though the present month
|
26
|
+
# has the majority of the trading volume.
|
27
|
+
#
|
28
|
+
# For example, in early November of 2011, many contracts have the
|
29
|
+
# vast majority of their volume in the Nov 2011 contract, but this
|
30
|
+
# method will return the Dec 2011 contract instead.
|
31
|
+
#
|
32
|
+
def self.next_expiry time=Time.now
|
33
|
+
"#{ next_quarter_year(time) }#{ sprintf("%02d", next_quarter_month(time)) }"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Convenience method; generates an IB::Contract instance for a futures
|
37
|
+
# contract with the given parameters.
|
38
|
+
#
|
39
|
+
# If expiry is nil, it will use the end month of the current
|
40
|
+
# quarter. This will be wrong for most contracts most of the time,
|
41
|
+
# since most contracts have the majority of their volume in a
|
42
|
+
# nearby intraquarter month.
|
43
|
+
#
|
44
|
+
# It is recommended that you specify an expiration date manually
|
45
|
+
# until next_expiry is fixed. Expiry should be a string in the
|
46
|
+
# format "YYYYMM", where YYYY is the 4 digit year and MM is the 2
|
47
|
+
# digit month. For example, November 2011 is "201111".
|
48
|
+
#
|
49
|
+
def self.future(base_symbol, exchange, currency, description="", expiry=nil)
|
50
|
+
IB::Contract.new :symbol => base_symbol,
|
51
|
+
:expiry => expiry || next_expiry,
|
52
|
+
:exchange => exchange,
|
53
|
+
:currency => currency,
|
54
|
+
:sec_type => SECURITY_TYPES[:future],
|
55
|
+
:description => description
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.contracts
|
59
|
+
@contracts ||= {
|
60
|
+
:ym => IB::Contract.new(:symbol => "YM",
|
61
|
+
:expiry => next_expiry,
|
62
|
+
:exchange => "ECBOT",
|
63
|
+
:currency => "USD",
|
64
|
+
:sec_type => :future,
|
65
|
+
:description => "Mini-DJIA future"),
|
66
|
+
|
67
|
+
:es => IB::Contract.new(:symbol => "ES",
|
68
|
+
:expiry => next_expiry,
|
69
|
+
:exchange => "GLOBEX",
|
70
|
+
:currency => "USD",
|
71
|
+
:sec_type => :future,
|
72
|
+
:multiplier => 50,
|
73
|
+
:description => "E-Mini S&P 500 future"),
|
74
|
+
|
75
|
+
:gbp => IB::Contract.new(:symbol => "GBP",
|
76
|
+
:expiry => next_expiry,
|
77
|
+
:exchange => "GLOBEX",
|
78
|
+
:currency => "USD",
|
79
|
+
:sec_type => :future,
|
80
|
+
:multiplier => 62500,
|
81
|
+
:description => "British Pounds future"),
|
82
|
+
|
83
|
+
:eur => IB::Contract.new(:symbol => "EUR",
|
84
|
+
:expiry => next_expiry,
|
85
|
+
:exchange => "GLOBEX",
|
86
|
+
:currency => "USD",
|
87
|
+
:sec_type => :future,
|
88
|
+
:multiplier => 12500,
|
89
|
+
:description => "Euro FX future"),
|
90
|
+
|
91
|
+
:jpy => IB::Contract.new(:symbol => "JPY",
|
92
|
+
:expiry => next_expiry,
|
93
|
+
:exchange => "GLOBEX",
|
94
|
+
:currency => "USD",
|
95
|
+
:sec_type => :future,
|
96
|
+
:multiplier => 12500000,
|
97
|
+
:description => "Japanese Yen future"),
|
98
|
+
|
99
|
+
:hsi => IB::Contract.new(:symbol => "HSI",
|
100
|
+
:expiry => next_expiry,
|
101
|
+
:exchange => "HKFE",
|
102
|
+
:currency => "HKD",
|
103
|
+
:sec_type => :future,
|
104
|
+
:multiplier => 50,
|
105
|
+
:description => "Hang Seng Index future"),
|
106
|
+
|
107
|
+
:vix => IB::Contract.new(:symbol => "VIX",
|
108
|
+
:expiry => next_expiry,
|
109
|
+
:exchange => "CFE", #"ECBOT",
|
110
|
+
# :currency => "USD",
|
111
|
+
:sec_type => :future,
|
112
|
+
:description => "CBOE Volatility Index future")
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Option contracts definitions.
|
2
|
+
# TODO: add next_expiry and other convenience from Futures module.
|
3
|
+
module IB
|
4
|
+
module Symbols
|
5
|
+
module Options
|
6
|
+
extend Symbols
|
7
|
+
|
8
|
+
def self.contracts
|
9
|
+
@contracts ||= {
|
10
|
+
:wfc20 => IB::Option.new(:symbol => "WFC",
|
11
|
+
:expiry => "201301",
|
12
|
+
:right => "CALL",
|
13
|
+
:strike => 20.0,
|
14
|
+
:description => "Wells Fargo 20 Call 2013-01"),
|
15
|
+
:aapl500 => IB::Option.new(:symbol => "AAPL",
|
16
|
+
:expiry => "201301",
|
17
|
+
:right => "CALL",
|
18
|
+
:strike => 500,
|
19
|
+
:description => "Apple 500 Call 2013-01"),
|
20
|
+
:z50 => IB::Option.new(:symbol => "Z",
|
21
|
+
:exchange => "LIFFE",
|
22
|
+
:expiry => "201301",
|
23
|
+
:right => "CALL",
|
24
|
+
:strike => 50.0,
|
25
|
+
:description => " FTSE-100 index 50 Call 2013-03"),
|
26
|
+
:spy75 => IB::Option.new(:symbol => 'SPY',
|
27
|
+
:expiry => "201301",
|
28
|
+
:right => "P",
|
29
|
+
:currency => "USD",
|
30
|
+
:strike => 75.0,
|
31
|
+
:description => "SPY 75.0 Put 2014-01"),
|
32
|
+
:spy100 => IB::Option.new(:osi => 'SPY 140118P00100000'),
|
33
|
+
:vix20 => IB::Option.new(:osi => 'VIX 121121C00020000'),
|
34
|
+
:vxx40 => IB::Option.new(:osi => 'VXX 121117C00040000'),
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Frequently used stock contracts definitions
|
2
|
+
# TODO: auto-request :ContractDetails from IB if unknown symbol is requested?
|
3
|
+
module IB
|
4
|
+
module Symbols
|
5
|
+
module Stocks
|
6
|
+
extend Symbols
|
7
|
+
|
8
|
+
def self.contracts
|
9
|
+
@contracts ||= {
|
10
|
+
:aapl => IB::Contract.new(:symbol => "AAPL",
|
11
|
+
:currency => "USD",
|
12
|
+
:sec_type => :stock,
|
13
|
+
:description => "Apple Inc."),
|
14
|
+
|
15
|
+
:vxx => IB::Contract.new(:symbol => "VXX",
|
16
|
+
:exchange => "ARCA",
|
17
|
+
# :currency => "USD",
|
18
|
+
:sec_type => :stock,
|
19
|
+
:description => "iPath S&P500 VIX short term Futures ETN"),
|
20
|
+
|
21
|
+
:wfc => IB::Contract.new(:symbol => "WFC",
|
22
|
+
:exchange => "NYSE",
|
23
|
+
:currency => "USD",
|
24
|
+
:sec_type => :stock,
|
25
|
+
:description => "Wells Fargo"),
|
26
|
+
|
27
|
+
:wrong => IB::Contract.new(:symbol => "QEEUUE",
|
28
|
+
:exchange => "NYSE",
|
29
|
+
:currency => "USD",
|
30
|
+
:sec_type => :stock,
|
31
|
+
:description => "Non-existent stock"),
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/ib/version.rb
ADDED
@@ -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 => /\Abag\z/, :message => "should be a bag"
|
15
|
+
validates_format_of :right, :with => /\Anone\z/, :message => "should be none"
|
16
|
+
validates_format_of :expiry, :with => /\A\z/, :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,45 @@
|
|
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
|
+
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, # TODO: convert into Time object?
|
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.
|
21
|
+
|
22
|
+
validates_numericality_of :open, :high, :low, :close, :volume
|
23
|
+
|
24
|
+
# Order comparison
|
25
|
+
def == other
|
26
|
+
super(other) ||
|
27
|
+
other.is_a?(self.class) &&
|
28
|
+
time == other.time &&
|
29
|
+
open == other.open &&
|
30
|
+
high == other.high &&
|
31
|
+
low == other.low &&
|
32
|
+
close == other.close &&
|
33
|
+
wap == other.wap &&
|
34
|
+
trades == other.trades &&
|
35
|
+
volume == other.volume
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_human
|
39
|
+
"<Bar: #{time} wap #{wap} OHLC #{open} #{high} #{low} #{close} " +
|
40
|
+
(trades ? "trades #{trades}" : "") + " vol #{volume} gaps #{has_gaps}>"
|
41
|
+
end
|
42
|
+
|
43
|
+
alias to_s to_human
|
44
|
+
end # class Bar
|
45
|
+
end # module IB
|
@@ -0,0 +1,103 @@
|
|
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 => /\A\z/,
|
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
|
+
super(other) ||
|
91
|
+
other.is_a?(self.class) &&
|
92
|
+
con_id == other.con_id &&
|
93
|
+
ratio == other.ratio &&
|
94
|
+
open_close == other.open_close &&
|
95
|
+
short_sale_slot == other.short_sale_slot &&
|
96
|
+
exempt_code == other.exempt_code &&
|
97
|
+
side == other.side &&
|
98
|
+
exchange == other.exchange &&
|
99
|
+
designated_location == other.designated_location
|
100
|
+
end
|
101
|
+
|
102
|
+
end # ComboLeg
|
103
|
+
end # module IB
|