schwab-api-ruby 2.1.0 → 2.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2aa249e30dc900c19f003e9085db5fe33907a69156e8eef47cd669bd09364d7
4
- data.tar.gz: 298dc7d7330942e8c9f89189e3552ca165038616f8a825873cc0ce227f37fff4
3
+ metadata.gz: be363423988093dfb91101597addfe4c045bc1433123b1ca2eec384592f5445f
4
+ data.tar.gz: 6e344be9f638a121e8198d1dd473f99738d7cd50cbd2e23d4d132c2acffc9551
5
5
  SHA512:
6
- metadata.gz: 8807115e742d49e4eadbbb87ace49fcae2875643ca029b62788c72189276d0e5477cdef567c0a83ab00fabb8512a5605573ca5f56d0051805daeda06ae5bd55f
7
- data.tar.gz: d87fd1a1704c794cf60e7eae06026f7fa79098fb17a36af3b6fd0af0d14a0ede142b2640d019203b9d17e9d21906f4468c6675f6509e23a5349ece994608eef9
6
+ metadata.gz: d37d7ae1dfb667ba753a29042332416a6b832216ae3516f9e8e2927f3ff3719d4c85d673ab127fd6636cc89859ba8db2994a576a0d5ad25998062e218336bfc4
7
+ data.tar.gz: 5985f75a67f7f75c118b20f68de6dc839eb8183cfa8f149de27f80d69d7d9801bb7510d0ee175548b329c9953282e7722f2fed737470c2b6cdbd7a672fbd555c
data/CHANGELOG.md CHANGED
@@ -1,8 +1,11 @@
1
+ Version 2.2.0
2
+ - Add new methods: `get_account_numbers`, `get_accounts`, `get_instrument_by_cusip`, `get_transactions`
3
+
1
4
  Version 2.1.0
2
5
  - Enable Instrument endpoint with "projection" param so you can query for fundamentals or other data about the ticker
3
6
 
4
7
  Version 2.0.0
5
- - This is version 2.0.0 becuase it's a continuation of the TDAmeritradeAPI gem.
8
+ - This is version 2.0.0 because it's a continuation of the TDAmeritradeAPI gem.
6
9
 
7
10
  Version 1.3.0.20210215
8
11
  - Added tracking of token expiration dates to instance vars
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # TD Ameritrade API gem for Ruby
1
+ # Schwab API (formerly TD Ameritrade API) gem for Ruby
2
2
 
3
3
  This is a gem for connecting to the OAuth/JSON-based Schwab API released in 2018. Go to
4
4
  https://beta-developer.schwab.com/ to create your OAuth application login and view the official documentation.
@@ -42,11 +42,74 @@ The official API is documented [here](https://developer.tdameritrade.com/apis).
42
42
  following functionality. If you would like to expand its functionality, then please submit a pull request.
43
43
 
44
44
  - [x] Authentication
45
- - [ ] Accounts and Trading
46
- - [ ] Instruments
45
+ - [X] Accounts and Trading
46
+ - [X] Instruments
47
47
  - [x] Price History
48
48
  - [x] Real-time Quotes
49
49
 
50
+ The following functions correspond to the Schwab Individual Trader API specification found in the online docs:
51
+ * get_accounts(fields=nil)
52
+ * get_account_numbers
53
+ * get_instrument(symbol, projection)
54
+ * get_instrument_by_cusip(cusip)
55
+ * get_price_history(symbol, **options)
56
+ * get_quotes(symbols, **options)
57
+ * get_transactions(...)
58
+
59
+ # Example usage
60
+
61
+ ```ruby
62
+ client = Schwab::Client.new(
63
+ client_id: ENV.fetch('SCHWAB_CLIENT_ID'),
64
+ secret: ENV.fetch('SCHWAB_SECRET'),
65
+ redirect_uri: ENV.fetch('SCHWAB_REDIRECT_URI'),
66
+ access_token: '<access token you received after running the OAuth authentication step>',
67
+ refresh_token: '<access token you received after running the OAuth authentication step>',
68
+ access_token_expires_at:,
69
+ refresh_token_expires_at:
70
+ )
71
+
72
+ symbols = %w[SCHW HOOD MS]
73
+ result = client.get_quotes(symbols, fields: :quote)
74
+ #=> {"SCHW" =>
75
+ # {"assetMainType" => "EQUITY",
76
+ # "assetSubType" => "COE",
77
+ # "quoteType" => "NBBO",
78
+ # "realtime" => true,
79
+ # "ssid" => 1516105793,
80
+ # "symbol" => "SCHW",
81
+ # "quote" =>
82
+ # {"52WeekHigh" => 93.35,
83
+ # "52WeekLow" => 61.01,
84
+ # "askMICId" => "ARCX",
85
+ # "askPrice" => 92.0,
86
+ # "askSize" => 1,
87
+ # "askTime" => 1752276764663,
88
+ # "bidMICId" => "ARCX",
89
+ # "bidPrice" => 91.5,
90
+ # "bidSize" => 1,
91
+ # "bidTime" => 1752278152374,
92
+ # "closePrice" => 93.04,
93
+ # "highPrice" => 92.99,
94
+ # "lastMICId" => "ARCX",
95
+ # "lastPrice" => 91.8,
96
+ # "lastSize" => 5,
97
+ # "lowPrice" => 91.825,
98
+ # "mark" => 91.97,
99
+ # "markChange" => -1.07,
100
+ # "markPercentChange" => -1.15004299,
101
+ # "netChange" => -1.24,
102
+ # "netPercentChange" => -1.3327601,
103
+ # "openPrice" => 92.53,
104
+ # "postMarketChange" => -0.17,
105
+ # "postMarketPercentChange" => -0.18484288,
106
+ # "quoteTime" => 1752278152374,
107
+ # "securityStatus" => "Normal",
108
+ # "totalVolume" => 6815842,
109
+ # "tradeTime" => 1752277607998}}}
110
+ ```
111
+
112
+
50
113
  ## Contributions
51
114
 
52
115
  If you would like to make a contribution, please submit a pull request to the original branch. Feel free to email me Winston Kotzan
@@ -62,4 +125,4 @@ Please open an issue on Github if you have any problems or questions.
62
125
 
63
126
  ## Release Notes
64
127
 
65
- See CHANGELOG.md
128
+ See CHANGELOG.md
data/lib/schwab/client.rb CHANGED
@@ -2,9 +2,12 @@ require 'schwab/authentication'
2
2
  require 'schwab/client'
3
3
  require 'schwab/error'
4
4
  require 'schwab/version'
5
+ require 'schwab/operations/get_accounts'
6
+ require 'schwab/operations/get_account_numbers'
5
7
  require 'schwab/operations/get_instrument'
6
8
  require 'schwab/operations/get_price_history'
7
9
  require 'schwab/operations/get_quotes'
10
+ require 'schwab/operations/get_transactions'
8
11
 
9
12
  module Schwab
10
13
  class Client
@@ -21,10 +24,22 @@ module Schwab
21
24
  @redirect_uri = args[:redirect_uri] || raise_error('redirect_uri is required!')
22
25
  end
23
26
 
27
+ def get_accounts(fields=nil)
28
+ Operations::GetAccounts.new(self).call(fields:)
29
+ end
30
+
31
+ def get_account_numbers
32
+ Operations::GetAccountNumbers.new(self).call
33
+ end
34
+
24
35
  def get_instrument(symbol, projection)
25
36
  Operations::GetInstrument.new(self).call(symbol, projection:)
26
37
  end
27
38
 
39
+ def get_instrument_by_cusip(cusip)
40
+ Operations::GetInstrumentByCusip.new(self).call(cusip)
41
+ end
42
+
28
43
  def get_price_history(symbol, **options)
29
44
  Operations::GetPriceHistory.new(self).call(symbol, **options)
30
45
  end
@@ -33,5 +48,8 @@ module Schwab
33
48
  Operations::GetQuotes.new(self).call(symbols:, **options)
34
49
  end
35
50
 
51
+ def get_transactions(...)
52
+ Operations::GetTransactions.new(self).call(...)
53
+ end
36
54
  end
37
55
  end
data/lib/schwab/error.rb CHANGED
@@ -8,6 +8,9 @@ module Schwab
8
8
  class NotAuthorizedError < StandardError
9
9
  end
10
10
 
11
+ class BadRequestError < StandardError
12
+ end
13
+
11
14
  module Error
12
15
  module_function
13
16
 
@@ -27,6 +27,8 @@ module Schwab
27
27
  raise Schwab::RateLimitError.new(response.body)
28
28
  elsif response.code == 401
29
29
  raise Schwab::NotAuthorizedError.new(response.body)
30
+ elsif response.code == 404
31
+ raise Schwab::BadRequestError.new(response.body)
30
32
  end
31
33
 
32
34
  error_message = response['errors'].to_s
@@ -41,7 +43,7 @@ module Schwab
41
43
  raise ArgumentError unless response.is_a?(HTTParty::Response)
42
44
  handle_api_error(response) unless response_success?(response)
43
45
 
44
- response.to_h
46
+ JSON.parse(response.body)
45
47
  end
46
48
 
47
49
  def perform_api_get_request(url: , query: nil)
@@ -0,0 +1,11 @@
1
+ require 'schwab/operations/base_operation'
2
+
3
+ module Schwab; module Operations
4
+ class GetAccountNumbers < BaseOperation
5
+
6
+ def call
7
+ perform_api_get_request(url: 'https://api.schwabapi.com/trader/v1/accounts/accountNumbers')
8
+ end
9
+
10
+ end
11
+ end; end
@@ -0,0 +1,106 @@
1
+ require 'schwab/operations/base_operation'
2
+
3
+ module Schwab; module Operations
4
+ class GetAccounts < BaseOperation
5
+
6
+ # Example Response:
7
+ # [
8
+ # {
9
+ # "securitiesAccount": {
10
+ # "type": "CASH",
11
+ # "accountNumber": "<account1>",
12
+ # "roundTrips": 0,
13
+ # "isDayTrader": false,
14
+ # "isClosingOnlyRestricted": false,
15
+ # "pfcbFlag": false,
16
+ # "positions": [
17
+ # {
18
+ # "shortQuantity": 0,
19
+ # "averagePrice": 13.4,
20
+ # "currentDayProfitLoss": -31.59,
21
+ # "currentDayProfitLossPercentage": -5.44,
22
+ # "longQuantity": 39,
23
+ # "settledLongQuantity": 39,
24
+ # "settledShortQuantity": 0,
25
+ # "instrument": {
26
+ # "assetType": "EQUITY",
27
+ # "cusip": "83406F102",
28
+ # "symbol": "SOFI",
29
+ # "netChange": -0.8
30
+ # },
31
+ # "marketValue": 549.51,
32
+ # "maintenanceRequirement": 0,
33
+ # "averageLongPrice": 13.4,
34
+ # "taxLotAverageLongPrice": 13.4,
35
+ # "longOpenProfitLoss": 26.91,
36
+ # "previousSessionLongQuantity": 39,
37
+ # "currentDayCost": 0
38
+ # },
39
+ # ],
40
+ # "initialBalances": {
41
+ # "accruedInterest": 0,
42
+ # "cashAvailableForTrading": 1561.98,
43
+ # "cashAvailableForWithdrawal": 1561.98,
44
+ # "cashBalance": 1561.98,
45
+ # "bondValue": 0,
46
+ # "cashReceipts": 0,
47
+ # "liquidationValue": 8566.46,
48
+ # "longOptionMarketValue": 0,
49
+ # "longStockValue": 7004.48,
50
+ # "moneyMarketFund": 0,
51
+ # "mutualFundValue": 1561.98,
52
+ # "shortOptionMarketValue": 0,
53
+ # "shortStockValue": 0,
54
+ # "isInCall": false,
55
+ # "unsettledCash": 0,
56
+ # "cashDebitCallValue": 0,
57
+ # "pendingDeposits": 0,
58
+ # "accountValue": 8566.46
59
+ # },
60
+ # "currentBalances": {
61
+ # "accruedInterest": 0,
62
+ # "cashBalance": 1561.98,
63
+ # "cashReceipts": 0,
64
+ # "longOptionMarketValue": 0,
65
+ # "liquidationValue": 8566.46,
66
+ # "longMarketValue": 7004.48,
67
+ # "moneyMarketFund": 0,
68
+ # "savings": 0,
69
+ # "shortMarketValue": 0,
70
+ # "pendingDeposits": 0,
71
+ # "mutualFundValue": 0,
72
+ # "bondValue": 0,
73
+ # "shortOptionMarketValue": 0,
74
+ # "cashAvailableForTrading": 1561.98,
75
+ # "cashAvailableForWithdrawal": 1561.98,
76
+ # "cashCall": 0,
77
+ # "longNonMarginableMarketValue": 1561.98,
78
+ # "totalCash": 1561.98,
79
+ # "cashDebitCallValue": 0,
80
+ # "unsettledCash": 0
81
+ # },
82
+ # "projectedBalances": {
83
+ # "cashAvailableForTrading": 1561.98,
84
+ # "cashAvailableForWithdrawal": 1561.98
85
+ # }
86
+ # },
87
+ # "aggregatedBalance": {
88
+ # "currentLiquidationValue": 8566.46,
89
+ # "liquidationValue": 8566.46
90
+ # }
91
+ # },
92
+ def call(fields: 'positions')
93
+ params = {
94
+ fields:,
95
+ }
96
+
97
+ response = perform_api_get_request(
98
+ url: 'https://api.schwabapi.com/trader/v1/accounts',
99
+ query: params,
100
+ )
101
+
102
+ response
103
+ end
104
+
105
+ end
106
+ end; end
@@ -3,7 +3,11 @@ require 'schwab/operations/base_operation'
3
3
  module Schwab; module Operations
4
4
  class GetInstrument < BaseOperation
5
5
 
6
+ PROJECTIONS = %w[symbol-search symbol-regex desc-search desc-regex search fundamental]
7
+
6
8
  def call(symbols, projection:)
9
+ raise ArgumentError, "projection must be in #{PROJECTIONS}" unless PROJECTIONS.include?(projection)
10
+
7
11
  symbol = symbols.is_a?(Array) ? symbols.join(',') : symbols
8
12
 
9
13
  params = {
@@ -0,0 +1,11 @@
1
+ require 'schwab/operations/base_operation'
2
+
3
+ module Schwab; module Operations
4
+ class GetInstrumentByCusip < BaseOperation
5
+
6
+ def call(cusip)
7
+ perform_api_get_request(url: "https://api.schwabapi.com/marketdata/v1/instruments/#{cusip}")
8
+ end
9
+
10
+ end
11
+ end; end
@@ -0,0 +1,44 @@
1
+ require 'schwab/operations/base_operation'
2
+
3
+ module Schwab; module Operations
4
+ class GetTransactions < BaseOperation
5
+
6
+ TYPES = %w[
7
+ TRADE
8
+ RECEIVE_AND_DELIVER
9
+ DIVIDEND_OR_INTEREST
10
+ ACH_RECEIPT
11
+ ACH_DISBURSEMENT
12
+ CASH_RECEIPT
13
+ CASH_DISBURSEMENT
14
+ ELECTRONIC_FUND
15
+ WIRE_OUT
16
+ WIRE_IN
17
+ JOURNAL
18
+ MEMORANDUM
19
+ MARGIN_CALL
20
+ MONEY_MARKET
21
+ SMA_ADJUSTMENT
22
+ ]
23
+
24
+ def call(account_number:, start_date:, end_date:, types:, symbol: nil)
25
+ raise(ArgumentError, "types must be one of #{TYPES}") unless TYPES.include?(types)
26
+ raise(ArgumentError, "start_date and end_date must be Time") unless [start_date, end_date].all?(Time)
27
+
28
+ params = {
29
+ startDate: start_date.utc.iso8601,
30
+ endDate: end_date.utc.iso8601,
31
+ types:,
32
+ }
33
+ params.merge!(symbol:) if symbol
34
+
35
+ response = perform_api_get_request(
36
+ url: "https://api.schwabapi.com/trader/v1/accounts/#{account_number}/transactions",
37
+ query: params,
38
+ )
39
+
40
+ response
41
+ end
42
+
43
+ end
44
+ end; end
@@ -1,3 +1,3 @@
1
1
  module Schwab
2
- VERSION = '2.1.0'
2
+ VERSION = '2.2.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schwab-api-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Winston Kotzan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-15 00:00:00.000000000 Z
11
+ date: 2025-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -114,9 +114,13 @@ files:
114
114
  - lib/schwab/client.rb
115
115
  - lib/schwab/error.rb
116
116
  - lib/schwab/operations/base_operation.rb
117
+ - lib/schwab/operations/get_account_numbers.rb
118
+ - lib/schwab/operations/get_accounts.rb
117
119
  - lib/schwab/operations/get_instrument.rb
120
+ - lib/schwab/operations/get_instrument_by_cusip.rb
118
121
  - lib/schwab/operations/get_price_history.rb
119
122
  - lib/schwab/operations/get_quotes.rb
123
+ - lib/schwab/operations/get_transactions.rb
120
124
  - lib/schwab/operations/support/build_watchlist_items.rb
121
125
  - lib/schwab/util.rb
122
126
  - lib/schwab/version.rb