Update: 2026-07-06 17:51:16

This commit is contained in:
Hamza-Ayed
2026-07-06 17:51:16 +03:00
parent e21e1f4ec2
commit 9d257c5d7d
8 changed files with 292 additions and 241 deletions
+78 -16
View File
@@ -52,7 +52,7 @@ async function main(): Promise<void> {
const opts = parseArgs();
const startTime = Date.now();
console.log(`🚀 Siro Pricing Engine v1.0`);
console.log(`🚀 Siro Pricing Engine v1.1`);
console.log(` Mode: ${opts.mode}`);
if (opts.competitor) console.log(` Competitor: ${opts.competitor}`);
if (opts.country) console.log(` Country: ${opts.country}`);
@@ -95,6 +95,67 @@ async function main(): Promise<void> {
}
}
/**
* Compute the peak hours array from surge pattern data.
* Returns the longest contiguous block of hours where avg multiplier > 1.05.
* Used by both formula saving and surge insight saving.
*/
function computePeakHours(surgePatterns: Array<{ surgePrices: Array<{ time: string; multiplier: number }> }>): {
peakHours: number[];
peakStart: number;
peakEnd: number;
} {
// Aggregate all route multipliers per hour of day
const hourMults = new Map<number, number[]>();
for (const sr of surgePatterns) {
for (const sp of sr.surgePrices) {
const h = parseInt(sp.time.split(':')[0]);
if (isNaN(h)) continue;
if (!hourMults.has(h)) hourMults.set(h, []);
hourMults.get(h)!.push(sp.multiplier);
}
}
// Keep only hours where the average multiplier exceeds 1.05
const peakHours: number[] = [];
for (const [h, mults] of hourMults) {
const avg = mults.reduce((a, b) => a + b, 0) / mults.length;
if (avg > 1.05) peakHours.push(h);
}
peakHours.sort((a, b) => a - b);
// Find the longest contiguous block of peak hours
let bestStart = 0, bestEnd = 0, bestLen = 0;
let curStart = -1, curEnd = -1;
for (let i = 0; i < peakHours.length; i++) {
if (curStart < 0) {
curStart = peakHours[i];
curEnd = peakHours[i];
} else if (peakHours[i] === curEnd + 1) {
curEnd = peakHours[i];
} else {
if (curEnd - curStart > bestLen) {
bestLen = curEnd - curStart;
bestStart = curStart;
bestEnd = curEnd;
}
curStart = peakHours[i];
curEnd = peakHours[i];
}
}
if (curEnd - curStart > bestLen) {
bestLen = curEnd - curStart;
bestStart = curStart;
bestEnd = curEnd;
}
const peakStart = bestLen > 0 ? bestStart : 0;
const peakEnd = bestLen > 0 ? bestEnd : 23;
return { peakHours, peakStart, peakEnd };
}
async function processCompetitor(
pool: Pool,
comp: CompetitorEntry,
@@ -126,11 +187,18 @@ async function processCompetitor(
competitorName: comp.competitor_name,
countryCode: comp.country_code,
cleanOutliers: true,
surgeThreshold: 0.12,
// surgeThresholdFraction defaults to 0.05 (5% of median price) — currency-agnostic
tierCount: 3,
});
// Save tier formulas
// --- Compute peak hours once, reuse in both formulas and surge insights ---
const { peakHours, peakStart, peakEnd } = report.surgePatterns.length > 0
? computePeakHours(report.surgePatterns)
: { peakHours: [], peakStart: 0, peakEnd: 23 };
const peakHoursJson = JSON.stringify(peakHours);
// --- Save tier formulas (includes actual peak hours) ---
const formulas = report.tiers
.filter(t => t.regression !== null && t.regression!.sampleCount >= 5)
.map(tier => ({
@@ -145,29 +213,23 @@ async function processCompetitor(
rSquared: tier.regression!.rSquared,
sampleCount: tier.regression!.sampleCount,
surgeMultiplier: 1.0,
peakHours: '[]',
// Now populated with real peak hours instead of always '[]'
peakHours: peakHoursJson,
}));
if (formulas.length > 0) {
await saveFormulas(pool, formulas);
console.log(` ✅ Saved ${formulas.length} tier formulas`);
if (peakHours.length > 0) {
console.log(` Peak hours stored: [${peakHours.join(', ')}]`);
}
}
// Save surge insights — use the average multiplier across all detected routes
// --- Save surge insights ---
if (opts.mode !== 'report' && report.surgePatterns.length > 0) {
const avgMultiplier = report.surgePatterns
.reduce((sum, sr) => sum + sr.maxMultiplier, 0) / report.surgePatterns.length;
// Find peak hour range from aggregate pattern
const allHours = report.surgePatterns.flatMap(sr =>
sr.surgePrices
.filter(sp => sp.multiplier > 1.05)
.map(sp => parseInt(sp.time.split(':')[0]))
);
const peakStart = allHours.length > 0 ? Math.min(...allHours) : 0;
const peakEnd = allHours.length > 0 ? Math.max(...allHours) : 23;
const surgeInsights = [{
competitorName: comp.competitor_name,
countryCode: comp.country_code,
@@ -178,7 +240,7 @@ async function processCompetitor(
}];
await saveSurgeInsights(pool, surgeInsights);
console.log(` ✅ Saved surge insight: avg ${(avgMultiplier).toFixed(3)}x, hours ${peakStart}:00-${peakEnd}:00`);
console.log(` ✅ Saved surge insight: avg ${avgMultiplier.toFixed(3)}x, hours ${peakStart}:00-${peakEnd}:00`);
}
}