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
data/models/ib/index.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
class Index < Contract
|
|
3
|
+
validates_format_of :sec_type, :with => /\Aind\z/,
|
|
4
|
+
:message => "should be a Index"
|
|
5
|
+
def default_attributes
|
|
6
|
+
super.merge :sec_type => 'IND'
|
|
7
|
+
end
|
|
8
|
+
def to_human
|
|
9
|
+
"<Index: " + [symbol, currency].join(" ") + " (#{description}) >"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
data/models/ib/option.rb
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
class Option < Contract
|
|
3
|
+
|
|
4
|
+
validates_numericality_of :strike, :greater_than => 0
|
|
5
|
+
validates_format_of :sec_type, :with => /\Aoption\z/,
|
|
6
|
+
:message => "should be an option"
|
|
7
|
+
validates_format_of :local_symbol, :with => /\A\w+\s*\d{6}[pcPC]\d{8}$|\A\z/,
|
|
8
|
+
:message => "invalid OSI code"
|
|
9
|
+
validates_format_of :right, :with => /\Aput$|^call\z/,
|
|
10
|
+
:message => "should be put or call"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# introduce Option.greek with reference to IB::OptionDetail-dataset
|
|
14
|
+
#
|
|
15
|
+
has_one :greek , as: :option_detail
|
|
16
|
+
# For Options, this is contract's OSI (Option Symbology Initiative) name/code
|
|
17
|
+
alias osi local_symbol
|
|
18
|
+
|
|
19
|
+
def osi= value
|
|
20
|
+
# Normalize to 21 char
|
|
21
|
+
self.local_symbol = value.sub(/ /, ' '*(22-value.size))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Make valid IB Contract definition from OSI (Option Symbology Initiative) code.
|
|
25
|
+
# NB: Simply making a new Contract with *local_symbol* (osi) property set to a
|
|
26
|
+
# valid OSI code works just as well, just do NOT set *expiry*, *right* or
|
|
27
|
+
# *strike* properties in this case.
|
|
28
|
+
# This class method provided as a backup and shows how to analyse OSI codes.
|
|
29
|
+
def self.from_osi osi
|
|
30
|
+
|
|
31
|
+
# Parse contract's OSI (OCC Option Symbology Initiative) code
|
|
32
|
+
args = osi.match(/(\w+)\s?(\d\d)(\d\d)(\d\d)([pcPC])(\d+)/).to_a.drop(1)
|
|
33
|
+
symbol = args.shift
|
|
34
|
+
year = 2000 + args.shift.to_i
|
|
35
|
+
month = args.shift.to_i
|
|
36
|
+
day = args.shift.to_i
|
|
37
|
+
right = args.shift.upcase
|
|
38
|
+
strike = args.shift.to_i/1000.0
|
|
39
|
+
|
|
40
|
+
# Set correct expiry date - IB expiry date differs from OSI if expiry date
|
|
41
|
+
# falls on Saturday (see https://github.com/arvicco/option_mower/issues/4)
|
|
42
|
+
expiry_date = Time.utc(year, month, day)
|
|
43
|
+
expiry_date = Time.utc(year, month, day-1) if expiry_date.wday == 6
|
|
44
|
+
|
|
45
|
+
new :symbol => symbol,
|
|
46
|
+
:exchange => "SMART",
|
|
47
|
+
:expiry => expiry_date.to_ib[2..7], # YYMMDD
|
|
48
|
+
:right => right,
|
|
49
|
+
:strike => strike
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def default_attributes
|
|
53
|
+
super.merge :sec_type => :option
|
|
54
|
+
#self[:description] ||= osi ? osi : "#{symbol} #{strike} #{right} #{expiry}"
|
|
55
|
+
end
|
|
56
|
+
def == other
|
|
57
|
+
super(other) || ( # finish positive, if contract#== is true
|
|
58
|
+
# otherwise, we most probably compare the response from IB with our selfmade input
|
|
59
|
+
exchange == other.exchange &&
|
|
60
|
+
include_expired == other.include_expired &&
|
|
61
|
+
sec_type == other.sec_type &&
|
|
62
|
+
multiplier == other.multiplier &&
|
|
63
|
+
strike == other.strike &&
|
|
64
|
+
right == other.right &&
|
|
65
|
+
multiplier == other.multiplier &&
|
|
66
|
+
expiry == other.expiry )
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# returns the verified option for the next (regular) expiry of the contract.
|
|
72
|
+
#
|
|
73
|
+
# Argument: Reference date, provided as Date-Object or as parseable string or integer, i.e
|
|
74
|
+
# Symbols::Options.rut.merge( strike: 2000 ).next_expiry( "2405")
|
|
75
|
+
# returns "<Option: RUT 20240516 put 2000.0 SMART USD>" instead of
|
|
76
|
+
# "<Option: RUT 20240517 put 2000.0 SMART USD>" because the third friday is a bank holiday
|
|
77
|
+
#
|
|
78
|
+
# Optionally a block can be provided, returning the expiry to check in the format "yyyymmdd"
|
|
79
|
+
#
|
|
80
|
+
# if verify is not available, the option is just returned.
|
|
81
|
+
#
|
|
82
|
+
# (always returns a new option, respects immutability of the IB::Contract)
|
|
83
|
+
#
|
|
84
|
+
# raises IB::LoadError if no Option is available for the expiry given.
|
|
85
|
+
#
|
|
86
|
+
def next_expiry d = Date.today
|
|
87
|
+
# get the next regular option
|
|
88
|
+
exp = block_given? ? yield : self.class.next_expiry( d )
|
|
89
|
+
# check if the option exists, otherwise fetch the previous date until a valid contract is detected
|
|
90
|
+
if IB::Connection.current.plugins.include? 'verify'
|
|
91
|
+
real_option = nil
|
|
92
|
+
loop do
|
|
93
|
+
real_option = merge( expiry: exp ).verify.first
|
|
94
|
+
break unless real_option.nil?
|
|
95
|
+
exp = ( exp.to_i - 1 ).to_s
|
|
96
|
+
error( "No suitable next expiry option found", :load ) if exp[-2..-1] == "00"
|
|
97
|
+
end
|
|
98
|
+
real_option
|
|
99
|
+
else
|
|
100
|
+
merge( expiry: exp, last_trading_day: Date.parse( exp ).strftime( "%Y-%m-%d" ) )
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# returns the third friday of the (next) month (class method)
|
|
106
|
+
#
|
|
107
|
+
# Argument: can either be Date, a String which parses to a Date or
|
|
108
|
+
# an Integer, yymm yyyymm or yyyymmdd --> 2406 or 202406 or 20240618
|
|
109
|
+
#
|
|
110
|
+
# if called with a digit, this is interpretated a day of the current month
|
|
111
|
+
#
|
|
112
|
+
def self.next_expiry base = Date.today
|
|
113
|
+
|
|
114
|
+
c = 0
|
|
115
|
+
begin
|
|
116
|
+
base_date = if base.is_a? Date
|
|
117
|
+
[ base.year, base.month ]
|
|
118
|
+
else
|
|
119
|
+
(base = Date.parse(base.to_s)).then { | d | [ d.year,d.month ] }
|
|
120
|
+
end.then{ |y,m| Date.new y,m }
|
|
121
|
+
rescue Date::Error => e
|
|
122
|
+
base = base.to_s + "01"
|
|
123
|
+
c = c + 1
|
|
124
|
+
retry if c == 1
|
|
125
|
+
end
|
|
126
|
+
error "Next-Expiry: Not a valid date: #{base}" if base_date.nil?
|
|
127
|
+
friday = 5
|
|
128
|
+
base_wday = base_date.wday
|
|
129
|
+
b= base_date + ( friday > base_wday ? friday - base_wday : 7 - base_wday + friday ) + 14
|
|
130
|
+
|
|
131
|
+
if b < base
|
|
132
|
+
next_expiry base.then{| y | a = y + 25; a.strftime "%Y%m01" }
|
|
133
|
+
else
|
|
134
|
+
b.strftime "%Y%m%d"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def to_human
|
|
139
|
+
"<Option: " + [symbol, expiry, right, strike, exchange, currency].join(" ") + ">"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
end # class Option
|
|
143
|
+
|
|
144
|
+
class FutureOption < Option
|
|
145
|
+
def default_attributes
|
|
146
|
+
super.merge :sec_type => :futures_option
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end # module IB
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
# Additional Option properties and Option-Calculations
|
|
3
|
+
class OptionDetail < Base
|
|
4
|
+
include BaseProperties
|
|
5
|
+
|
|
6
|
+
prop :delta, :gamma, :vega, :theta, # greeks
|
|
7
|
+
:implied_volatility,
|
|
8
|
+
:pv_dividend, # anticipated Dividend
|
|
9
|
+
:under_price, # price of the Underlying
|
|
10
|
+
:option_price,
|
|
11
|
+
:close_price,
|
|
12
|
+
:open_tick,
|
|
13
|
+
:bid_price,
|
|
14
|
+
:ask_price,
|
|
15
|
+
:prev_strike,
|
|
16
|
+
:next_strike,
|
|
17
|
+
:prev_expiry,
|
|
18
|
+
:next_expiry,
|
|
19
|
+
:option_price,
|
|
20
|
+
:updated_at
|
|
21
|
+
belongs_to :option
|
|
22
|
+
|
|
23
|
+
# returns true if all datafields are filled with reasonal data
|
|
24
|
+
def complete?
|
|
25
|
+
fields= [ :delta, :gamma, :vega, :theta,
|
|
26
|
+
:implied_volatility, :pv_dividend, :open_tick,
|
|
27
|
+
:under_price, :option_price, :close_price, :bid_price, :ask_price]
|
|
28
|
+
|
|
29
|
+
!fields.detect{|y| self.send(y).nil?}
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# true if greeks are present
|
|
34
|
+
def greeks? # theta and implied volatility are not always present
|
|
35
|
+
fields= [ :delta, :gamma, :vega ]
|
|
36
|
+
|
|
37
|
+
!fields.detect{|y| self.send(y).nil?}
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# true if prices are received
|
|
42
|
+
def prices?
|
|
43
|
+
fields = [:implied_volatility, :under_price, :option_price]
|
|
44
|
+
!fields.detect{|y| self.send(y).nil?}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def iv
|
|
48
|
+
implied_volatility
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def spread
|
|
52
|
+
bid_price - ask_price
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def to_human
|
|
56
|
+
outstr= ->( item ) { if item.nil? then "--" else sprintf("%g" , item) end }
|
|
57
|
+
att = " optionPrice: #{ outstr[ option_price ]}, UnderlyingPrice: #{ outstr[ under_price] } impl.Vola: #{ outstr[ implied_volatility ]} ; dividend: #{ outstr[ pv_dividend ]}; "
|
|
58
|
+
greeks = "Greeks:: delta: #{ outstr[ delta ] }; gamma: #{ outstr[ gamma ]}, vega: #{ outstr[ vega ] }; theta: #{ outstr[ theta ]}"
|
|
59
|
+
prices= " close: #{ outstr[ close_price ]}; bid: #{ outstr[ bid_price ]}; ask: #{ outstr[ ask_price ]} "
|
|
60
|
+
if complete?
|
|
61
|
+
"< "+ prices + "\n" + att + "\n" + greeks + " >"
|
|
62
|
+
elsif prices?
|
|
63
|
+
"< " + att + greeks + " >"
|
|
64
|
+
else
|
|
65
|
+
"< " + greeks + " >"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def table_header
|
|
71
|
+
[ 'Greeks', 'price', 'impl. vola', 'dividend', 'delta','gamma', 'vega' , 'theta']
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def table_row
|
|
75
|
+
outstr= ->( item ) { { value: item.nil? ? "--" : sprintf("%g" , item) , alignment: :right } }
|
|
76
|
+
outprice= ->( item ) { { value: item.nil? ? "--" : sprintf("%7.2f" , item) , alignment: :right } }
|
|
77
|
+
option_short = ->{"#{option.right} #{option.symbol}#{ "/"+ option.trading_class unless option.trading_class == option.symbol } #{option.expiry} #{option.strike}"}
|
|
78
|
+
[ option_short[], outprice[ option_price ], outprice[ implied_volatility ],
|
|
79
|
+
outprice[ pv_dividend ],
|
|
80
|
+
outprice[ delta ], outprice[ gamma ], outprice[ vega ] , outprice[ theta ] ]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end # class
|
|
84
|
+
end # module
|