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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/lib/ib-api.rb +10 -0
  3. data/lib/ib/base.rb +99 -0
  4. data/lib/ib/base_properties.rb +154 -0
  5. data/lib/ib/connection.rb +327 -0
  6. data/lib/ib/constants.rb +334 -0
  7. data/lib/ib/db.rb +29 -0
  8. data/lib/ib/engine.rb +35 -0
  9. data/lib/ib/errors.rb +40 -0
  10. data/lib/ib/extensions.rb +72 -0
  11. data/lib/ib/flex.rb +106 -0
  12. data/lib/ib/logger.rb +25 -0
  13. data/lib/ib/messages.rb +88 -0
  14. data/lib/ib/messages/abstract_message.rb +89 -0
  15. data/lib/ib/messages/incoming.rb +134 -0
  16. data/lib/ib/messages/incoming/abstract_message.rb +99 -0
  17. data/lib/ib/messages/incoming/alert.rb +34 -0
  18. data/lib/ib/messages/incoming/contract_data.rb +102 -0
  19. data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
  20. data/lib/ib/messages/incoming/execution_data.rb +54 -0
  21. data/lib/ib/messages/incoming/historical_data.rb +55 -0
  22. data/lib/ib/messages/incoming/market_depths.rb +44 -0
  23. data/lib/ib/messages/incoming/next_valid_id.rb +18 -0
  24. data/lib/ib/messages/incoming/open_order.rb +232 -0
  25. data/lib/ib/messages/incoming/order_status.rb +81 -0
  26. data/lib/ib/messages/incoming/portfolio_value.rb +39 -0
  27. data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
  28. data/lib/ib/messages/incoming/scanner_data.rb +53 -0
  29. data/lib/ib/messages/incoming/ticks.rb +131 -0
  30. data/lib/ib/messages/outgoing.rb +331 -0
  31. data/lib/ib/messages/outgoing/abstract_message.rb +73 -0
  32. data/lib/ib/messages/outgoing/bar_requests.rb +189 -0
  33. data/lib/ib/messages/outgoing/place_order.rb +141 -0
  34. data/lib/ib/model.rb +6 -0
  35. data/lib/ib/models.rb +10 -0
  36. data/lib/ib/requires.rb +9 -0
  37. data/lib/ib/socket.rb +81 -0
  38. data/lib/ib/symbols.rb +35 -0
  39. data/lib/ib/symbols/bonds.rb +28 -0
  40. data/lib/ib/symbols/forex.rb +41 -0
  41. data/lib/ib/symbols/futures.rb +117 -0
  42. data/lib/ib/symbols/options.rb +39 -0
  43. data/lib/ib/symbols/stocks.rb +37 -0
  44. data/lib/ib/version.rb +6 -0
  45. data/lib/models/ib/bag.rb +51 -0
  46. data/lib/models/ib/bar.rb +45 -0
  47. data/lib/models/ib/combo_leg.rb +103 -0
  48. data/lib/models/ib/contract.rb +292 -0
  49. data/lib/models/ib/contract_detail.rb +89 -0
  50. data/lib/models/ib/execution.rb +65 -0
  51. data/lib/models/ib/option.rb +60 -0
  52. data/lib/models/ib/order.rb +391 -0
  53. data/lib/models/ib/order_state.rb +128 -0
  54. data/lib/models/ib/underlying.rb +34 -0
  55. metadata +96 -0
@@ -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
@@ -0,0 +1,6 @@
1
+ require 'pathname'
2
+
3
+ module IB
4
+ VERSION_FILE = Pathname.new(__FILE__).dirname + '../../VERSION' # :nodoc:
5
+ VERSION = VERSION_FILE.exist? ? VERSION_FILE.read.strip : nil
6
+ end
@@ -0,0 +1,51 @@
1
+ require 'models/ib/contract'
2
+
3
+ module IB
4
+
5
+ # "BAG" is not really a contract, but a combination (combo) of securities.
6
+ # AKA basket or bag of securities. Individual securities in combo are represented
7
+ # by ComboLeg objects.
8
+ class Bag < Contract
9
+ # General Notes:
10
+ # 1. :exchange for the leg definition must match that of the combination order.
11
+ # The exception is for a STK legs, which must specify the SMART exchange.
12
+ # 2. :symbol => "USD" For combo Contract, this is an arbitrary value (like "USD")
13
+
14
+ validates_format_of :sec_type, :with => /\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