schwab-api-ruby 2.0.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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +32 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/Rakefile +8 -0
- data/lib/schwab/authentication.rb +87 -0
- data/lib/schwab/client.rb +37 -0
- data/lib/schwab/error.rb +20 -0
- data/lib/schwab/operations/base_operation.rb +67 -0
- data/lib/schwab/operations/get_instrument_fundamentals.rb +23 -0
- data/lib/schwab/operations/get_price_history.rb +54 -0
- data/lib/schwab/operations/get_quotes.rb +23 -0
- data/lib/schwab/operations/support/build_watchlist_items.rb +22 -0
- data/lib/schwab/util.rb +12 -0
- data/lib/schwab/version.rb +3 -0
- data/lib/schwab-api-ruby.rb +1 -0
- data/lib/schwab.rb +7 -0
- data/schwab-api-ruby.gemspec +29 -0
- data/spec/spec_helper.rb +110 -0
- data/spec/support/authenticated_client.rb +12 -0
- data/spec/support/spec/mocks/mock_get_instrument_fundamentals.rb +18 -0
- data/spec/support/spec/mocks/mock_get_price_history.rb +18 -0
- data/spec/support/spec/mocks/mock_get_quotes.rb +18 -0
- data/spec/support/spec/mocks/mock_watchlists.rb +44 -0
- data/spec/support/spec/mocks/tdameritrade_api_mock_base.rb +22 -0
- data/spec/support/webmock_off.rb +7 -0
- data/spec/tdameritrade/operations/error_spec.rb +75 -0
- data/spec/tdameritrade/operations/get_instrument_fundamentals_spec.rb +182 -0
- data/spec/tdameritrade/operations/get_price_history_spec.rb +160 -0
- data/spec/tdameritrade/operations/get_quotes_spec.rb +351 -0
- metadata +171 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b9659741b90ac05d0426da9e7ff4c6d3a03a205deeb2901d3ec97b9a8dd39165
|
4
|
+
data.tar.gz: 5d27d65ef5d09158b61d41d621db97869cd137d11912d339fa0ff3cbea71258e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3f5c84e4f946111c19adc93b4d73a2d3c4c45edfd397b9f6ec3ee05e1632f18695d0c311e6fd4d07442f2bd0d7604fda875056c09a55c496b721a4bcfddc0919
|
7
|
+
data.tar.gz: a9ae1f55fd29ff52d032463b8e0d28ab50f51576957e78d98aefa3c7c9a15ee9e6a020dac4fec5d59d24183c24d448665a5019a6920206a45ddfd2e8321aef4a
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
.idea
|
7
|
+
.DS_STORE
|
8
|
+
spec/test_data/sample_stream_rspec_test.binary
|
9
|
+
Gemfile.lock
|
10
|
+
InstalledFiles
|
11
|
+
_yardoc
|
12
|
+
coverage
|
13
|
+
doc/
|
14
|
+
lib/bundler/man
|
15
|
+
pkg
|
16
|
+
rdoc
|
17
|
+
spec/reports
|
18
|
+
spec/test_data/sample_stream_archives
|
19
|
+
test/tmp
|
20
|
+
test/version_tmp
|
21
|
+
tmp
|
22
|
+
Gemfile.lock
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
Version 2.0.0
|
2
|
+
- This is version 2.0.0 becuase it's a continuation of the TDAmeritradeAPI gem.
|
3
|
+
|
4
|
+
Version 1.3.0.20210215
|
5
|
+
- Added tracking of token expiration dates to instance vars
|
6
|
+
- API request results are now returned as a Hashie::Mash to make 'quotes' or :symbol reference of values indifferent
|
7
|
+
- Corrected incorrect date submission in create watchlist operation
|
8
|
+
|
9
|
+
Verion 1.2.0.20190915
|
10
|
+
- (Breaking change) Make get_price_history return datetime stamp as Ruby Time vs milliseconds since epoch
|
11
|
+
|
12
|
+
Verion 1.1.1.20190915
|
13
|
+
- Fix incorrect formatting when using startdate and enddate in get_price_history
|
14
|
+
|
15
|
+
Version 1.1.0 8/28/19
|
16
|
+
- Enhance error messages for rate limit and invalid token
|
17
|
+
|
18
|
+
Version 1.0 8/27/19
|
19
|
+
- Change structure internally to use dependency injection of oprations
|
20
|
+
- Price history feature
|
21
|
+
- Real time quotes feature
|
22
|
+
- Added RSpec test coverage
|
23
|
+
|
24
|
+
Version 0.2.alpha
|
25
|
+
- Basic Watchlist retrieval and modification
|
26
|
+
|
27
|
+
Version 0.1.alpha - 11/29/2018
|
28
|
+
- Initial, very basic release
|
29
|
+
- Authentication: refresh access token by refresh_token
|
30
|
+
- Instruments: get fundamentals
|
31
|
+
|
32
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 wkotzan
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# TD Ameritrade API gem for Ruby
|
2
|
+
|
3
|
+
This is a gem for connecting to the OAuth/JSON-based Schwab API released in 2018. Go to
|
4
|
+
https://beta-developer.schwab.com/ to create your OAuth application login and view the official documentation.
|
5
|
+
|
6
|
+
For a gem that allows you to connect to the older version of the TDAmeritrade API, go to
|
7
|
+
https://github.com/wakproductions/tdameritrade_api
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
In your Gemfile
|
12
|
+
|
13
|
+
`gem 'schwab-api-ruby', git: 'https://github.com/wakproductions/schwab-api-ruby.git'`
|
14
|
+
|
15
|
+
## Authenticating
|
16
|
+
|
17
|
+
Currently this gem is designed for local app authorization of the "Trader API - Individual". It is based on the
|
18
|
+
assumption that you will be using https://127.0.0.1 as the OAuth redirect_uri, as that is the only value that's
|
19
|
+
been tested and verified to work
|
20
|
+
|
21
|
+
## Basic Usage
|
22
|
+
|
23
|
+
```
|
24
|
+
client = SchwabAPI::Client.new(
|
25
|
+
client_id: <client_id>,
|
26
|
+
redirect_uri: 'https://127.0.0.1',
|
27
|
+
refresh_token: '<refresh_token>'
|
28
|
+
)
|
29
|
+
|
30
|
+
client.get_instrument_fundamentals('MSFT')
|
31
|
+
#=> {"MSFT"=>
|
32
|
+
{"fundamental"=>
|
33
|
+
{"symbol"=>"MSFT",
|
34
|
+
"high52"=>425.24,
|
35
|
+
"low52"=>340.33,
|
36
|
+
...
|
37
|
+
```
|
38
|
+
|
39
|
+
# Current State of Functionality
|
40
|
+
|
41
|
+
The official API is documented [here](https://developer.tdameritrade.com/apis). This gem currently implements the
|
42
|
+
following functionality. If you would like to expand its functionality, then please submit a pull request.
|
43
|
+
|
44
|
+
- [x] Authentication
|
45
|
+
- [ ] Accounts and Trading
|
46
|
+
- [ ] Instruments
|
47
|
+
- [x] Price History
|
48
|
+
- [x] Real-time Quotes
|
49
|
+
|
50
|
+
## Contributions
|
51
|
+
|
52
|
+
If you would like to make a contribution, please submit a pull request to the original branch. Feel free to email me Winston Kotzan
|
53
|
+
at wak@wakproductions.com with any feature requests, bug reports, or feedback.
|
54
|
+
|
55
|
+
#### Wish List
|
56
|
+
|
57
|
+
* Test Coverage in RSpec
|
58
|
+
|
59
|
+
## Support
|
60
|
+
|
61
|
+
Please open an issue on Github if you have any problems or questions.
|
62
|
+
|
63
|
+
## Release Notes
|
64
|
+
|
65
|
+
See CHANGELOG.md
|
data/Rakefile
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'schwab/util'
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
module Schwab
|
5
|
+
module Authentication
|
6
|
+
include Util
|
7
|
+
|
8
|
+
attr_reader :client_id, :secret, :redirect_uri, :authorization_code, :access_token, :refresh_token,
|
9
|
+
:access_token_expires_at, :refresh_token_expires_at
|
10
|
+
|
11
|
+
def authorization_header_token
|
12
|
+
client_id_and_secret = "#{client_id}:#{secret}"
|
13
|
+
Base64.strict_encode64(client_id_and_secret)
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_headers
|
17
|
+
{
|
18
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
19
|
+
'Authorization': "Basic #{authorization_header_token}"
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return value looks like:
|
24
|
+
# {"expires_in"=>1800,
|
25
|
+
# "token_type"=>"Bearer",
|
26
|
+
# "scope"=>"api",
|
27
|
+
# "refresh_token"=>"LGGun...",
|
28
|
+
# "access_token"=>"I0.b2F...@",
|
29
|
+
# "id_token"=>"eyJ0eXA..."}
|
30
|
+
def request_access_token(authorization_grant_code)
|
31
|
+
# headers = { 'Content-Type': 'application/x-www-form-urlencoded' } # turns out didn't need this
|
32
|
+
options = {
|
33
|
+
body: {
|
34
|
+
'grant_type': 'authorization_code',
|
35
|
+
'code': authorization_grant_code,
|
36
|
+
'redirect_uri': redirect_uri
|
37
|
+
},
|
38
|
+
headers: default_headers
|
39
|
+
}
|
40
|
+
|
41
|
+
response = HTTParty.post(
|
42
|
+
'https://api.schwabapi.com/v1/oauth/token',
|
43
|
+
options
|
44
|
+
)
|
45
|
+
|
46
|
+
unless response_success?(response)
|
47
|
+
raise Schwab::Error::SchwabAPIError.new(
|
48
|
+
"Unable to retrieve access tokens from API - #{response.code} - #{response.body}"
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
update_tokens(response)
|
53
|
+
response
|
54
|
+
end
|
55
|
+
|
56
|
+
def refresh_access_token
|
57
|
+
options = {
|
58
|
+
body: {
|
59
|
+
grant_type: 'refresh_token',
|
60
|
+
refresh_token: refresh_token,
|
61
|
+
},
|
62
|
+
headers: default_headers
|
63
|
+
}
|
64
|
+
|
65
|
+
response = HTTParty.post(
|
66
|
+
'https://api.schwabapi.com/v1/oauth/token',
|
67
|
+
options
|
68
|
+
)
|
69
|
+
|
70
|
+
update_tokens(response)
|
71
|
+
response
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def update_tokens(args={})
|
77
|
+
raise_error("#{args['error']} - #{args['error_description']}") if args.has_key?('error')
|
78
|
+
|
79
|
+
@access_token = args['access_token']
|
80
|
+
@refresh_token = args['refresh_token']
|
81
|
+
@access_token_expires_at = Time.now + args['expires_in'].to_i
|
82
|
+
@refresh_token_expires_at = 7.days.from_now
|
83
|
+
|
84
|
+
args
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'schwab/authentication'
|
2
|
+
require 'schwab/client'
|
3
|
+
require 'schwab/error'
|
4
|
+
require 'schwab/version'
|
5
|
+
# require 'schwab/operations/get_instrument_fundamentals'
|
6
|
+
require 'schwab/operations/get_price_history'
|
7
|
+
require 'schwab/operations/get_quotes'
|
8
|
+
|
9
|
+
module Schwab
|
10
|
+
class Client
|
11
|
+
include Schwab::Authentication
|
12
|
+
include Schwab::Error
|
13
|
+
|
14
|
+
def initialize(**args)
|
15
|
+
@access_token = args[:access_token]
|
16
|
+
@refresh_token = args[:refresh_token]
|
17
|
+
@access_token_expires_at = args[:access_token_expires_at]
|
18
|
+
@refresh_token_expires_at = args[:refresh_token_expires_at]
|
19
|
+
@client_id = args[:client_id] || raise_error('client_id is required!')
|
20
|
+
@secret = args[:secret] || raise_error('client_id is required!')
|
21
|
+
@redirect_uri = args[:redirect_uri] || raise_error('redirect_uri is required!')
|
22
|
+
end
|
23
|
+
|
24
|
+
# def get_instrument_fundamentals(symbol)
|
25
|
+
# Operations::GetInstrumentFundamentals.new(self).call(symbol)
|
26
|
+
# end
|
27
|
+
|
28
|
+
def get_price_history(symbol, **options)
|
29
|
+
Operations::GetPriceHistory.new(self).call(symbol, **options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_quotes(symbols, **options)
|
33
|
+
Operations::GetQuotes.new(self).call(symbols:, **options)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/schwab/error.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Schwab
|
2
|
+
class APIError < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class RateLimitError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
class NotAuthorizedError < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
module Error
|
12
|
+
module_function
|
13
|
+
|
14
|
+
def raise_error(message, klass=Schwab::APIError)
|
15
|
+
error = klass.new(message)
|
16
|
+
error.set_backtrace(caller)
|
17
|
+
raise error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'schwab/error'
|
2
|
+
require 'schwab/util'
|
3
|
+
|
4
|
+
module Schwab
|
5
|
+
module Operations
|
6
|
+
class BaseOperation
|
7
|
+
include Schwab::Error
|
8
|
+
include Util
|
9
|
+
|
10
|
+
HTTP_DEBUG_OUTPUT=ENV['DEBUG_OUTPUT'] # to make live testing easier
|
11
|
+
|
12
|
+
attr_reader :client
|
13
|
+
|
14
|
+
def initialize(client)
|
15
|
+
@client = client # inject dependency of client credentials
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def debug_output?
|
21
|
+
HTTP_DEBUG_OUTPUT.to_s == 'true'
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle_api_error(response)
|
25
|
+
# "Individual App's transactions per seconds restriction, please update to commercial apps for unrestricted tps"
|
26
|
+
if response.code == 429
|
27
|
+
raise Schwab::RateLimitError.new(response.body)
|
28
|
+
elsif response.code == 401
|
29
|
+
raise Schwab::NotAuthorizedError.new(response.body)
|
30
|
+
end
|
31
|
+
|
32
|
+
error_message = response['errors'].to_s
|
33
|
+
raise Schwab::APIError.new("#{response.code}: #{error_message}")
|
34
|
+
rescue JSON::ParserError
|
35
|
+
raise Schwab::APIError.new(
|
36
|
+
"Unable to parse error response from Schwab API: #{response.code} - #{response.body} | #{response}"
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_response(response)
|
41
|
+
raise ArgumentError unless response.is_a?(HTTParty::Response)
|
42
|
+
handle_api_error(response) unless response_success?(response)
|
43
|
+
|
44
|
+
response.to_h
|
45
|
+
end
|
46
|
+
|
47
|
+
def perform_api_get_request(url: , query: nil)
|
48
|
+
options = {
|
49
|
+
headers: {
|
50
|
+
'Authorization': "Bearer #{client.access_token}",
|
51
|
+
'Accept': "application/json"
|
52
|
+
}
|
53
|
+
}
|
54
|
+
options.merge!(query: query) if query
|
55
|
+
options.merge!(debug_output: $stdout) if debug_output?
|
56
|
+
|
57
|
+
response = HTTParty.get(
|
58
|
+
url,
|
59
|
+
options
|
60
|
+
)
|
61
|
+
|
62
|
+
parse_response(response)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# require 'schwab/operations/base_operation'
|
2
|
+
#
|
3
|
+
# module Schwab
|
4
|
+
# ; module Operations
|
5
|
+
# class GetInstrumentFundamentals < BaseOperation
|
6
|
+
#
|
7
|
+
# def call(symbol)
|
8
|
+
# params = {
|
9
|
+
# apikey: client.client_id,
|
10
|
+
# symbol: symbol,
|
11
|
+
# projection: 'fundamental'
|
12
|
+
# }
|
13
|
+
#
|
14
|
+
# response = perform_api_get_request(
|
15
|
+
# url: 'https://api.tdameritrade.com/v1/instruments',
|
16
|
+
# query: params,
|
17
|
+
# )
|
18
|
+
#
|
19
|
+
# parse_api_response(response)
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# end
|
23
|
+
# end; end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'schwab/operations/base_operation'
|
2
|
+
|
3
|
+
module Schwab; module Operations
|
4
|
+
class GetPriceHistory < BaseOperation
|
5
|
+
|
6
|
+
# Not used right now, but can be used later on for validation
|
7
|
+
FREQUENCY_TYPE=[:minute, :daily, :weekly, :monthly]
|
8
|
+
PERIOD_TYPE=[:day, :month, :year, :ytd]
|
9
|
+
|
10
|
+
def call(
|
11
|
+
symbol,
|
12
|
+
period_type: nil,
|
13
|
+
period: nil,
|
14
|
+
frequency_type: nil,
|
15
|
+
frequency: nil,
|
16
|
+
end_date: nil, # should be a Date
|
17
|
+
start_date: nil, # should be a Date
|
18
|
+
need_extended_hours_data: false
|
19
|
+
)
|
20
|
+
raise(ArgumentError, "Can't use period if using start_date/end_date") if start_date.present? && period.present?
|
21
|
+
|
22
|
+
params = {
|
23
|
+
needExtendedHoursData: need_extended_hours_data,
|
24
|
+
symbol: symbol
|
25
|
+
}
|
26
|
+
|
27
|
+
params.merge!(frequencyType: frequency_type) if frequency_type
|
28
|
+
params.merge!(frequency: frequency) if frequency
|
29
|
+
|
30
|
+
params.merge!(periodType: period_type) if period_type
|
31
|
+
params.merge!(period: period) if period
|
32
|
+
params.merge!(startDate: "#{start_date.strftime('%s')}000") if start_date
|
33
|
+
params.merge!(endDate: "#{end_date.strftime('%s')}000") if end_date
|
34
|
+
|
35
|
+
response = perform_api_get_request(
|
36
|
+
url: 'https://api.schwabapi.com/marketdata/v1/pricehistory',
|
37
|
+
query: params,
|
38
|
+
)
|
39
|
+
|
40
|
+
if response["candles"]
|
41
|
+
response["candles"].map do |candle|
|
42
|
+
if candle["datetime"].is_a? Numeric
|
43
|
+
candle["datetime"] = Time.at(candle["datetime"] / 1000)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
response
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
end
|
54
|
+
end; end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'schwab/operations/base_operation'
|
2
|
+
|
3
|
+
module Schwab; module Operations
|
4
|
+
class GetQuotes < BaseOperation
|
5
|
+
|
6
|
+
FIELDS = %i[quote fundamental]
|
7
|
+
|
8
|
+
def call(symbols: [], fields: nil)
|
9
|
+
raise(ArgumentError, 'fields must be :quote or :fundamental') if fields.present? && FIELDS.exclude?(fields)
|
10
|
+
|
11
|
+
params = {
|
12
|
+
symbols: symbols.join(','),
|
13
|
+
}
|
14
|
+
params.merge!(fields:) if fields.present?
|
15
|
+
|
16
|
+
perform_api_get_request(
|
17
|
+
url: "https://api.schwabapi.com/marketdata/v1/quotes",
|
18
|
+
query: params,
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end; end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Schwab
|
2
|
+
; module Operations; module Support
|
3
|
+
module BuildWatchlistItems
|
4
|
+
|
5
|
+
# This gem only supports EQUITY type, even though there is a lot more you can do with the API
|
6
|
+
def build_watchlist_items(symbol_list)
|
7
|
+
symbol_list.map do |symbol|
|
8
|
+
{
|
9
|
+
"quantity": 0,
|
10
|
+
"averagePrice": 0,
|
11
|
+
"commission": 0,
|
12
|
+
"purchasedDate": (Date.today).strftime('%Y-%m-%d'),
|
13
|
+
"instrument": {
|
14
|
+
"symbol": symbol,
|
15
|
+
"assetType": "EQUITY"
|
16
|
+
}
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end; end; end
|
data/lib/schwab/util.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'schwab'
|
data/lib/schwab.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'schwab/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "schwab-api-ruby"
|
8
|
+
spec.version = Schwab::VERSION
|
9
|
+
spec.authors = ["Winston Kotzan"]
|
10
|
+
spec.email = ["wak@wakproductions.com"]
|
11
|
+
spec.summary = %q{This is a simple gem for connecting to the Schwab OAuth API}
|
12
|
+
spec.description = "This is a gem for connecting to the OAuth/JSON-based Schwab API. See "
|
13
|
+
"https://beta-developer.schwab.com/ for the official documentation and to create your developer account."
|
14
|
+
spec.homepage = "https://github.com/wakproductions/schwab-api-ruby"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = [`git ls-files`.split($/)] + Dir["lib/**/*"]
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "bundler", ">= 2.0"
|
23
|
+
spec.add_dependency "rake"
|
24
|
+
spec.add_dependency "httparty", ">= 0.20"
|
25
|
+
|
26
|
+
spec.add_development_dependency "rspec", ">= 3.2"
|
27
|
+
spec.add_development_dependency "pry"
|
28
|
+
spec.add_development_dependency "webmock"
|
29
|
+
end
|