refactor: persist EWMA only on LLM failure, dedup EWMA helper
Audit items #7 and #5. #7 — BrentPricePredictor::generatePrediction previously wrote both an EWMA row and an LLM row to price_predictions on every run. The downstream OilSignal already prefers llm_with_context > llm > ewma, so the EWMA row was dead weight 95% of the time. Now we try LLM first; if it returns null (no API key, parse failure, etc.) we compute and persist EWMA as a real fallback. This also avoids redundant work on the success path. Updated the "stores both" test to "stores only LLM" — asserts no EWMA row is written when the provider succeeds. #5 — BrentPricePredictor and AnthropicPredictionProvider both had byte-identical computeEwma() methods with identical EWMA_ALPHA = 0.3 constants. Extracted to App\Services\Ewma::compute() and dropped both private methods + their alpha constants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ namespace App\Services\LlmPrediction;
|
||||
|
||||
use App\Enums\PredictionSource;
|
||||
use App\Models\PricePrediction;
|
||||
use App\Services\Ewma;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -11,8 +12,6 @@ use Throwable;
|
||||
|
||||
class AnthropicPredictionProvider extends AbstractLlmPredictionProvider
|
||||
{
|
||||
private const float EWMA_ALPHA = 0.3;
|
||||
|
||||
/**
|
||||
* Tries web-search-enriched prediction first, falls back to basic tool use.
|
||||
* Overrides the parent flow because Anthropic uses two phases (web search
|
||||
@@ -112,9 +111,9 @@ class AnthropicPredictionProvider extends AbstractLlmPredictionProvider
|
||||
private function predictBasic(Collection $prices): ?PricePrediction
|
||||
{
|
||||
$chronological = $prices->sortBy('date');
|
||||
$ewma3 = $this->computeEwma($chronological->take(-3)->pluck('price_usd')->values()->all());
|
||||
$ewma7 = $this->computeEwma($chronological->take(-7)->pluck('price_usd')->values()->all());
|
||||
$ewma14 = $this->computeEwma($chronological->pluck('price_usd')->values()->all());
|
||||
$ewma3 = Ewma::compute($chronological->take(-3)->pluck('price_usd')->values()->all());
|
||||
$ewma7 = Ewma::compute($chronological->take(-7)->pluck('price_usd')->values()->all());
|
||||
$ewma14 = Ewma::compute($chronological->pluck('price_usd')->values()->all());
|
||||
|
||||
$url = 'https://api.anthropic.com/v1/messages';
|
||||
|
||||
@@ -229,16 +228,4 @@ class AnthropicPredictionProvider extends AbstractLlmPredictionProvider
|
||||
|
||||
return $block['input'] ?? null;
|
||||
}
|
||||
|
||||
/** @param float[] $prices Chronological order (oldest first) */
|
||||
private function computeEwma(array $prices): float
|
||||
{
|
||||
$ema = $prices[0];
|
||||
|
||||
foreach (array_slice($prices, 1) as $price) {
|
||||
$ema = self::EWMA_ALPHA * $price + (1 - self::EWMA_ALPHA) * $ema;
|
||||
}
|
||||
|
||||
return round($ema, 4);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user