feat(admin): add Filament resources for the forecasting stack

Adds three resources under a new "Forecasting" navigation group: a full-CRUD
WatchedEventResource for the Layer 5 volatility detector, plus read-only
WeeklyForecastResource and BacktestResource so the ridge model output and
its calibration can be inspected without SQL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ovidiu U
2026-05-03 09:13:05 +01:00
parent 1c46667f56
commit 8dad223d06
25 changed files with 1289 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
<?php
namespace Database\Factories;
use App\Models\WatchedEvent;
use Illuminate\Database\Eloquent\Factories\Factory;
/** @extends Factory<WatchedEvent> */
class WatchedEventFactory extends Factory
{
public function definition(): array
{
$startsAt = fake()->dateTimeBetween('-30 days', '+30 days');
$endsAt = (clone $startsAt)->modify('+'.fake()->numberBetween(1, 14).' days');
return [
'label' => fake()->sentence(3),
'starts_at' => $startsAt,
'ends_at' => $endsAt,
'notes' => fake()->boolean() ? fake()->paragraph() : null,
];
}
public function active(): static
{
return $this->state([
'starts_at' => now()->subDays(2),
'ends_at' => now()->addDays(2),
]);
}
public function inactive(): static
{
return $this->state([
'starts_at' => now()->subDays(30),
'ends_at' => now()->subDays(15),
]);
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Database\Factories;
use App\Models\WeeklyForecast;
use Illuminate\Database\Eloquent\Factories\Factory;
/** @extends Factory<WeeklyForecast> */
class WeeklyForecastFactory extends Factory
{
public function definition(): array
{
return [
'forecast_for' => now()->startOfWeek()->toDateString(),
'model_version' => 'ridge-'.fake()->unique()->bothify('????????'),
'direction' => fake()->randomElement(['rising', 'falling', 'flat']),
'magnitude_pence' => fake()->numberBetween(-300, 300),
'ridge_confidence' => fake()->numberBetween(20, 90),
'flagged_duty_change' => false,
'reasoning' => fake()->paragraph(),
'generated_at' => now(),
];
}
}