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.
Files changed (119) hide show
  1. data/.yardopts +8 -0
  2. data/LICENSE.md +20 -0
  3. data/README.md +114 -0
  4. data/Rakefile +19 -0
  5. data/lib/tradier.rb +31 -0
  6. data/lib/tradier/account.rb +30 -0
  7. data/lib/tradier/api/accounts.rb +122 -0
  8. data/lib/tradier/api/markets.rb +113 -0
  9. data/lib/tradier/api/orders.rb +84 -0
  10. data/lib/tradier/api/utils.rb +47 -0
  11. data/lib/tradier/api/utils/account.rb +15 -0
  12. data/lib/tradier/api/utils/balance.rb +15 -0
  13. data/lib/tradier/api/utils/base.rb +46 -0
  14. data/lib/tradier/api/utils/event.rb +15 -0
  15. data/lib/tradier/api/utils/expiration.rb +19 -0
  16. data/lib/tradier/api/utils/gainloss.rb +15 -0
  17. data/lib/tradier/api/utils/history.rb +15 -0
  18. data/lib/tradier/api/utils/option_quote.rb +15 -0
  19. data/lib/tradier/api/utils/order.rb +15 -0
  20. data/lib/tradier/api/utils/position.rb +15 -0
  21. data/lib/tradier/api/utils/quote.rb +15 -0
  22. data/lib/tradier/api/utils/strike.rb +15 -0
  23. data/lib/tradier/api/utils/timesales.rb +15 -0
  24. data/lib/tradier/api/utils/watchlist.rb +15 -0
  25. data/lib/tradier/api/watchlists.rb +139 -0
  26. data/lib/tradier/balance.rb +17 -0
  27. data/lib/tradier/base.rb +76 -0
  28. data/lib/tradier/calendar.rb +23 -0
  29. data/lib/tradier/client.rb +89 -0
  30. data/lib/tradier/clock.rb +21 -0
  31. data/lib/tradier/configurable.rb +76 -0
  32. data/lib/tradier/core_ext/enumerable.rb +9 -0
  33. data/lib/tradier/default.rb +75 -0
  34. data/lib/tradier/error.rb +34 -0
  35. data/lib/tradier/error/bad_gateway.rb +11 -0
  36. data/lib/tradier/error/bad_request.rb +10 -0
  37. data/lib/tradier/error/client_error.rb +28 -0
  38. data/lib/tradier/error/configuration_error.rb +6 -0
  39. data/lib/tradier/error/decode_error.rb +9 -0
  40. data/lib/tradier/error/forbidden.rb +10 -0
  41. data/lib/tradier/error/gateway_timeout.rb +11 -0
  42. data/lib/tradier/error/internal_server_error.rb +11 -0
  43. data/lib/tradier/error/not_acceptable.rb +10 -0
  44. data/lib/tradier/error/not_found.rb +10 -0
  45. data/lib/tradier/error/raise_error.rb +31 -0
  46. data/lib/tradier/error/server_error.rb +28 -0
  47. data/lib/tradier/error/service_unavailable.rb +11 -0
  48. data/lib/tradier/error/too_many_requests.rb +12 -0
  49. data/lib/tradier/error/unauthorized.rb +10 -0
  50. data/lib/tradier/error/unprocessable_entity.rb +10 -0
  51. data/lib/tradier/event.rb +8 -0
  52. data/lib/tradier/history.rb +20 -0
  53. data/lib/tradier/option_quote.rb +37 -0
  54. data/lib/tradier/order.rb +14 -0
  55. data/lib/tradier/position.rb +7 -0
  56. data/lib/tradier/profile.rb +14 -0
  57. data/lib/tradier/quote.rb +12 -0
  58. data/lib/tradier/response/parse_json.rb +25 -0
  59. data/lib/tradier/response/raise_error.rb +31 -0
  60. data/lib/tradier/symbol.rb +147 -0
  61. data/lib/tradier/timesales.rb +11 -0
  62. data/lib/tradier/version.rb +3 -0
  63. data/lib/tradier/watchlist.rb +16 -0
  64. data/lib/tradier/watchlist_item.rb +17 -0
  65. data/spec/fixtures/account_balances.json +28 -0
  66. data/spec/fixtures/account_gainloss.json +18 -0
  67. data/spec/fixtures/account_history.json +96 -0
  68. data/spec/fixtures/account_orders.json +833 -0
  69. data/spec/fixtures/account_positions.json +22 -0
  70. data/spec/fixtures/calendar.json +400 -0
  71. data/spec/fixtures/chain.json +2972 -0
  72. data/spec/fixtures/clock.json +10 -0
  73. data/spec/fixtures/expirations.json +18 -0
  74. data/spec/fixtures/history.json +2086 -0
  75. data/spec/fixtures/option_quote.json +14 -0
  76. data/spec/fixtures/option_quotes.json +26 -0
  77. data/spec/fixtures/order.json +1 -0
  78. data/spec/fixtures/order_with_warnings.json +17 -0
  79. data/spec/fixtures/placed_order.json +6 -0
  80. data/spec/fixtures/quote.json +45 -0
  81. data/spec/fixtures/quotes.json +88 -0
  82. data/spec/fixtures/session.json +6 -0
  83. data/spec/fixtures/strikes.json +173 -0
  84. data/spec/fixtures/timesales.json +2956 -0
  85. data/spec/fixtures/user_balances.json +118 -0
  86. data/spec/fixtures/user_gainloss.json +6775 -0
  87. data/spec/fixtures/user_history.json +101 -0
  88. data/spec/fixtures/user_orders.json +57 -0
  89. data/spec/fixtures/user_positions.json +50 -0
  90. data/spec/fixtures/user_profile.json +103 -0
  91. data/spec/fixtures/watchlist.json +30 -0
  92. data/spec/fixtures/watchlist_item.json +6 -0
  93. data/spec/fixtures/watchlists.json +18 -0
  94. data/spec/spec_helper.rb +64 -0
  95. data/spec/tradier/account_spec.rb +76 -0
  96. data/spec/tradier/api/accounts_spec.rb +195 -0
  97. data/spec/tradier/api/markets_spec.rb +219 -0
  98. data/spec/tradier/api/orders_spec.rb +94 -0
  99. data/spec/tradier/api/utils/expiration_spec.rb +8 -0
  100. data/spec/tradier/api/watchlists_spec.rb +164 -0
  101. data/spec/tradier/balance_spec.rb +4 -0
  102. data/spec/tradier/base_spec.rb +11 -0
  103. data/spec/tradier/calendar_spec.rb +27 -0
  104. data/spec/tradier/client_spec.rb +125 -0
  105. data/spec/tradier/clock_spec.rb +73 -0
  106. data/spec/tradier/error/client_error_spec.rb +22 -0
  107. data/spec/tradier/error/server_error_spec.rb +22 -0
  108. data/spec/tradier/error_spec.rb +26 -0
  109. data/spec/tradier/option_quote_spec.rb +61 -0
  110. data/spec/tradier/order_spec.rb +43 -0
  111. data/spec/tradier/position_spec.rb +4 -0
  112. data/spec/tradier/profile_spec.rb +28 -0
  113. data/spec/tradier/quote_spec.rb +29 -0
  114. data/spec/tradier/symbol_spec.rb +54 -0
  115. data/spec/tradier/watchlist_item_spec.rb +48 -0
  116. data/spec/tradier/watchlist_spec.rb +35 -0
  117. data/spec/tradier_spec.rb +105 -0
  118. data/tradier.gemspec +30 -0
  119. 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,6 @@
1
+ module Tradier
2
+ class Error
3
+ class ConfigurationError < ::ArgumentError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ require 'tradier/error'
2
+
3
+ module Tradier
4
+ class Error
5
+ # Raised when JSON parsing fails
6
+ class DecodeError < Tradier::Error
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ require 'tradier/error/client_error'
2
+
3
+ module Tradier
4
+ class Error
5
+ # Raised when Tradier returns the HTTP status code 403
6
+ class Forbidden < Tradier::Error::ClientError
7
+ HTTP_STATUS_CODE = 403
8
+ end
9
+ end
10
+ 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,10 @@
1
+ require 'tradier/error/client_error'
2
+
3
+ module Tradier
4
+ class Error
5
+ # Raised when Tradier returns the HTTP status code 406
6
+ class NotAcceptable < Tradier::Error::ClientError
7
+ HTTP_STATUS_CODE = 406
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'tradier/error/client_error'
2
+
3
+ module Tradier
4
+ class Error
5
+ # Raised when Tradier returns the HTTP status code 404
6
+ class NotFound < Tradier::Error::ClientError
7
+ HTTP_STATUS_CODE = 404
8
+ end
9
+ end
10
+ 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,10 @@
1
+ require 'tradier/error/client_error'
2
+
3
+ module Tradier
4
+ class Error
5
+ # Raised when Tradier returns the HTTP status code 401
6
+ class Unauthorized < Tradier::Error::ClientError
7
+ HTTP_STATUS_CODE = 401
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'tradier/error/client_error'
2
+
3
+ module Tradier
4
+ class Error
5
+ # Raised when Tradier returns the HTTP status code 404
6
+ class UnprocessableEntity < Tradier::Error::ClientError
7
+ HTTP_STATUS_CODE = 422
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ require 'tradier/base'
2
+
3
+ module Tradier
4
+ class Event < Tradier::Base
5
+
6
+ attr_reader :symbol, :meta, :description, :event_type, :amount
7
+ end
8
+ 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,7 @@
1
+ require 'tradier/base'
2
+
3
+ module Tradier
4
+ class Position < Tradier::Base
5
+ attr_reader :costbasis, :date_acquired, :quantity, :symbol
6
+ end
7
+ 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,12 @@
1
+ require 'tradier/base'
2
+
3
+ module Tradier
4
+ class Quote < Tradier::Base
5
+ attr_reader :symbol
6
+
7
+ def ==(quote)
8
+ self.symbol == quote.symbol
9
+ end
10
+
11
+ end
12
+ 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