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,17 @@
1
+ require 'tradier/base'
2
+
3
+ module Tradier
4
+ class Balance < Tradier::Base
5
+
6
+ attr_reader :account_number, :account_type, :cash_available, \
7
+ :cash_margin_value, :dividend_balance, :equity_value, :long_liquid_value, \
8
+ :long_market_value, :market_value, :net_value, :option_requirement, \
9
+ :prev_cash_available, :prev_maintenance_call, :short_liquid_value, \
10
+ :short_market_value, :sweep, :accumFedAmt, :accumFedPrev, :bopmvEqty, \
11
+ :cashMgnCashAv, :optnAddtlReqAmt, :currType
12
+
13
+ def self.from_response(body={})
14
+ new(body[:balances] || body)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,76 @@
1
+ module Tradier
2
+ class Base
3
+
4
+ def self.from_response(body={})
5
+ new(body)
6
+ end
7
+
8
+ # Define methods that retrieve the value from an initialized instance variable Hash, using the attribute as a key
9
+ #
10
+ # @param attrs [Array, Set, Symbol]
11
+ def self.attr_reader(*attrs)
12
+ mod = Module.new do
13
+ attrs.each do |attribute|
14
+ define_method attribute do
15
+ @attrs[attribute.to_sym]
16
+ end
17
+ define_method "#{attribute}?" do
18
+ !!@attrs[attribute.to_sym]
19
+ end
20
+ end
21
+ end
22
+ const_set(:Attributes, mod)
23
+ include mod
24
+ end
25
+
26
+ # Initializes a new object
27
+ #
28
+ # @param attrs [Hash]
29
+ # @return [Tradier::Base]
30
+ def initialize(attrs={})
31
+ @attrs = attrs
32
+ end
33
+
34
+ # Fetches an attribute of an object using hash notation
35
+ #
36
+ # @param method [String, Symbol] Message to send to the object
37
+ def [](method)
38
+ send(method.to_sym)
39
+ rescue NoMethodError
40
+ nil
41
+ end
42
+
43
+ # Retrieve the attributes of an object
44
+ #
45
+ # @return [Hash]
46
+ def attrs
47
+ @attrs
48
+ end
49
+ alias to_hash attrs
50
+
51
+ # Update the attributes of an object
52
+ #
53
+ # @param attrs [Hash]
54
+ # @return [Tradier::Base]
55
+ def update(attrs)
56
+ @attrs.update(attrs)
57
+ self
58
+ end
59
+
60
+ protected
61
+
62
+ # @param attr [Symbol]
63
+ # @param other [Tradier::Base]
64
+ # @return [Boolean]
65
+ def attr_equal(attr, other)
66
+ self.class == other.class && !other.send(attr).nil? && send(attr) == other.send(attr)
67
+ end
68
+
69
+ # @param other [Tradier::Base]
70
+ # @return [Boolean]
71
+ def attrs_equal(other)
72
+ self.class == other.class && !other.attrs.empty? && attrs == other.attrs
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,23 @@
1
+ require 'tradier/base'
2
+
3
+ module Tradier
4
+ class Calendar < Tradier::Base
5
+
6
+ # Create a new [Tradier::Calendar] instance from a response
7
+ #
8
+ # @param body [Hash]
9
+ # @return [Tradier::Calendar]
10
+ def self.from_response(body={})
11
+ new(body)
12
+ end
13
+
14
+ def month
15
+ @_month ||= @attrs[:calendar][:days][:month]
16
+ end
17
+
18
+ def year
19
+ @_year ||= @attrs[:calendar][:days][:year]
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,89 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'multi_json'
4
+ require 'tradier/api/accounts'
5
+ require 'tradier/api/markets'
6
+ require 'tradier/api/orders'
7
+ require 'tradier/api/watchlists'
8
+ require 'tradier/configurable'
9
+ require 'tradier/error/client_error'
10
+ require 'tradier/error/decode_error'
11
+
12
+ module Tradier
13
+ # Wrapper for the Tradier REST API
14
+ class Client
15
+ include Tradier::API::Accounts
16
+ include Tradier::API::Markets
17
+ include Tradier::API::Orders
18
+ include Tradier::API::Watchlists
19
+ include Tradier::Configurable
20
+
21
+ # Initializes a new Client object
22
+ #
23
+ # @param options [Hash]
24
+ # @return [Tradier::Client]
25
+ def initialize(options={})
26
+ Tradier::Configurable.keys.each do |key|
27
+ instance_variable_set(:"@#{key}", options[key] || Tradier.instance_variable_get(:"@#{key}"))
28
+ end
29
+ end
30
+
31
+ # Perform an HTTP DELETE request
32
+ def delete(path, params={})
33
+ request(:delete, path, params)
34
+ end
35
+
36
+ # Perform an HTTP GET request
37
+ def get(path, params={})
38
+ request(:get, path, params)
39
+ end
40
+
41
+ # Perform an HTTP POST request
42
+ def post(path, params={})
43
+ request(:post, path, params)
44
+ end
45
+
46
+ # Perform an HTTP PUT request
47
+ def put(path, params={})
48
+ request(:put, path, params)
49
+ end
50
+
51
+ private
52
+
53
+ # Returns a proc that can be used to setup the Faraday::Request headers
54
+ #
55
+ # @param method [Symbol]
56
+ # @param path [String]
57
+ # @param params [Hash]
58
+ # @return [Proc]
59
+ def request_setup(method, path, params)
60
+ Proc.new do |request|
61
+ request.headers[:authorization] = "Bearer #{@access_token}"
62
+ end
63
+ end
64
+
65
+ def request(method, request_path, params={}, signature_params=params)
66
+ request_setup = request_setup(method, versioned_path(request_path), params)
67
+ connection.send(method.to_sym, versioned_path(request_path), params, &request_setup).env
68
+ rescue Faraday::Error::ClientError
69
+ raise Tradier::Error::ClientError
70
+ rescue MultiJson::DecodeError
71
+ raise Tradier::Error::DecodeError
72
+ end
73
+
74
+ # Returns a Faraday::Connection object
75
+ #
76
+ # @return [Faraday::Connection]
77
+ def connection
78
+ @connection ||= begin
79
+ connection_options = {:builder => @middleware}
80
+ Faraday.new(@endpoint, @connection_options.merge(connection_options))
81
+ end
82
+ end
83
+
84
+ def versioned_path(request_path)
85
+ @version + request_path
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,21 @@
1
+ require 'tradier/base'
2
+
3
+ module Tradier
4
+ class Clock < Tradier::Base
5
+
6
+ attr_reader :description, :next_change, :next_state, :state
7
+
8
+ def date
9
+ return unless @date || @attrs[:date]
10
+
11
+ @date ||= Date.parse(@attrs[:date])
12
+ end
13
+
14
+ def time
15
+ return unless @time || @attrs[:timestamp]
16
+
17
+ @time ||= Time.at(@attrs[:timestamp].to_i)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,76 @@
1
+ require 'forwardable'
2
+ require 'tradier/error/configuration_error'
3
+
4
+ module Tradier
5
+ module Configurable
6
+ extend Forwardable
7
+ attr_writer :access_token
8
+ attr_accessor :endpoint, :connection_options, :middleware, :version
9
+ def_delegator :options, :hash
10
+
11
+ class << self
12
+
13
+ def keys
14
+ @keys ||= [
15
+ :access_token,
16
+ :endpoint,
17
+ :version,
18
+ :connection_options,
19
+ :middleware
20
+ ]
21
+ end
22
+
23
+ end
24
+
25
+ # Convenience method to allow configuration options to be set in a block
26
+ #
27
+ # @raise [Tradier::Error::ConfigurationError] Error is raised when supplied
28
+ # Tradier credentials are not a String or Symbol.
29
+ def configure
30
+ yield self
31
+ validate_credentials!
32
+ self
33
+ end
34
+
35
+ # @return [Boolean]
36
+ def credentials?
37
+ credentials.values.all?
38
+ end
39
+
40
+ def reset!
41
+ Tradier::Configurable.keys.each do |key|
42
+ instance_variable_set(:"@#{key}", Tradier::Default.options[key])
43
+ end
44
+ self
45
+ end
46
+ alias setup reset!
47
+
48
+ private
49
+
50
+ # @return [Hash]
51
+ def credentials
52
+ { :access_token => @access_token }
53
+ end
54
+
55
+ # @return [Hash]
56
+ def options
57
+ Hash[Tradier::Configurable.keys.map{|key| [key, instance_variable_get(:"@#{key}")]}]
58
+ end
59
+
60
+ # Ensures that all credentials set during configuration are of a
61
+ # valid type. Valid types are String and Symbol.
62
+ #
63
+ # @raise [Tradier::Error::ConfigurationError] Error is raised when
64
+ # supplied Tradier credentials are not a String or Symbol.
65
+ def validate_credentials!
66
+ credentials.each do |credential, value|
67
+ next if value.nil?
68
+
69
+ unless value.is_a?(String) || value.is_a?(::Symbol)
70
+ raise(Error::ConfigurationError, "Invalid #{credential} specified: #{value} must be a string or symbol.")
71
+ end
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,9 @@
1
+ require 'celluloid/autostart'
2
+
3
+ module Enumerable
4
+ # Simple parallel map using Celluloid::Futures
5
+ def pmap(&block)
6
+ futures = map { |elem| Celluloid::Future.new(elem, &block) }
7
+ futures.map { |future| future.value }
8
+ end
9
+ end
@@ -0,0 +1,75 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'tradier/configurable'
4
+ require 'tradier/error/client_error'
5
+ require 'tradier/error/server_error'
6
+ require 'tradier/response/parse_json'
7
+ require 'tradier/response/raise_error'
8
+ require 'tradier/version'
9
+
10
+ module Tradier
11
+ module Default
12
+ ENDPOINT = 'https://api.tradier.com' unless defined? Tradier::Default::ENDPOINT
13
+
14
+ VERSION = 'v1' unless defined?(Tradier::Default::VERSION)
15
+
16
+ CONNECTION_OPTIONS = {
17
+ :headers => {
18
+ :accept => 'application/json',
19
+ :user_agent => "Tradier Ruby Gem #{Tradier::VERSION}",
20
+ },
21
+ :request => {
22
+ :open_timeout => 5,
23
+ :timeout => 10,
24
+ }
25
+ } unless defined? Tradier::Default::CONNECTION_OPTIONS
26
+
27
+ MIDDLEWARE = Faraday::Builder.new do |builder|
28
+ # Convert request params to "www-form-urlencoded"
29
+ builder.use Faraday::Request::UrlEncoded
30
+ # Parse JSON response bodies using MultiJson
31
+ builder.use Tradier::Response::ParseJson
32
+ # # Handle 4xx server responses
33
+ builder.use Tradier::Response::RaiseError, Tradier::Error::ClientError
34
+ # # Handle 5xx server responses
35
+ builder.use Tradier::Response::RaiseError, Tradier::Error::ServerError
36
+ # Set Faraday's HTTP adapter
37
+ builder.adapter Faraday.default_adapter
38
+ end unless defined? Tradier::Default::MIDDLEWARE
39
+
40
+ class << self
41
+
42
+ # @return [Hash]
43
+ def options
44
+ Hash[Tradier::Configurable.keys.map{|key| [key, send(key)]}]
45
+ end
46
+
47
+ # @return [String]
48
+ def access_token
49
+ ENV['TRADIER_ACCESS_TOKEN']
50
+ end
51
+
52
+ # @return [String]
53
+ def endpoint
54
+ ENDPOINT
55
+ end
56
+
57
+ def version
58
+ VERSION
59
+ end
60
+
61
+ def connection_options
62
+ CONNECTION_OPTIONS
63
+ end
64
+
65
+ # @note Faraday's middleware stack implementation is comparable to that of Rack middleware. The order of middleware is important: the first middleware on the list wraps all others, while the last middleware is the innermost one.
66
+ # @see https://github.com/technoweenie/faraday#advanced-middleware-usage
67
+ # @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
68
+ # @return [Faraday::Builder]
69
+ def middleware
70
+ MIDDLEWARE
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,34 @@
1
+ module Tradier
2
+ # Custom error class for rescuing from all Tradier errors
3
+ class Error < StandardError
4
+ attr_reader :wrapped_exception
5
+
6
+ # @return [Hash]
7
+ def self.errors
8
+ @errors ||= descendants.each_with_object({}) do |klass, hash|
9
+ hash[klass::HTTP_STATUS_CODE] = klass if defined?(klass::HTTP_STATUS_CODE)
10
+ hash.update(klass.errors)
11
+ end
12
+ end
13
+
14
+ # @return [Array]
15
+ def self.descendants
16
+ ObjectSpace.each_object(::Class).select{|klass| klass < self}
17
+ end
18
+
19
+ # Initializes a new Error object
20
+ #
21
+ # @param exception [Exception, String]
22
+ # @param response_headers [Hash]
23
+ # @return [Tradier::Error]
24
+ def initialize(exception=$!, response_headers={})
25
+ @wrapped_exception = exception
26
+ exception.respond_to?(:backtrace) ? super(exception.message) : super(exception.to_s)
27
+ end
28
+
29
+ def backtrace
30
+ @wrapped_exception.respond_to?(:backtrace) ? @wrapped_exception.backtrace : super
31
+ end
32
+
33
+ end
34
+ 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 502
6
+ class BadGateway < Tradier::Error::ServerError
7
+ HTTP_STATUS_CODE = 502
8
+ MESSAGE = "Tradier is down or being upgraded."
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 400
6
+ class BadRequest < Tradier::Error::ClientError
7
+ HTTP_STATUS_CODE = 400
8
+ end
9
+ end
10
+ end