tdameritrade-api-ruby 1.3.0.20210215

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +2 -0
  4. data/CHANGELOG.md +29 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +84 -0
  8. data/Rakefile +8 -0
  9. data/lib/tdameritrade-api-ruby.rb +1 -0
  10. data/lib/tdameritrade.rb +9 -0
  11. data/lib/tdameritrade/authentication.rb +65 -0
  12. data/lib/tdameritrade/client.rb +56 -0
  13. data/lib/tdameritrade/error.rb +20 -0
  14. data/lib/tdameritrade/operations/base_operation.rb +36 -0
  15. data/lib/tdameritrade/operations/create_watchlist.rb +30 -0
  16. data/lib/tdameritrade/operations/get_instrument_fundamentals.rb +22 -0
  17. data/lib/tdameritrade/operations/get_price_history.rb +55 -0
  18. data/lib/tdameritrade/operations/get_quotes.rb +21 -0
  19. data/lib/tdameritrade/operations/get_watchlists.rb +12 -0
  20. data/lib/tdameritrade/operations/replace_watchlist.rb +33 -0
  21. data/lib/tdameritrade/operations/support/build_watchlist_items.rb +21 -0
  22. data/lib/tdameritrade/operations/update_watchlist.rb +33 -0
  23. data/lib/tdameritrade/util.rb +50 -0
  24. data/lib/tdameritrade/version.rb +3 -0
  25. data/spec/spec_helper.rb +110 -0
  26. data/spec/support/authenticated_client.rb +12 -0
  27. data/spec/support/spec/mocks/mock_get_instrument_fundamentals.rb +17 -0
  28. data/spec/support/spec/mocks/mock_get_price_history.rb +17 -0
  29. data/spec/support/spec/mocks/mock_get_quotes.rb +17 -0
  30. data/spec/support/spec/mocks/mock_watchlists.rb +43 -0
  31. data/spec/support/spec/mocks/tdameritrade_api_mock_base.rb +21 -0
  32. data/spec/support/webmock_off.rb +7 -0
  33. data/spec/tdameritrade/operations/create_watchlist_spec.rb +42 -0
  34. data/spec/tdameritrade/operations/error_spec.rb +75 -0
  35. data/spec/tdameritrade/operations/get_instrument_fundamentals_spec.rb +182 -0
  36. data/spec/tdameritrade/operations/get_price_history_spec.rb +160 -0
  37. data/spec/tdameritrade/operations/get_quotes_spec.rb +351 -0
  38. data/spec/tdameritrade/operations/get_watchlists_spec.rb +80 -0
  39. data/spec/tdameritrade/operations/replace_watchlist_spec.rb +45 -0
  40. data/spec/tdameritrade/operations/update_watchlist_spec.rb +45 -0
  41. data/tdameritrade-api-ruby.gemspec +33 -0
  42. metadata +253 -0
@@ -0,0 +1,351 @@
1
+ require 'spec_helper'
2
+ require 'support/authenticated_client'
3
+ require 'tdameritrade'
4
+
5
+ describe TDAmeritrade::Operations::GetQuotes do
6
+ include_context 'authenticated client'
7
+ # include_context 'webmock off'
8
+
9
+ context 'valid symbol' do
10
+ subject do
11
+ client.get_quotes(symbols)
12
+ end
13
+
14
+ let(:symbols) { %w(PG MSFT CVX) }
15
+
16
+ let!(:expected_request) do
17
+ TDAmeritrade::Spec::Mocks::MockGetQuotes.mock_find(
18
+ request: {
19
+ headers: { 'Authorization': "Bearer #{client.access_token}" },
20
+ query: {
21
+ apikey: client.client_id,
22
+ symbol: symbols.join(','),
23
+ }.map {|k,v| [k, v.to_s] }.to_h,
24
+ },
25
+ response: {
26
+ body: <<~RESPONSE
27
+ {
28
+ "PG": {
29
+ "assetType": "EQUITY",
30
+ "symbol": "PG",
31
+ "description": "Procter & Gamble Company (The) Common Stock",
32
+ "bidPrice": 120.55,
33
+ "bidSize": 500,
34
+ "bidId": "P",
35
+ "askPrice": 120.99,
36
+ "askSize": 500,
37
+ "askId": "P",
38
+ "lastPrice": 120.55,
39
+ "lastSize": 0,
40
+ "lastId": "N",
41
+ "openPrice": 119.99,
42
+ "highPrice": 121.05,
43
+ "lowPrice": 119.78,
44
+ "bidTick": " ",
45
+ "closePrice": 119.32,
46
+ "netChange": 1.23,
47
+ "totalVolume": 6996550,
48
+ "quoteTimeInLong": 1566950400020,
49
+ "tradeTimeInLong": 1566945034633,
50
+ "mark": 120.55,
51
+ "exchange": "n",
52
+ "exchangeName": "NYSE",
53
+ "marginable": true,
54
+ "shortable": true,
55
+ "volatility": 0.01,
56
+ "digits": 2,
57
+ "52WkHigh": 121.76,
58
+ "52WkLow": 78.49,
59
+ "nAV": 0.0,
60
+ "peRatio": 79.1467,
61
+ "divAmount": 2.9836,
62
+ "divYield": 2.5,
63
+ "divDate": "2019-07-18 00:00:00.000",
64
+ "securityStatus": "Normal",
65
+ "regularMarketLastPrice": 120.55,
66
+ "regularMarketLastSize": 12155,
67
+ "regularMarketNetChange": 1.23,
68
+ "regularMarketTradeTimeInLong": 1566945000012,
69
+ "netPercentChangeInDouble": 1.0308,
70
+ "markChangeInDouble": 1.23,
71
+ "markPercentChangeInDouble": 1.0308,
72
+ "regularMarketPercentChangeInDouble": 1.0308,
73
+ "delayed": false
74
+ },
75
+ "MSFT": {
76
+ "assetType": "EQUITY",
77
+ "symbol": "MSFT",
78
+ "description": "Microsoft Corporation - Common Stock",
79
+ "bidPrice": 135.87,
80
+ "bidSize": 500,
81
+ "bidId": "K",
82
+ "askPrice": 136.06,
83
+ "askSize": 200,
84
+ "askId": "Q",
85
+ "lastPrice": 136.04,
86
+ "lastSize": 200,
87
+ "lastId": "D",
88
+ "openPrice": 136.39,
89
+ "highPrice": 136.72,
90
+ "lowPrice": 134.66,
91
+ "bidTick": " ",
92
+ "closePrice": 135.45,
93
+ "netChange": 0.59,
94
+ "totalVolume": 23115635,
95
+ "quoteTimeInLong": 1566950328949,
96
+ "tradeTimeInLong": 1566950275129,
97
+ "mark": 135.87,
98
+ "exchange": "q",
99
+ "exchangeName": "NASD",
100
+ "marginable": true,
101
+ "shortable": true,
102
+ "volatility": 0.0095,
103
+ "digits": 4,
104
+ "52WkHigh": 141.675,
105
+ "52WkLow": 93.96,
106
+ "nAV": 0.0,
107
+ "peRatio": 28.1021,
108
+ "divAmount": 1.84,
109
+ "divYield": 1.36,
110
+ "divDate": "2019-08-14 00:00:00.000",
111
+ "securityStatus": "Normal",
112
+ "regularMarketLastPrice": 135.74,
113
+ "regularMarketLastSize": 2,
114
+ "regularMarketNetChange": 0.29,
115
+ "regularMarketTradeTimeInLong": 1566936001000,
116
+ "netPercentChangeInDouble": 0.4356,
117
+ "markChangeInDouble": 0.42,
118
+ "markPercentChangeInDouble": 0.3101,
119
+ "regularMarketPercentChangeInDouble": 0.2141,
120
+ "delayed": false
121
+ },
122
+ "CVX": {
123
+ "assetType": "EQUITY",
124
+ "symbol": "CVX",
125
+ "description": "Chevron Corporation Common Stock",
126
+ "bidPrice": 116.06,
127
+ "bidSize": 1000,
128
+ "bidId": "P",
129
+ "askPrice": 117.75,
130
+ "askSize": 300,
131
+ "askId": "P",
132
+ "lastPrice": 116.38,
133
+ "lastSize": 0,
134
+ "lastId": "N",
135
+ "openPrice": 116.27,
136
+ "highPrice": 117.03,
137
+ "lowPrice": 115.04,
138
+ "bidTick": " ",
139
+ "closePrice": 115.74,
140
+ "netChange": 0.64,
141
+ "totalVolume": 4238458,
142
+ "quoteTimeInLong": 1566950400019,
143
+ "tradeTimeInLong": 1566949966528,
144
+ "mark": 116.06,
145
+ "exchange": "n",
146
+ "exchangeName": "NYSE",
147
+ "marginable": true,
148
+ "shortable": true,
149
+ "volatility": 0.0147,
150
+ "digits": 2,
151
+ "52WkHigh": 127.6,
152
+ "52WkLow": 100.22,
153
+ "nAV": 0.0,
154
+ "peRatio": 14.94,
155
+ "divAmount": 4.76,
156
+ "divYield": 4.11,
157
+ "divDate": "2019-08-16 00:00:00.000",
158
+ "securityStatus": "Normal",
159
+ "regularMarketLastPrice": 115.83,
160
+ "regularMarketLastSize": 7908,
161
+ "regularMarketNetChange": 0.09,
162
+ "regularMarketTradeTimeInLong": 1566945000008,
163
+ "netPercentChangeInDouble": 0.553,
164
+ "markChangeInDouble": 0.32,
165
+ "markPercentChangeInDouble": 0.2765,
166
+ "regularMarketPercentChangeInDouble": 0.0778,
167
+ "delayed": false
168
+ }
169
+ }
170
+ RESPONSE
171
+ }
172
+ )
173
+ end
174
+
175
+ let(:expected_result) do
176
+ {"PG"=>
177
+ {"assetType"=>"EQUITY",
178
+ "symbol"=>"PG",
179
+ "description"=>"Procter & Gamble Company (The) Common Stock",
180
+ "bidPrice"=>120.55,
181
+ "bidSize"=>500,
182
+ "bidId"=>"P",
183
+ "askPrice"=>120.99,
184
+ "askSize"=>500,
185
+ "askId"=>"P",
186
+ "lastPrice"=>120.55,
187
+ "lastSize"=>0,
188
+ "lastId"=>"N",
189
+ "openPrice"=>119.99,
190
+ "highPrice"=>121.05,
191
+ "lowPrice"=>119.78,
192
+ "bidTick"=>" ",
193
+ "closePrice"=>119.32,
194
+ "netChange"=>1.23,
195
+ "totalVolume"=>6996550,
196
+ "quoteTimeInLong"=>1566950400020,
197
+ "tradeTimeInLong"=>1566945034633,
198
+ "mark"=>120.55,
199
+ "exchange"=>"n",
200
+ "exchangeName"=>"NYSE",
201
+ "marginable"=>true,
202
+ "shortable"=>true,
203
+ "volatility"=>0.01,
204
+ "digits"=>2,
205
+ "52WkHigh"=>121.76,
206
+ "52WkLow"=>78.49,
207
+ "nAV"=>0.0,
208
+ "peRatio"=>79.1467,
209
+ "divAmount"=>2.9836,
210
+ "divYield"=>2.5,
211
+ "divDate"=>"2019-07-18 00:00:00.000",
212
+ "securityStatus"=>"Normal",
213
+ "regularMarketLastPrice"=>120.55,
214
+ "regularMarketLastSize"=>12155,
215
+ "regularMarketNetChange"=>1.23,
216
+ "regularMarketTradeTimeInLong"=>1566945000012,
217
+ "netPercentChangeInDouble"=>1.0308,
218
+ "markChangeInDouble"=>1.23,
219
+ "markPercentChangeInDouble"=>1.0308,
220
+ "regularMarketPercentChangeInDouble"=>1.0308,
221
+ "delayed"=>false},
222
+ "MSFT"=>
223
+ {"assetType"=>"EQUITY",
224
+ "symbol"=>"MSFT",
225
+ "description"=>"Microsoft Corporation - Common Stock",
226
+ "bidPrice"=>135.87,
227
+ "bidSize"=>500,
228
+ "bidId"=>"K",
229
+ "askPrice"=>136.06,
230
+ "askSize"=>200,
231
+ "askId"=>"Q",
232
+ "lastPrice"=>136.04,
233
+ "lastSize"=>200,
234
+ "lastId"=>"D",
235
+ "openPrice"=>136.39,
236
+ "highPrice"=>136.72,
237
+ "lowPrice"=>134.66,
238
+ "bidTick"=>" ",
239
+ "closePrice"=>135.45,
240
+ "netChange"=>0.59,
241
+ "totalVolume"=>23115635,
242
+ "quoteTimeInLong"=>1566950328949,
243
+ "tradeTimeInLong"=>1566950275129,
244
+ "mark"=>135.87,
245
+ "exchange"=>"q",
246
+ "exchangeName"=>"NASD",
247
+ "marginable"=>true,
248
+ "shortable"=>true,
249
+ "volatility"=>0.0095,
250
+ "digits"=>4,
251
+ "52WkHigh"=>141.675,
252
+ "52WkLow"=>93.96,
253
+ "nAV"=>0.0,
254
+ "peRatio"=>28.1021,
255
+ "divAmount"=>1.84,
256
+ "divYield"=>1.36,
257
+ "divDate"=>"2019-08-14 00:00:00.000",
258
+ "securityStatus"=>"Normal",
259
+ "regularMarketLastPrice"=>135.74,
260
+ "regularMarketLastSize"=>2,
261
+ "regularMarketNetChange"=>0.29,
262
+ "regularMarketTradeTimeInLong"=>1566936001000,
263
+ "netPercentChangeInDouble"=>0.4356,
264
+ "markChangeInDouble"=>0.42,
265
+ "markPercentChangeInDouble"=>0.3101,
266
+ "regularMarketPercentChangeInDouble"=>0.2141,
267
+ "delayed"=>false},
268
+ "CVX"=>
269
+ {"assetType"=>"EQUITY",
270
+ "symbol"=>"CVX",
271
+ "description"=>"Chevron Corporation Common Stock",
272
+ "bidPrice"=>116.06,
273
+ "bidSize"=>1000,
274
+ "bidId"=>"P",
275
+ "askPrice"=>117.75,
276
+ "askSize"=>300,
277
+ "askId"=>"P",
278
+ "lastPrice"=>116.38,
279
+ "lastSize"=>0,
280
+ "lastId"=>"N",
281
+ "openPrice"=>116.27,
282
+ "highPrice"=>117.03,
283
+ "lowPrice"=>115.04,
284
+ "bidTick"=>" ",
285
+ "closePrice"=>115.74,
286
+ "netChange"=>0.64,
287
+ "totalVolume"=>4238458,
288
+ "quoteTimeInLong"=>1566950400019,
289
+ "tradeTimeInLong"=>1566949966528,
290
+ "mark"=>116.06,
291
+ "exchange"=>"n",
292
+ "exchangeName"=>"NYSE",
293
+ "marginable"=>true,
294
+ "shortable"=>true,
295
+ "volatility"=>0.0147,
296
+ "digits"=>2,
297
+ "52WkHigh"=>127.6,
298
+ "52WkLow"=>100.22,
299
+ "nAV"=>0.0,
300
+ "peRatio"=>14.94,
301
+ "divAmount"=>4.76,
302
+ "divYield"=>4.11,
303
+ "divDate"=>"2019-08-16 00:00:00.000",
304
+ "securityStatus"=>"Normal",
305
+ "regularMarketLastPrice"=>115.83,
306
+ "regularMarketLastSize"=>7908,
307
+ "regularMarketNetChange"=>0.09,
308
+ "regularMarketTradeTimeInLong"=>1566945000008,
309
+ "netPercentChangeInDouble"=>0.553,
310
+ "markChangeInDouble"=>0.32,
311
+ "markPercentChangeInDouble"=>0.2765,
312
+ "regularMarketPercentChangeInDouble"=>0.0778,
313
+ "delayed"=>false}
314
+ }
315
+ end
316
+
317
+ it { is_expected.to eql(expected_result) }
318
+ end
319
+
320
+ context 'invalid symbol' do
321
+ subject do
322
+ client.get_quotes(symbols)
323
+ end
324
+
325
+ let(:symbols) { %w(XZXX) }
326
+
327
+ let!(:expected_request) do
328
+ TDAmeritrade::Spec::Mocks::MockGetQuotes.mock_find(
329
+ request: {
330
+ headers: { 'Authorization': "Bearer #{client.access_token}" },
331
+ query: {
332
+ apikey: client.client_id,
333
+ symbol: symbols.join(','),
334
+ }.map {|k,v| [k, v.to_s] }.to_h,
335
+ },
336
+ response: {
337
+ body: <<~RESPONSE
338
+ {}
339
+ RESPONSE
340
+ }
341
+ )
342
+ end
343
+
344
+ let(:expected_result) do
345
+ {}
346
+ end
347
+
348
+ it { is_expected.to eql(expected_result) }
349
+ end
350
+
351
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+ require 'support/authenticated_client'
3
+ require 'tdameritrade'
4
+
5
+ describe TDAmeritrade::Operations::GetWatchlists do
6
+ include_context 'authenticated client'
7
+ # include_context 'webmock off'
8
+
9
+ context 'for single account' do
10
+ subject do
11
+ client.get_watchlists(account_id)
12
+ end
13
+
14
+ let(:account_id) { '123456789' }
15
+
16
+ let!(:expected_request) do
17
+ TDAmeritrade::Spec::Mocks::MockWatchlists.mock_find_for_account(
18
+ account_id,
19
+ request: {
20
+ headers: { 'Authorization': "Bearer #{client.access_token}" },
21
+ },
22
+ response: {
23
+ body: <<~RESPONSE
24
+ [
25
+ {
26
+ "name": "Previous Trades",
27
+ "watchlistId": "666666666",
28
+ "accountId": "123456789",
29
+ "watchlistItems": [
30
+ {
31
+ "sequenceId": 1,
32
+ "quantity": 0.0,
33
+ "averagePrice": 0.0,
34
+ "commission": 0.0,
35
+ "instrument": {
36
+ "symbol": "BZUN",
37
+ "assetType": "EQUITY"
38
+ }
39
+ },
40
+ {
41
+ "sequenceId": 2,
42
+ "quantity": 0.0,
43
+ "averagePrice": 0.0,
44
+ "commission": 0.0,
45
+ "instrument": {
46
+ "symbol": "SFLY",
47
+ "assetType": "EQUITY"
48
+ }
49
+ }
50
+ ]
51
+ }
52
+ ]
53
+ RESPONSE
54
+ }
55
+ )
56
+ end
57
+
58
+ let(:expected_result) do
59
+ [{"name"=>"Previous Trades",
60
+ "watchlistId"=>"666666666",
61
+ "accountId"=>"123456789",
62
+ "watchlistItems"=>
63
+ [{"sequenceId"=>1,
64
+ "quantity"=>0.0,
65
+ "averagePrice"=>0.0,
66
+ "commission"=>0.0,
67
+ "instrument"=>{"symbol"=>"BZUN", "assetType"=>"EQUITY"}},
68
+ {"sequenceId"=>2,
69
+ "quantity"=>0.0,
70
+ "averagePrice"=>0.0,
71
+ "commission"=>0.0,
72
+ "instrument"=>{"symbol"=>"SFLY", "assetType"=>"EQUITY"}}]}
73
+ ]
74
+ end
75
+
76
+ it { is_expected.to eql(expected_result) }
77
+ end
78
+
79
+
80
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+ require 'support/authenticated_client'
3
+ require 'tdameritrade'
4
+
5
+ describe TDAmeritrade::Operations::ReplaceWatchlist do
6
+ include_context 'authenticated client'
7
+ # include_context 'webmock off'
8
+
9
+ context 'for single account' do
10
+ subject do
11
+ client.replace_watchlist(account_id, watchlist_id, 'My New Watchlist', %w(AAPL MSFT VZ))
12
+ end
13
+
14
+ let(:account_id) { '123456789' }
15
+ let(:watchlist_id) { '666666666' }
16
+
17
+ let!(:expected_request) do
18
+ TDAmeritrade::Spec::Mocks::MockWatchlists.mock_replace(
19
+ account_id,
20
+ watchlist_id,
21
+ request: {
22
+ headers: { 'Authorization': "Bearer #{client.access_token}" },
23
+ body: expected_request_body
24
+ }
25
+ )
26
+ end
27
+
28
+ let(:expected_request_body) do
29
+ {
30
+ :name=>"My New Watchlist",
31
+ :watchlistId=>"#{watchlist_id}",
32
+ :watchlistItems=>
33
+ [
34
+ {:quantity=>0, :averagePrice=>0, :commission=>0, :purchasedDate=>Date.today.strftime('%Y-%m-%d'), :instrument=>{:symbol=>"AAPL", :assetType=>"EQUITY"}},
35
+ {:quantity=>0, :averagePrice=>0, :commission=>0, :purchasedDate=>Date.today.strftime('%Y-%m-%d'), :instrument=>{:symbol=>"MSFT", :assetType=>"EQUITY"}},
36
+ {:quantity=>0, :averagePrice=>0, :commission=>0, :purchasedDate=>Date.today.strftime('%Y-%m-%d'), :instrument=>{:symbol=>"VZ", :assetType=>"EQUITY"}}
37
+ ]
38
+ }.to_json
39
+ end
40
+
41
+ it { is_expected.to be_truthy }
42
+ end
43
+
44
+
45
+ end