tradier 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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