update forecast ai page

This commit is contained in:
MuslemRahimi 2025-03-13 10:41:07 +01:00
parent 57fa0224bf
commit fa70a10672
2 changed files with 158 additions and 113 deletions

View File

@ -41,10 +41,10 @@
AI Score Definition AI Score Definition
</h3> </h3>
<div class=" p-2"> <div class=" p-2">
Revenue, also called sales, is the amount of money a company The AI Score uses fundamental and technical indicators, along
receives from its business activities, such as sales of products with statistical data, to predict if a stock will be bullish in
or services. Revenue does not take any expenses into account and the next quarter. It ranges from 1 to 10, with higher scores
is therefore different from profits. indicating a greater likelihood of the prediction being correct.
</div> </div>
<!-- <!--
<div class="px-2"> <div class="px-2">
@ -67,10 +67,10 @@
AI Trend Forecast Definition AI Trend Forecast Definition
</h3> </h3>
<div class=" p-2"> <div class=" p-2">
Revenue, also called sales, is the amount of money a company The AI Trend Model forecasts stock trends by analyzing
receives from its business activities, such as sales of products historical price data. It detects patterns, seasonality, and
or services. Revenue does not take any expenses into account and trends to predict future price movements, making it useful for
is therefore different from profits. predicting price targets for the next 12 months.
</div> </div>
<!-- <!--
<div class="px-2"> <div class="px-2">

View File

@ -16,128 +16,172 @@
return `Q${quarter} '${year}`; return `Q${quarter} '${year}`;
}; };
const price = data?.getStockQuote?.price?.toFixed(2) || 0;
const calculatePriceChange = (targetPrice) => const calculatePriceChange = (targetPrice) =>
targetPrice && price ? ((targetPrice / price - 1) * 100)?.toFixed(2) : 0; targetPrice && price ? ((targetPrice / price - 1) * 100)?.toFixed(2) : 0;
const avgPriceTarget = data?.getPriceAnalysis?.avgPriceTarget || 0; function prepareDataset() {
const medianPriceTarget = data?.getPriceAnalysis?.medianPriceTarget || 0; price = data?.getStockQuote?.price?.toFixed(2) || 0;
const lowPriceTarget = data?.getPriceAnalysis?.lowPriceTarget || 0;
const highPriceTarget = data?.getPriceAnalysis?.highPriceTarget || 0;
const lowChange = calculatePriceChange(lowPriceTarget); avgPriceTarget = data?.getPriceAnalysis?.avgPriceTarget || 0;
const medianChange = calculatePriceChange(medianPriceTarget); medianPriceTarget = data?.getPriceAnalysis?.medianPriceTarget || 0;
const avgChange = calculatePriceChange(avgPriceTarget); lowPriceTarget = data?.getPriceAnalysis?.lowPriceTarget || 0;
const highChange = calculatePriceChange(highPriceTarget); highPriceTarget = data?.getPriceAnalysis?.highPriceTarget || 0;
// Assume data.getHistoricalPrice contains objects with a "time" field (e.g. "2015-01-02") lowChange = calculatePriceChange(lowPriceTarget);
const historicalData = data?.getHistoricalPrice || []; medianChange = calculatePriceChange(medianPriceTarget);
avgChange = calculatePriceChange(avgPriceTarget);
highChange = calculatePriceChange(highPriceTarget);
const backtestList = data?.getAIScore?.backtest || []; // Assume data.getHistoricalPrice contains objects with a "time" field (e.g. "2015-01-02")
historicalData = data?.getHistoricalPrice || [];
// Append the latest historical date (using "time") if available. Note that this entry may not include a score. backtestList = data?.getAIScore?.backtest || [];
if (historicalData && historicalData.length) {
const latest = historicalData.at(-1); // Append the latest historical date (using "time") if available. Note that this entry may not include a score.
backtestList.push({ date: latest.time }); if (historicalData && historicalData?.length) {
const latest = historicalData?.at(-1);
backtestList?.push({ date: latest.time });
const seenDates = new Set();
backtestList = backtestList?.filter((item) => {
// Check if the date is already seen
if (seenDates?.has(item?.date)) {
// If yes, skip this duplicate (delete the last one)
return false;
}
// Otherwise, record the date and keep the item
seenDates?.add(item?.date);
return true;
});
}
processedData = backtestList?.map((item) => {
const dateStr = item.date;
const targetTime = new Date(dateStr).getTime();
let closestPoint = historicalData[0];
let minDiff = Infinity;
historicalData.forEach((point) => {
const pointTime = new Date(point.time).getTime();
const diff = Math.abs(pointTime - targetTime);
if (diff < minDiff) {
minDiff = diff;
closestPoint = point;
}
});
// Base data point with x as the target date timestamp and y as the close price from the closest historical point
const dataPoint = { x: targetTime, y: closestPoint.close };
// If a score is provided, add marker configuration based on its value.
if (item.hasOwnProperty("score")) {
let markerColor, markerSymbol;
if (item.score > 6) {
// Bullish: green marker with an upward triangle
markerColor = "#2ecc71";
markerSymbol = "triangle-up";
} else if (item.score < 5) {
// Bearish: red marker with a downward triangle
markerColor = "#e74c3c";
markerSymbol = "triangle-down";
} else {
// Neutral (score exactly 5): yellow marker with a circle
markerColor = "#f1c40f";
markerSymbol = "circle";
}
dataPoint.marker = {
symbol: markerSymbol,
radius: 4,
fillColor: markerColor,
lineWidth: 2,
lineColor: $mode === "light" ? "black" : "white",
};
dataPoint.dataLabels = {
enabled: true,
format: String(item.score),
style: {
color: $mode === "light" ? "black" : "white",
fontWeight: "bold",
fontSize: "14px",
},
y: -10,
};
}
return dataPoint;
});
tableDates = processedData
?.slice(0, -1)
?.map((item) => formatDateToQuarter(item?.x));
tableScore = processedData
?.slice(0, -1)
?.map((item) => item?.dataLabels?.format);
// Compute percentage change
tableQuarterChange = processedData
?.slice(0, -1)
?.map((item, index, arr) => {
const prevY = arr[index - 1]?.y; // Get the previous value
if (prevY == null || item.y == null) return null; // Handle missing values
const change = ((item.y - prevY) / prevY) * 100; // Calculate percentage change
return {
quarter: tableDates[index],
change: Number(change?.toFixed(2)), // Format to 2 decimal places
};
})
?.filter(Boolean); // Remove null values
// Compute Average Return
returns = processedData
?.slice(1) // Skip the first value since there's no previous value for it
?.map((item, index) => {
const prevY = processedData[index]?.y;
if (prevY == null || item.y == null) return null;
const returnPercentage = ((item.y - prevY) / prevY) * 100;
return returnPercentage;
})
.filter(Boolean); // Remove null values
avgReturn =
returns?.reduce((sum, returnPercentage) => sum + returnPercentage, 0) /
returns?.length;
} }
let price;
let avgPriceTarget;
let medianPriceTarget;
let lowPriceTarget;
let highPriceTarget;
let lowChange;
let medianChange;
let avgChange;
let highChange;
let historicalData;
let backtestList;
// For each backtest entry, find the historical price with the closest available time. // For each backtest entry, find the historical price with the closest available time.
// Then, if a score exists, attach a marker and data label based on the score. // Then, if a score exists, attach a marker and data label based on the score.
const processedData = backtestList?.map((item) => { let processedData = [];
const dateStr = item.date;
const targetTime = new Date(dateStr).getTime();
let closestPoint = historicalData[0];
let minDiff = Infinity;
historicalData.forEach((point) => { let tableDates;
const pointTime = new Date(point.time).getTime();
const diff = Math.abs(pointTime - targetTime);
if (diff < minDiff) {
minDiff = diff;
closestPoint = point;
}
});
// Base data point with x as the target date timestamp and y as the close price from the closest historical point let tableScore;
const dataPoint = { x: targetTime, y: closestPoint.close };
// If a score is provided, add marker configuration based on its value.
if (item.hasOwnProperty("score")) {
let markerColor, markerSymbol;
if (item.score > 6) {
// Bullish: green marker with an upward triangle
markerColor = "#2ecc71";
markerSymbol = "triangle-up";
} else if (item.score < 5) {
// Bearish: red marker with a downward triangle
markerColor = "#e74c3c";
markerSymbol = "triangle-down";
} else {
// Neutral (score exactly 5): yellow marker with a circle
markerColor = "#f1c40f";
markerSymbol = "circle";
}
dataPoint.marker = {
symbol: markerSymbol,
radius: 4,
fillColor: markerColor,
lineWidth: 2,
lineColor: $mode === "light" ? "black" : "white",
};
dataPoint.dataLabels = {
enabled: true,
format: String(item.score),
style: {
color: $mode === "light" ? "black" : "white",
fontWeight: "bold",
fontSize: "14px",
},
y: -10,
};
}
return dataPoint;
});
const tableDates = processedData
?.slice(0, -1)
?.map((item) => formatDateToQuarter(item.x));
const tableScore = processedData
?.slice(0, -1)
?.map((item) => item?.dataLabels?.format);
// Compute percentage change // Compute percentage change
const tableQuarterChange = processedData let tableQuarterChange;
?.slice(0, -1)
.map((item, index, arr) => {
const prevY = arr[index - 1]?.y; // Get the previous value
if (prevY == null || item.y == null) return null; // Handle missing values
const change = ((item.y - prevY) / prevY) * 100; // Calculate percentage change
return {
quarter: tableDates[index],
change: Number(change?.toFixed(2)), // Format to 2 decimal places
};
})
?.filter(Boolean); // Remove null values
// Compute Average Return // Compute Average Return
const returns = processedData let returns;
?.slice(1) // Skip the first value since there's no previous value for it
?.map((item, index) => {
const prevY = processedData[index]?.y;
if (prevY == null || item.y == null) return null;
const returnPercentage = ((item.y - prevY) / prevY) * 100;
return returnPercentage;
})
.filter(Boolean); // Remove null values
const avgReturn = let avgReturn;
returns?.reduce((sum, returnPercentage) => sum + returnPercentage, 0) /
returns?.length;
function getAIScorePlot() { function getAIScorePlot() {
const solidData = processedData.slice(0, -1); const solidData = processedData.slice(0, -1);
@ -422,7 +466,8 @@
let configScore = null; let configScore = null;
$: { $: {
if ($mode) { if ($stockTicker || $mode) {
prepareDataset();
configScore = getAIScorePlot() || null; configScore = getAIScorePlot() || null;
config = getPriceForecastChart() || null; config = getPriceForecastChart() || null;
} }