Skip to content

Commit 369bf90

Browse files
feat: move to Alpha Vantage stock API (#444)
1 parent ab36079 commit 369bf90

File tree

5 files changed

+68
-16
lines changed

5 files changed

+68
-16
lines changed

apps/stock-price-checker-proxy/api/v1.js

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const db = new Datastore({
1212

1313
// cleaning cache data on app restart
1414
db.remove(
15-
{ $or: [{ data: {} }, { data: "Unknown symbol" }] },
15+
{ $or: [{ stockData: {} }, { stockData: "Unknown symbol" }] },
1616
{ multi: true },
1717
(err, count) => {
1818
console.log("\nremoving garbage from cache...");
@@ -31,10 +31,12 @@ const getUID = (n = 8, symbols = _symbols) =>
3131
.map(() => symbols[Math.floor(Math.random() * symbols.length)])
3232
.join("");
3333

34-
const { IEX_API_KEY = "", CACHE_TTL_MINUTES = 10 } = process.env;
34+
const { ALPHA_VANTAGE_API_KEY = "", CACHE_TTL_MINUTES = 10 } = process.env;
3535

3636
const validTickerRegExp = /^[a-z]{1,6}$/;
37-
const isValidStock = stock => validTickerRegExp.test(stock);
37+
const isValidStock = (stock) => validTickerRegExp.test(stock);
38+
const parseFloatAndRound = (value, digits) =>
39+
Number(parseFloat(value).toFixed(digits));
3840

3941
router.use(cors());
4042

@@ -60,30 +62,78 @@ router.get("/stock/:stock/quote", (req, res, next) => {
6062
if (err) return next(err);
6163
if (cached) {
6264
console.log(`rid: ${req_id} ** ${stock} from cache **`);
63-
return res.json(cached.data);
65+
return res.json(cached.stockData);
6466
}
6567
try {
6668
const { data } = await axios.get(
67-
`https://cloud.iexapis.com/stable/stock/${stock}/quote?token=${IEX_API_KEY}`
69+
`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${stock}&apikey=${ALPHA_VANTAGE_API_KEY}`
6870
);
6971
console.log(`rid: ${req_id} !! ${stock} from api !!`);
70-
res.json(data);
72+
const temp = { ...data?.["Global Quote"] };
73+
let stockData;
74+
if (Object.keys(temp).length === 0 && temp.constructor === Object) {
75+
stockData = "Unknown symbol"; // Mimic IEX API response for this case
76+
} else {
77+
const symbol = temp["01. symbol"];
78+
const open = parseFloatAndRound(temp["02. open"], 2);
79+
const high = parseFloatAndRound(temp["03. high"], 2);
80+
const low = parseFloatAndRound(temp["04. low"], 2);
81+
const close = parseFloatAndRound(temp["05. price"], 2);
82+
const volume = Number(parseFloatAndRound(temp["06. volume"]), 2);
83+
const latestTime = new Date(
84+
temp["07. latest trading day"]
85+
).toLocaleString("en-US", {
86+
month: "long",
87+
day: "numeric",
88+
year: "numeric"
89+
});
90+
const previousClose = parseFloatAndRound(
91+
temp["08. previous close"],
92+
2
93+
);
94+
const change = parseFloatAndRound(temp["09. change"], 2);
95+
96+
// Transform the response to match the IEX's as closely as possible
97+
// with the available data
98+
stockData = {
99+
change,
100+
changePercent: parseFloatAndRound(
101+
(close - previousClose) / previousClose,
102+
5
103+
),
104+
close,
105+
high,
106+
latestPrice: close,
107+
latestTime,
108+
latestVolume: volume,
109+
low,
110+
open,
111+
previousClose,
112+
symbol,
113+
volume
114+
};
115+
}
116+
res.json(stockData);
71117
db.update(
72118
{
73119
_id: stock
74120
},
75-
{ _id: stock, data, updatedAt: Date.now() },
121+
{ _id: stock, stockData, updatedAt: Date.now() },
76122
{ upsert: true },
77123
() => console.log(`rid: ${req_id} ++ ${stock} stored ++`)
78124
);
79125
} catch (e) {
80126
if (e.response) {
81-
res.status(e.response.status).json(e.response.data);
127+
res.status(e.response.status).json(e.response.stockData);
82128
db.update(
83129
{
84130
_id: stock
85131
},
86-
{ _id: stock, data: e.response.data, updatedAt: Date.now() },
132+
{
133+
_id: stock,
134+
stockData: e.response.stockData,
135+
updatedAt: Date.now()
136+
},
87137
{ upsert: true }
88138
);
89139
} else {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
PORT=3000
22
CACHE_TTL_MINUTES=
3-
IEX_API_KEY=
3+
ALPHA_VANTAGE_API_KEY=

apps/stock-price-checker-proxy/server.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
require('dotenv').config();
1+
require("dotenv").config();
22
const express = require("express");
33
const app = express();
44

@@ -12,7 +12,8 @@ app.get("/", (req, res) => {
1212
res.sendFile(__dirname + "/views/index.html");
1313
});
1414

15-
app.use((err, req, res) => {
15+
// eslint-disable-next-line no-unused-vars
16+
app.use((err, req, res, next) => {
1617
if (err) {
1718
console.log(err.message, err.stack);
1819
res.status(500).json({ status: "internal server error" });

docker-compose.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
networks:
22
proxy:
33

4+
45
services:
56

67
caddy:
@@ -12,8 +13,8 @@ services:
1213
volumes:
1314
- ./Caddyfile:/etc/caddy/Caddyfile
1415
ports:
15-
- 80:80
16-
- 443:443
16+
- 80:80
17+
- 443:443
1718

1819
mongo:
1920
image: mongo
@@ -373,7 +374,7 @@ services:
373374
dockerfile: ./Dockerfile
374375
environment:
375376
- CACHE_TTL_MINUTES=${STOCK_PRICE_CHECKER_PROXY_CACHE_TTL_MINUTES}
376-
- IEX_API_KEY=${STOCK_PRICE_CHECKER_PROXY_IEX_API_KEY}
377+
- ALPHA_VANTAGE_API_KEY=${STOCK_PRICE_CHECKER_PROXY_ALPHA_VANTAGE_API_KEY}
377378
ports:
378379
- 50130:3000
379380

sample.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ STOCK_PRICE_CHECKER_DB_URI=mongodb://mongo:27017/stock-price-checker
4242

4343
# Stock Price Checker Proxy
4444
STOCK_PRICE_CHECKER_PROXY_CACHE_TTL_MINUTES=30
45-
STOCK_PRICE_CHECKER_PROXY_IEX_API_KEY=api_key_from_iex_dashboard
45+
STOCK_PRICE_CHECKER_PROXY_ALPHA_VANTAGE_API_KEY=api_key_from_alpha_vantage
4646

4747
# Twitch Proxy
4848
TWITCH_PROXY_TWITCH_CLIENT_ID=client_id_from_twitch_dashboard

0 commit comments

Comments
 (0)