Skip to content

Commit

Permalink
feat: move to Alpha Vantage stock API (freeCodeCamp#444)
Browse files Browse the repository at this point in the history
  • Loading branch information
scissorsneedfoodtoo committed Jun 27, 2023
1 parent ab36079 commit 369bf90
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 16 deletions.
68 changes: 59 additions & 9 deletions apps/stock-price-checker-proxy/api/v1.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const db = new Datastore({

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

const { IEX_API_KEY = "", CACHE_TTL_MINUTES = 10 } = process.env;
const { ALPHA_VANTAGE_API_KEY = "", CACHE_TTL_MINUTES = 10 } = process.env;

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

router.use(cors());

Expand All @@ -60,30 +62,78 @@ router.get("/stock/:stock/quote", (req, res, next) => {
if (err) return next(err);
if (cached) {
console.log(`rid: ${req_id} ** ${stock} from cache **`);
return res.json(cached.data);
return res.json(cached.stockData);
}
try {
const { data } = await axios.get(
`https://cloud.iexapis.com/stable/stock/${stock}/quote?token=${IEX_API_KEY}`
`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${stock}&apikey=${ALPHA_VANTAGE_API_KEY}`
);
console.log(`rid: ${req_id} !! ${stock} from api !!`);
res.json(data);
const temp = { ...data?.["Global Quote"] };
let stockData;
if (Object.keys(temp).length === 0 && temp.constructor === Object) {
stockData = "Unknown symbol"; // Mimic IEX API response for this case
} else {
const symbol = temp["01. symbol"];
const open = parseFloatAndRound(temp["02. open"], 2);
const high = parseFloatAndRound(temp["03. high"], 2);
const low = parseFloatAndRound(temp["04. low"], 2);
const close = parseFloatAndRound(temp["05. price"], 2);
const volume = Number(parseFloatAndRound(temp["06. volume"]), 2);
const latestTime = new Date(
temp["07. latest trading day"]
).toLocaleString("en-US", {
month: "long",
day: "numeric",
year: "numeric"
});
const previousClose = parseFloatAndRound(
temp["08. previous close"],
2
);
const change = parseFloatAndRound(temp["09. change"], 2);

// Transform the response to match the IEX's as closely as possible
// with the available data
stockData = {
change,
changePercent: parseFloatAndRound(
(close - previousClose) / previousClose,
5
),
close,
high,
latestPrice: close,
latestTime,
latestVolume: volume,
low,
open,
previousClose,
symbol,
volume
};
}
res.json(stockData);
db.update(
{
_id: stock
},
{ _id: stock, data, updatedAt: Date.now() },
{ _id: stock, stockData, updatedAt: Date.now() },
{ upsert: true },
() => console.log(`rid: ${req_id} ++ ${stock} stored ++`)
);
} catch (e) {
if (e.response) {
res.status(e.response.status).json(e.response.data);
res.status(e.response.status).json(e.response.stockData);
db.update(
{
_id: stock
},
{ _id: stock, data: e.response.data, updatedAt: Date.now() },
{
_id: stock,
stockData: e.response.stockData,
updatedAt: Date.now()
},
{ upsert: true }
);
} else {
Expand Down
2 changes: 1 addition & 1 deletion apps/stock-price-checker-proxy/sample.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
PORT=3000
CACHE_TTL_MINUTES=
IEX_API_KEY=
ALPHA_VANTAGE_API_KEY=
5 changes: 3 additions & 2 deletions apps/stock-price-checker-proxy/server.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require('dotenv').config();
require("dotenv").config();
const express = require("express");
const app = express();

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

app.use((err, req, res) => {
// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
if (err) {
console.log(err.message, err.stack);
res.status(500).json({ status: "internal server error" });
Expand Down
7 changes: 4 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
networks:
proxy:


services:

caddy:
Expand All @@ -12,8 +13,8 @@ services:
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
ports:
- 80:80
- 443:443
- 80:80
- 443:443

mongo:
image: mongo
Expand Down Expand Up @@ -373,7 +374,7 @@ services:
dockerfile: ./Dockerfile
environment:
- CACHE_TTL_MINUTES=${STOCK_PRICE_CHECKER_PROXY_CACHE_TTL_MINUTES}
- IEX_API_KEY=${STOCK_PRICE_CHECKER_PROXY_IEX_API_KEY}
- ALPHA_VANTAGE_API_KEY=${STOCK_PRICE_CHECKER_PROXY_ALPHA_VANTAGE_API_KEY}
ports:
- 50130:3000

Expand Down
2 changes: 1 addition & 1 deletion sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ STOCK_PRICE_CHECKER_DB_URI=mongodb://mongo:27017/stock-price-checker

# Stock Price Checker Proxy
STOCK_PRICE_CHECKER_PROXY_CACHE_TTL_MINUTES=30
STOCK_PRICE_CHECKER_PROXY_IEX_API_KEY=api_key_from_iex_dashboard
STOCK_PRICE_CHECKER_PROXY_ALPHA_VANTAGE_API_KEY=api_key_from_alpha_vantage

# Twitch Proxy
TWITCH_PROXY_TWITCH_CLIENT_ID=client_id_from_twitch_dashboard
Expand Down

0 comments on commit 369bf90

Please sign in to comment.