tradier 0.1.0
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.
- data/.yardopts +8 -0
- data/LICENSE.md +20 -0
- data/README.md +114 -0
- data/Rakefile +19 -0
- data/lib/tradier.rb +31 -0
- data/lib/tradier/account.rb +30 -0
- data/lib/tradier/api/accounts.rb +122 -0
- data/lib/tradier/api/markets.rb +113 -0
- data/lib/tradier/api/orders.rb +84 -0
- data/lib/tradier/api/utils.rb +47 -0
- data/lib/tradier/api/utils/account.rb +15 -0
- data/lib/tradier/api/utils/balance.rb +15 -0
- data/lib/tradier/api/utils/base.rb +46 -0
- data/lib/tradier/api/utils/event.rb +15 -0
- data/lib/tradier/api/utils/expiration.rb +19 -0
- data/lib/tradier/api/utils/gainloss.rb +15 -0
- data/lib/tradier/api/utils/history.rb +15 -0
- data/lib/tradier/api/utils/option_quote.rb +15 -0
- data/lib/tradier/api/utils/order.rb +15 -0
- data/lib/tradier/api/utils/position.rb +15 -0
- data/lib/tradier/api/utils/quote.rb +15 -0
- data/lib/tradier/api/utils/strike.rb +15 -0
- data/lib/tradier/api/utils/timesales.rb +15 -0
- data/lib/tradier/api/utils/watchlist.rb +15 -0
- data/lib/tradier/api/watchlists.rb +139 -0
- data/lib/tradier/balance.rb +17 -0
- data/lib/tradier/base.rb +76 -0
- data/lib/tradier/calendar.rb +23 -0
- data/lib/tradier/client.rb +89 -0
- data/lib/tradier/clock.rb +21 -0
- data/lib/tradier/configurable.rb +76 -0
- data/lib/tradier/core_ext/enumerable.rb +9 -0
- data/lib/tradier/default.rb +75 -0
- data/lib/tradier/error.rb +34 -0
- data/lib/tradier/error/bad_gateway.rb +11 -0
- data/lib/tradier/error/bad_request.rb +10 -0
- data/lib/tradier/error/client_error.rb +28 -0
- data/lib/tradier/error/configuration_error.rb +6 -0
- data/lib/tradier/error/decode_error.rb +9 -0
- data/lib/tradier/error/forbidden.rb +10 -0
- data/lib/tradier/error/gateway_timeout.rb +11 -0
- data/lib/tradier/error/internal_server_error.rb +11 -0
- data/lib/tradier/error/not_acceptable.rb +10 -0
- data/lib/tradier/error/not_found.rb +10 -0
- data/lib/tradier/error/raise_error.rb +31 -0
- data/lib/tradier/error/server_error.rb +28 -0
- data/lib/tradier/error/service_unavailable.rb +11 -0
- data/lib/tradier/error/too_many_requests.rb +12 -0
- data/lib/tradier/error/unauthorized.rb +10 -0
- data/lib/tradier/error/unprocessable_entity.rb +10 -0
- data/lib/tradier/event.rb +8 -0
- data/lib/tradier/history.rb +20 -0
- data/lib/tradier/option_quote.rb +37 -0
- data/lib/tradier/order.rb +14 -0
- data/lib/tradier/position.rb +7 -0
- data/lib/tradier/profile.rb +14 -0
- data/lib/tradier/quote.rb +12 -0
- data/lib/tradier/response/parse_json.rb +25 -0
- data/lib/tradier/response/raise_error.rb +31 -0
- data/lib/tradier/symbol.rb +147 -0
- data/lib/tradier/timesales.rb +11 -0
- data/lib/tradier/version.rb +3 -0
- data/lib/tradier/watchlist.rb +16 -0
- data/lib/tradier/watchlist_item.rb +17 -0
- data/spec/fixtures/account_balances.json +28 -0
- data/spec/fixtures/account_gainloss.json +18 -0
- data/spec/fixtures/account_history.json +96 -0
- data/spec/fixtures/account_orders.json +833 -0
- data/spec/fixtures/account_positions.json +22 -0
- data/spec/fixtures/calendar.json +400 -0
- data/spec/fixtures/chain.json +2972 -0
- data/spec/fixtures/clock.json +10 -0
- data/spec/fixtures/expirations.json +18 -0
- data/spec/fixtures/history.json +2086 -0
- data/spec/fixtures/option_quote.json +14 -0
- data/spec/fixtures/option_quotes.json +26 -0
- data/spec/fixtures/order.json +1 -0
- data/spec/fixtures/order_with_warnings.json +17 -0
- data/spec/fixtures/placed_order.json +6 -0
- data/spec/fixtures/quote.json +45 -0
- data/spec/fixtures/quotes.json +88 -0
- data/spec/fixtures/session.json +6 -0
- data/spec/fixtures/strikes.json +173 -0
- data/spec/fixtures/timesales.json +2956 -0
- data/spec/fixtures/user_balances.json +118 -0
- data/spec/fixtures/user_gainloss.json +6775 -0
- data/spec/fixtures/user_history.json +101 -0
- data/spec/fixtures/user_orders.json +57 -0
- data/spec/fixtures/user_positions.json +50 -0
- data/spec/fixtures/user_profile.json +103 -0
- data/spec/fixtures/watchlist.json +30 -0
- data/spec/fixtures/watchlist_item.json +6 -0
- data/spec/fixtures/watchlists.json +18 -0
- data/spec/spec_helper.rb +64 -0
- data/spec/tradier/account_spec.rb +76 -0
- data/spec/tradier/api/accounts_spec.rb +195 -0
- data/spec/tradier/api/markets_spec.rb +219 -0
- data/spec/tradier/api/orders_spec.rb +94 -0
- data/spec/tradier/api/utils/expiration_spec.rb +8 -0
- data/spec/tradier/api/watchlists_spec.rb +164 -0
- data/spec/tradier/balance_spec.rb +4 -0
- data/spec/tradier/base_spec.rb +11 -0
- data/spec/tradier/calendar_spec.rb +27 -0
- data/spec/tradier/client_spec.rb +125 -0
- data/spec/tradier/clock_spec.rb +73 -0
- data/spec/tradier/error/client_error_spec.rb +22 -0
- data/spec/tradier/error/server_error_spec.rb +22 -0
- data/spec/tradier/error_spec.rb +26 -0
- data/spec/tradier/option_quote_spec.rb +61 -0
- data/spec/tradier/order_spec.rb +43 -0
- data/spec/tradier/position_spec.rb +4 -0
- data/spec/tradier/profile_spec.rb +28 -0
- data/spec/tradier/quote_spec.rb +29 -0
- data/spec/tradier/symbol_spec.rb +54 -0
- data/spec/tradier/watchlist_item_spec.rb +48 -0
- data/spec/tradier/watchlist_spec.rb +35 -0
- data/spec/tradier_spec.rb +105 -0
- data/tradier.gemspec +30 -0
- metadata +302 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'tradier/error'
|
2
|
+
|
3
|
+
module Tradier
|
4
|
+
class Error
|
5
|
+
# Raised when Tradier returns a 4xx HTTP status code or there's an error in Faraday
|
6
|
+
class ClientError < Tradier::Error
|
7
|
+
MESSAGE = "Client Error"
|
8
|
+
|
9
|
+
# Create a new error from an HTTP environment
|
10
|
+
#
|
11
|
+
# @param response [Hash]
|
12
|
+
# @return [Tradier::Error]
|
13
|
+
def self.from_response(response={})
|
14
|
+
new(response[:body], response[:response_headers])
|
15
|
+
end
|
16
|
+
|
17
|
+
# Initializes a new ServerError object
|
18
|
+
#
|
19
|
+
# @param message [String]
|
20
|
+
# @param response_headers [Hash]
|
21
|
+
# @return [Tradier::Error::ServerError]
|
22
|
+
def initialize(message=nil, response_headers={})
|
23
|
+
super((message || self.class.const_get(:MESSAGE)), response_headers)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'tradier/error/server_error'
|
2
|
+
|
3
|
+
module Tradier
|
4
|
+
class Error
|
5
|
+
# Raised when Tradier returns the HTTP status code 504
|
6
|
+
class GatewayTimeout < Tradier::Error::ServerError
|
7
|
+
HTTP_STATUS_CODE = 504
|
8
|
+
MESSAGE = "The Tradier servers are up, but the request couldn't be serviced due to some failure within our stack. Try again later."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'tradier/error/server_error'
|
2
|
+
|
3
|
+
module Tradier
|
4
|
+
class Error
|
5
|
+
# Raised when Tradier returns the HTTP status code 500
|
6
|
+
class InternalServerError < Tradier::Error::ServerError
|
7
|
+
HTTP_STATUS_CODE = 500
|
8
|
+
MESSAGE = "Something is technically wrong."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'tradier/error/bad_gateway'
|
3
|
+
require 'tradier/error/bad_request'
|
4
|
+
require 'tradier/error/forbidden'
|
5
|
+
require 'tradier/error/gateway_timeout'
|
6
|
+
require 'tradier/error/internal_server_error'
|
7
|
+
require 'tradier/error/not_acceptable'
|
8
|
+
require 'tradier/error/not_found'
|
9
|
+
require 'tradier/error/service_unavailable'
|
10
|
+
require 'tradier/error/too_many_requests'
|
11
|
+
require 'tradier/error/unauthorized'
|
12
|
+
require 'tradier/error/unprocessable_entity'
|
13
|
+
|
14
|
+
module Tradier
|
15
|
+
module Response
|
16
|
+
class RaiseError < Faraday::Response::Middleware
|
17
|
+
|
18
|
+
def on_complete(env)
|
19
|
+
status_code = env[:status].to_i
|
20
|
+
error_class = @klass.errors[status_code]
|
21
|
+
raise error_class.from_response(env) if error_class
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(app, klass)
|
25
|
+
@klass = klass
|
26
|
+
super(app)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'tradier/error'
|
2
|
+
|
3
|
+
module Tradier
|
4
|
+
class Error
|
5
|
+
# Raised when Tradier returns a 5xx HTTP status code
|
6
|
+
class ServerError < Tradier::Error
|
7
|
+
MESSAGE = "Server Error"
|
8
|
+
|
9
|
+
# Create a new error from an HTTP environment
|
10
|
+
#
|
11
|
+
# @param response [Hash]
|
12
|
+
# @return [Tradier::Error]
|
13
|
+
def self.from_response(response={})
|
14
|
+
new(response[:body], response[:response_headers])
|
15
|
+
end
|
16
|
+
|
17
|
+
# Initializes a new ServerError object
|
18
|
+
#
|
19
|
+
# @param message [String]
|
20
|
+
# @param response_headers [Hash]
|
21
|
+
# @return [Tradier::Error::ServerError]
|
22
|
+
def initialize(message=nil, response_headers={})
|
23
|
+
super((message || self.class.const_get(:MESSAGE)), response_headers)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'tradier/error/server_error'
|
2
|
+
|
3
|
+
module Tradier
|
4
|
+
class Error
|
5
|
+
# Raised when Tradier returns the HTTP status code 503
|
6
|
+
class ServiceUnavailable < Tradier::Error::ServerError
|
7
|
+
HTTP_STATUS_CODE = 503
|
8
|
+
MESSAGE = "Tradier is over capacity."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'tradier/error/client_error'
|
2
|
+
|
3
|
+
module Tradier
|
4
|
+
class Error
|
5
|
+
# Raised when Tradier returns the HTTP status code 429
|
6
|
+
class TooManyRequests < Tradier::Error::ClientError
|
7
|
+
HTTP_STATUS_CODE = 429
|
8
|
+
end
|
9
|
+
EnhanceYourCalm = TooManyRequests
|
10
|
+
RateLimited = TooManyRequests
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'tradier/base'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module Tradier
|
5
|
+
class History < Tradier::Base
|
6
|
+
|
7
|
+
attr_reader :open , :close, :high, :low, :volume
|
8
|
+
|
9
|
+
def date
|
10
|
+
return unless @date || @attrs[:date]
|
11
|
+
|
12
|
+
@date ||= Date.parse(@attrs[:date])
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_response(body={})
|
16
|
+
new(body)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'tradier/base'
|
2
|
+
require 'tradier/symbol'
|
3
|
+
|
4
|
+
module Tradier
|
5
|
+
class OptionQuote < Tradier::Base
|
6
|
+
|
7
|
+
attr_reader :symbol, :strike, :last, :bid, :ask, :change, :open_interest, :bid_size, :ask_size, :volume
|
8
|
+
|
9
|
+
def put?
|
10
|
+
parsed_symbol.put?
|
11
|
+
end
|
12
|
+
|
13
|
+
def call?
|
14
|
+
parsed_symbol.call?
|
15
|
+
end
|
16
|
+
|
17
|
+
def underlier
|
18
|
+
parsed_symbol.underlier
|
19
|
+
end
|
20
|
+
|
21
|
+
def expiration_date
|
22
|
+
return unless @expiration_date || @attrs[:expiration_date]
|
23
|
+
|
24
|
+
@expiration_date ||= Date.parse(@attrs[:expiration_date])
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(option_quote)
|
28
|
+
self.symbol == option_quote.symbol
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def parsed_symbol
|
34
|
+
@parsed ||= Tradier::Symbol.parse(@attrs[:symbol])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'tradier/base'
|
2
|
+
|
3
|
+
module Tradier
|
4
|
+
class Order < Tradier::Base
|
5
|
+
attr_reader :id, :order_id, :type, :price, :symbol, :side, :quantity, \
|
6
|
+
:status, :time_in_force, :option_type, :exec_inst, :exec_status, :extended_hours, \
|
7
|
+
:trailing_limit_type, :trailing_stop_type, :request_date, :response_date, :num_legs, \
|
8
|
+
:errors, :result
|
9
|
+
|
10
|
+
def self.from_response(body={})
|
11
|
+
new(body[:order] || body)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'tradier/base'
|
2
|
+
|
3
|
+
module Tradier
|
4
|
+
class Profile < Tradier::Base
|
5
|
+
|
6
|
+
def name
|
7
|
+
@name ||= @attrs[:profile] && @attrs[:profile][:name]
|
8
|
+
end
|
9
|
+
|
10
|
+
def accounts
|
11
|
+
@accounts ||= @attrs[:profile] && [@attrs[:profile][:account]].flatten.map { |a| Tradier::Account.new(a); }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
module Tradier
|
5
|
+
module Response
|
6
|
+
class ParseJson < Faraday::Response::Middleware
|
7
|
+
|
8
|
+
def parse(body)
|
9
|
+
case body
|
10
|
+
when /\A^\s*$\z/, nil
|
11
|
+
nil
|
12
|
+
else
|
13
|
+
MultiJson.decode(body, :symbolize_keys => true)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_complete(env)
|
18
|
+
if respond_to?(:parse)
|
19
|
+
env[:body] = parse(env[:body]) unless [204, 301, 302, 304].include?(env[:status])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'tradier/error/bad_gateway'
|
3
|
+
require 'tradier/error/bad_request'
|
4
|
+
require 'tradier/error/forbidden'
|
5
|
+
require 'tradier/error/gateway_timeout'
|
6
|
+
require 'tradier/error/internal_server_error'
|
7
|
+
require 'tradier/error/not_acceptable'
|
8
|
+
require 'tradier/error/not_found'
|
9
|
+
require 'tradier/error/service_unavailable'
|
10
|
+
require 'tradier/error/too_many_requests'
|
11
|
+
require 'tradier/error/unauthorized'
|
12
|
+
require 'tradier/error/unprocessable_entity'
|
13
|
+
|
14
|
+
module Tradier
|
15
|
+
module Response
|
16
|
+
class RaiseError < Faraday::Response::Middleware
|
17
|
+
|
18
|
+
def on_complete(env)
|
19
|
+
status_code = env[:status].to_i
|
20
|
+
error_class = @klass.errors[status_code]
|
21
|
+
raise error_class.from_response(env) if error_class
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(app, klass)
|
25
|
+
@klass = klass
|
26
|
+
super(app)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Tradier
|
2
|
+
class Symbol
|
3
|
+
|
4
|
+
OPTION_SYMBOL_REGEXP = /([a-z]+)(\d?)(\d{2})(\d{2})(\d{2})(C|P)(\d{5})(\d{3})/ix
|
5
|
+
|
6
|
+
MONTHS = {
|
7
|
+
1 => 'Jan',
|
8
|
+
2 => 'Feb',
|
9
|
+
3 => 'Mar',
|
10
|
+
4 => 'Apr',
|
11
|
+
5 => 'May',
|
12
|
+
6 => 'Jun',
|
13
|
+
7 => 'Jul',
|
14
|
+
8 => 'Aug',
|
15
|
+
9 => 'Sep',
|
16
|
+
10 => 'Oct',
|
17
|
+
11 => 'Nov',
|
18
|
+
12 => 'Dec'
|
19
|
+
}
|
20
|
+
|
21
|
+
attr_accessor :symbol, :adjustment, :month, :day, :year, :option_type, :dollars, :cents
|
22
|
+
|
23
|
+
def initialize(symbol)
|
24
|
+
@symbol = symbol
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.parse(symbol)
|
28
|
+
symbol.strip!
|
29
|
+
|
30
|
+
sym = if match = symbol.match(OPTION_SYMBOL_REGEXP)
|
31
|
+
match = match.to_a
|
32
|
+
match.shift
|
33
|
+
|
34
|
+
s = Tradier::Symbol.new(match[0])
|
35
|
+
s.adjustment = match[1]
|
36
|
+
s.year = match[2]
|
37
|
+
s.month = match[3]
|
38
|
+
s.day = match[4]
|
39
|
+
s.option_type = match[5]
|
40
|
+
s.dollars = match[6]
|
41
|
+
s.cents = match[7]
|
42
|
+
s
|
43
|
+
elsif symbol =~ /\d/
|
44
|
+
nil
|
45
|
+
elsif symbol =~ /[a-zA-Z]+/
|
46
|
+
Tradier::Symbol.new(symbol)
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
raise ArgumentError.new("Invalid symbol: #{symbol}") unless sym && sym.valid?
|
52
|
+
sym
|
53
|
+
end
|
54
|
+
|
55
|
+
def adjustment?
|
56
|
+
adjustment && adjustment.to_i >= 1
|
57
|
+
end
|
58
|
+
|
59
|
+
def put?
|
60
|
+
return unless option_type
|
61
|
+
option_type.upcase == 'P'
|
62
|
+
end
|
63
|
+
|
64
|
+
def call?
|
65
|
+
return unless option_type
|
66
|
+
option_type.upcase == 'C'
|
67
|
+
end
|
68
|
+
|
69
|
+
def option?
|
70
|
+
put? || call?
|
71
|
+
end
|
72
|
+
|
73
|
+
def equity?
|
74
|
+
!option?
|
75
|
+
end
|
76
|
+
|
77
|
+
def valid?
|
78
|
+
equity? || (option? && valid_month?)
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_s
|
82
|
+
s = equity? ? underlier : "#{underlier}#{@adjustment if adjustment?}#{@year}#{occ_month}#{occ_day}#{@option_type}#{occ_dollars}#{occ_cents}"
|
83
|
+
s.upcase
|
84
|
+
end
|
85
|
+
|
86
|
+
def description
|
87
|
+
equity? ? underlier : "#{underlier} #{expiration_description} $#{strike_description} #{option_type_description}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def underlier
|
91
|
+
@symbol
|
92
|
+
end
|
93
|
+
|
94
|
+
def occ_month
|
95
|
+
@month.to_i >= 10 ? @month : "0#{@month.to_i}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def occ_day
|
99
|
+
@day.to_i >= 10 ? @day : "0#{@day.to_i}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def occ_cents
|
103
|
+
case @cents.size
|
104
|
+
when 3 then @cents
|
105
|
+
when 2 then "#{@cents}0"
|
106
|
+
when 1 then "#{@cents}00"
|
107
|
+
when 0 then "000"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def occ_dollars
|
112
|
+
return @dollars if @dollars.size == 5
|
113
|
+
('0' * (5 - @dollars.size)) + @dollars.to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
def expiration_description
|
117
|
+
"#{MONTHS[@month.to_i]} #{@day} #{year_description}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def year_description
|
121
|
+
"20#{@year}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def strike_description
|
125
|
+
"#{@dollars.to_i}.#{occ_cents[0,2]}"
|
126
|
+
end
|
127
|
+
|
128
|
+
def option_type_description
|
129
|
+
put? ? 'PUT' : 'CALL'
|
130
|
+
end
|
131
|
+
|
132
|
+
def expiration
|
133
|
+
Date.new(year_description.to_i, @month.to_i, @day.to_i)
|
134
|
+
end
|
135
|
+
|
136
|
+
def strike_price
|
137
|
+
"#{@dollars}.#{@cents}".to_f
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def valid_month?
|
143
|
+
@month.to_i <= 12 && @month.to_i >= 1
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|