diff --git a/dal/stock-markets.js b/dal/stock-markets.js index 581ba95..04d7fd4 100644 --- a/dal/stock-markets.js +++ b/dal/stock-markets.js @@ -49,12 +49,15 @@ const predictedStockInfoSchema = new Schema( market_name: String, market_id: String, stock_name: String, + ticker_id: String, stock_id: String, prediction_details: { - timestamp: Date, + date: Date, + volume: Number, open: Number, + high: Number, + low: Number, close: Number, - volume_traded: Number, }, }, { collection: 'PredictedStockInfo' } diff --git a/data/prediction.js b/data/prediction.js index 5073f7a..8b8eaed 100644 --- a/data/prediction.js +++ b/data/prediction.js @@ -1,8 +1,31 @@ const { realtimeStockInfo } = require('../dal/stock-markets'); const { predictedStockInfo } = require('../dal/stock-markets'); const { stockMarketInfo } = require('../dal/stock-markets'); +const axios = require('axios'); +const tickerToEndpointMap = { + AAPL: 'http://f6b1b502-ac55-4522-b0ef-f4becf1604a2.canadacentral.azurecontainer.io/score', + TWTR: '', + TXN: '', + FB: '', + GOOGL: '', + AMZN: '', + COST: '', + QCOM: '', + AVGO: '', + CSCO: '', + TSLA: '', + INTC: '', + CMCSA: '', + NFLX: '', + UBER: '', + PEP: '', + ADBE: 'http://15d1cdd8-a814-4b62-90ee-de15fc6d6225.eastus2.azurecontainer.io/score', + MSFT: 'http://08478e4a-bbc7-4e7b-bb07-38f4792fea4b.eastus2.azurecontainer.io/score', + PYPL: '', + NVDA: '', +}; -const number_of_days = [1, 3, 6]; +const numOfDays = [1, 3, 6]; const predictPrices = async () => { const doc = await stockMarketInfo.find({}); @@ -13,7 +36,7 @@ const predictPrices = async () => { ticker_id: stock_data.ticker, }); - get_dependent_variables(stock_data.ticker) + getDependentVariables(stock_data.ticker) .then(async (dependent_variable_list) => { if (stockPredictionInfo) { await predictedStockInfo.updateOne( @@ -47,35 +70,88 @@ const predictPrices = async () => { } }; -const get_dependent_variables = async (ticker) => { +// get Date, Volume, Open, High, Low, values from database and fluctuate their values +const getDependentVariables = async (ticker) => { const { - stock_details: { timestamp, open, close, high, low, volume }, + stock_details: { timestamp, volume, open, high, low }, } = await realtimeStockInfo.findOne({ ticker_id: ticker }); - console.log(timestamp, open, high, low, volume); - const new_open = open + open * 0.05; - const new_high = high + high * 0.05; - const new_low = low + low * 0.05; - const new_volume = volume + volume * 0.05; - const new_close = close + close * 0.05; const res = []; - for (let days of number_of_days) { + for (let days of numOfDays) { const date = new Date(timestamp); - const new_date = new Date(date.setDate(date.getDate() + days)); - const new_time = new Date(new_date.setUTCHours(0)); + let newDate = new Date(date.setDate(date.getDate() + days)); + let currentDay = newDate.getDay(); + let addDays = 0; + + // if date is weekend, set date to Monday since no predictions available for weekends + if (currentDay == 5) { + // saturday + addDays = 2; + } else if (currentDay == 6) { + // sunday + addDays = 1; + } + + newDate.setDate(newDate.getDate() + addDays); + const newDateWithTime = new Date(newDate.setUTCHours(0)); + + // randomize dependent variables + const randomPercent = (Math.random() * 5 + 1) * 0.01; + const new_volume = volume + volume * randomPercent; + const new_open = open + open * randomPercent; + const new_high = high + high * randomPercent; + const new_low = low + low * randomPercent; res.push({ - timestamp: new_time, - open: new_open, - close: new_close, - high: new_high, - low: new_low, - volume: new_volume * 1000, + Date: newDateWithTime, + Volume: Math.round(new_volume), + Open: roundToTwoDecimals(new_open), + High: roundToTwoDecimals(new_high), + Low: roundToTwoDecimals(new_low), }); } - return res; + return getPredictionScoreFromAPI(res, ticker); +}; + +// Returns a list of updated prediction_details with prediction scores from Azure AutoML endpoint +const getPredictionScoreFromAPI = async (dependentVariableList, ticker) => { + let scoreURI = ''; + for (const [key, value] of Object.entries(tickerToEndpointMap)) { + if (ticker === key) scoreURI = value; + } + + const data = { + data: dependentVariableList, + }; + + if (scoreURI !== '') { + let scoreRes = await axios.post(scoreURI, JSON.stringify(data), { + headers: { 'Content-Type': 'application/json' }, + }); + let score = scoreRes.data.replace('NaN', 0.0); // having NaN in JSON throws error + const scoreObj = JSON.parse(score); + const scoreList = scoreObj['forecast']; + let count = 0; + dependentVariableList.forEach(function (element) { + if (isNaN(scoreList[count])) { + element.Close = 0.0; + } else { + element.Close = roundToTwoDecimals(scoreList[count]); + } + count += 1; + }); + } else { + console.log('Score URI is an empty string'); + } + const newList = [...dependentVariableList]; // create new list of prediction_details to return + return newList; +}; + +// Rounds input to two decimal places +const roundToTwoDecimals = (num) => { + return +(Math.round(num + 'e+2') + 'e-2'); }; predictPrices().then((_) => { diff --git a/data/stock-market.js b/data/stock-market.js index c5c8ee9..1e490a8 100644 --- a/data/stock-market.js +++ b/data/stock-market.js @@ -84,6 +84,10 @@ const MARKET_DATA = [ ticker: 'AAPL', name: 'Apple', }, + { + ticker: 'AAPL', + name: 'Apple', + }, ], }, ]; diff --git a/src/components/StockDetails/StockPredictionDetails.jsx b/src/components/StockDetails/StockPredictionDetails.jsx index 1174972..8590d29 100644 --- a/src/components/StockDetails/StockPredictionDetails.jsx +++ b/src/components/StockDetails/StockPredictionDetails.jsx @@ -38,12 +38,11 @@ const PricePrediction = () => { const formatDate = useDateFormat(); const redColor = useColorModeValue('red.light', 'red.dark'); const greenColor = useColorModeValue('green.light', 'green.dark'); - return ( - Prediction + Prediction of Closing Prices { > {prediction_details - ? prediction_details.map( - ({ close, timestamp, _id }) => ( - ( + + - -
- - {formatDate(timestamp)} - -
- = 0 - ? greenColor - : redColor - } - > - $ - {close?.toFixed(2) ?? - ' --.--'} +
+ + {formatDate(Date)} - - - ) - ) +
+ = 0 + ? greenColor + : redColor + } + > + ${Close?.toFixed(2) ?? ' --.--'} + +
+
+ )) : null}
diff --git a/src/hooks/useDateFormat.js b/src/hooks/useDateFormat.js index 420eaba..53c6868 100644 --- a/src/hooks/useDateFormat.js +++ b/src/hooks/useDateFormat.js @@ -2,13 +2,13 @@ const useDateFormat = () => (date) => { if (date) { let year = parseInt(date.slice(0, 4)); let month = parseInt(date.slice(6, 7)); - let day = parseInt(date.slice(9, 10)); + let day = parseInt(date.slice(8, 10)); let dateObj = new Date(year, month - 1, day); + let options = { year: 'numeric', month: 'long', day: '2-digit' }; - let options = {year: 'numeric', month: 'long', day: '2-digit'}; return new Intl.DateTimeFormat('en-US', options).format(dateObj); } else { - return null + return null; } }; diff --git a/yarn.lock b/yarn.lock index 15962a6..bb524e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12353,6 +12353,11 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" +simplejson@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/simplejson/-/simplejson-0.0.2.tgz#9e3d9875aecec49565028cd9278b2a1ac6bcaaef" + integrity sha1-nj2Yda7OxJVlAozZJ4sqGsa8qu8= + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"