chore: retire legacy oil prediction pipeline

Removes everything that was made redundant by the new forecasting
stack. Per docs/superpowers/specs/2026-05-01-prediction-rebuild-design.md,
this was the cleanup planned at the end of Phase 4.

Deleted services and code:
- App\Services\Prediction\Signals\* (the old six-signal aggregator —
  trend, supermarket, day-of-week, brand-behaviour, stickiness,
  regional-momentum, oil — replaced by RidgeRegressionModel).
- App\Services\NationalFuelPredictionService (the post-Phase-4 thin
  shim; StationSearchService now depends on WeeklyForecastService
  directly, set up in the previous commit).
- App\Services\LlmPrediction\* (AbstractLlmPredictionProvider plus
  the four provider implementations — Anthropic, OpenAI, Gemini, and
  the OilPredictionProvider router. Replaced by LlmOverlayService).
- App\Services\BrentPricePredictor and App\Services\Ewma. The Ewma
  helper had no callers left after BrentPricePredictor went.
- App\Models\PricePrediction and its factory.
- App\Console\Commands\PredictOilPrices (the oil:predict command).
- App\Filament\Resources\OilPredictionResource and its Pages.

Schema and dashboard:
- Drop the price_predictions table via a new migration.
- Repoint the Filament StatsOverviewWidget tile from PricePrediction
  to WeeklyForecast so the dashboard reflects the new pipeline.
- Remove the OilPredictionProvider binding from AppServiceProvider.

Test cleanup:
- Delete tests for every retired service.
- Update StatsOverviewWidgetTest to seed weekly_forecasts instead of
  price_predictions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ovidiu U
2026-05-03 08:40:28 +01:00
parent ddd591ad47
commit 203200acb9
32 changed files with 61 additions and 2727 deletions

View File

@@ -1,58 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Services\BrentPricePredictor;
use Illuminate\Console\Command;
use Throwable;
class PredictOilPrices extends Command
{
protected $signature = 'oil:predict {--force : Generate even if the latest price already has a prediction}';
protected $description = 'Generate a Brent crude oil price direction prediction';
public function handle(BrentPricePredictor $predictor): int
{
try {
$latest = $predictor->latestPrice();
if ($latest?->prediction_generated_at !== null && ! $this->option('force')) {
$message = sprintf(
'Prediction already generated for %s at %s.',
$latest->date->toDateString(),
$latest->prediction_generated_at->toDateTimeString(),
);
if (! $this->confirm($message.' Run again anyway?', default: false)) {
$this->info('Skipped.');
return self::SUCCESS;
}
}
$this->info('Generating prediction...');
$prediction = $predictor->generatePrediction();
if ($prediction === null) {
$this->error('Could not generate a prediction — not enough price data.');
return self::FAILURE;
}
$this->info(sprintf(
'Done. [%s] direction=%s confidence=%d%% — %s',
strtoupper($prediction->source->value),
$prediction->direction->value,
$prediction->confidence,
$prediction->reasoning,
));
} catch (Throwable $e) {
$this->error("Prediction failed: {$e->getMessage()}");
return self::FAILURE;
}
return self::SUCCESS;
}
}