asdasd
All checks were successful
Deploy / deploy-staging (push) Successful in 6s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-05-01 03:37:41 +02:00
parent 5a154f896b
commit c444ece852
10 changed files with 436 additions and 152 deletions

View File

@@ -20,7 +20,12 @@ final class AnalyticsService
$baselineAt = (string) ($settings['baseline_measured_at'] ?? '');
$costPlans = is_array($settings['cost_plans'] ?? null) ? $settings['cost_plans'] : [];
$payouts = is_array($settings['payouts'] ?? null) ? $settings['payouts'] : [];
$measurementRates = is_array($settings['measurement_rates'] ?? null) ? $settings['measurement_rates'] : [];
$preferredPriceCurrencies = array_values(array_unique(array_filter([
strtoupper(trim((string) ($settings['report_currency'] ?? ''))),
'USD',
'EUR',
'DOGE',
])));
$baselineTs = $this->utcTimestamp($baselineAt);
$previous = null;
@@ -79,7 +84,7 @@ final class AnalyticsService
$latestPriceByCurrency[(string) $rawPriceCurrency] = $rawPrice;
}
$measurementDerivedPrices = $this->measurementDerivedPrices($measurementRates, (int) ($row['id'] ?? 0));
$measurementDerivedPrices = $this->measurementDerivedPrices($row, $preferredPriceCurrencies);
foreach ($measurementDerivedPrices as $derivedCurrency => $derivedPrice) {
$latestPriceByCurrency[$derivedCurrency] = $derivedPrice;
}
@@ -96,7 +101,7 @@ final class AnalyticsService
}
if ($price === null) {
foreach (['USD', 'EUR'] as $fallbackCurrency) {
$fxPrice = $this->convertAmount(1.0, 'DOGE', $fallbackCurrency);
$fxPrice = $this->convertAmount(1.0, 'DOGE', $fallbackCurrency, $row);
if ($fxPrice !== null && $fxPrice > 0) {
$latestPriceByCurrency[$fallbackCurrency] = $fxPrice;
}
@@ -107,7 +112,7 @@ final class AnalyticsService
}
}
$effectiveDailyCost = $this->effectiveDailyCost($costPlans, $measuredTs, $priceCurrency);
$effectiveDailyCost = $this->effectiveDailyCost($costPlans, $measuredTs, $priceCurrency, $row);
$currentValue = $price !== null ? $visibleCoinsTotal * $price : null;
$currentValueEffective = $price !== null ? $effectiveCoinsTotal * $price : null;
$theoreticalDailyRevenue = ($price !== null && $perDayInterval !== null) ? $perDayInterval * $price : null;
@@ -148,6 +153,7 @@ final class AnalyticsService
'effective_price_per_coin' => $this->roundOrNull($price, 8),
'effective_price_currency' => $priceCurrency,
'price_is_fallback' => $rawPrice === null && $price !== null,
'price_quotes' => $measurementDerivedPrices,
'current_value' => $this->roundOrNull($currentValue, 8),
'current_value_effective' => $this->roundOrNull($currentValueEffective, 8),
'effective_daily_cost' => $this->roundOrNull($effectiveDailyCost, 8),
@@ -183,10 +189,18 @@ final class AnalyticsService
$latest = $measurements[array_key_last($measurements)];
$latestPriceByCurrency = [];
foreach ($measurements as $measurement) {
if ($measurement['price_per_coin'] !== null && $measurement['price_currency'] !== null) {
$latestPriceByCurrency[(string) $measurement['price_currency']] = (float) $measurement['price_per_coin'];
$quotes = is_array($measurement['price_quotes'] ?? null) ? $measurement['price_quotes'] : [];
foreach ($quotes as $currency => $price) {
$normalizedCurrency = strtoupper(trim((string) $currency));
if ($normalizedCurrency !== '' && is_numeric($price)) {
$latestPriceByCurrency[$normalizedCurrency] = (float) $price;
}
}
}
$latestEffectiveCurrency = strtoupper(trim((string) ($latest['effective_price_currency'] ?? $latest['price_currency'] ?? '')));
if ($latestEffectiveCurrency !== '' && is_numeric($latest['effective_price_per_coin'] ?? null)) {
$latestPriceByCurrency[$latestEffectiveCurrency] = (float) $latest['effective_price_per_coin'];
}
$payouts = is_array($settings['payouts'] ?? null) ? $settings['payouts'] : [];
$purchasedMiners = is_array($settings['purchased_miners'] ?? null) ? $settings['purchased_miners'] : [];
@@ -221,7 +235,7 @@ final class AnalyticsService
: (is_numeric($linkedOffer['effective_price_amount'] ?? null) ? (float) $linkedOffer['effective_price_amount'] : $targetAmount);
}
$price = $latestPriceByCurrency[$currency] ?? $this->convertLatestPrice($latestPriceByCurrency, $currency);
$price = $latestPriceByCurrency[$currency] ?? $this->convertLatestPrice($latestPriceByCurrency, $currency, $latest);
$requiredDoge = ($price && $targetAmount !== null) ? $targetAmount / $price : null;
$remainingDoge = $requiredDoge !== null ? $requiredDoge - (float) ($latest['coins_total_effective'] ?? $latest['coins_total']) : null;
$remainingDays = (
@@ -264,7 +278,8 @@ final class AnalyticsService
is_array($settings['cost_plans'] ?? null) ? $settings['cost_plans'] : [],
$purchasedMiners,
$this->utcTimestamp((string) ($latest['measured_at'] ?? '')),
$latestCurrency
$latestCurrency,
$latest
)
: null;
$currentDailyRevenue = is_numeric($latest['theoretical_daily_revenue'] ?? null) ? (float) $latest['theoretical_daily_revenue'] : null;
@@ -399,7 +414,7 @@ final class AnalyticsService
return $value === null ? null : round($value, $precision);
}
private function effectiveDailyCost(array $costPlans, int $measurementTs, ?string $currency): ?float
private function effectiveDailyCost(array $costPlans, int $measurementTs, ?string $currency, ?array $fxContext = null): ?float
{
if ($currency === null) {
return null;
@@ -429,7 +444,8 @@ final class AnalyticsService
$convertedDailyCost = $this->convertAmount(
$planDailyCost,
(string) ($plan['currency'] ?? ''),
$currency
$currency,
$fxContext
);
if ($convertedDailyCost === null) {
continue;
@@ -442,10 +458,10 @@ final class AnalyticsService
return $matched ? $dailyTotal : null;
}
private function convertLatestPrice(array $latestPriceByCurrency, string $targetCurrency): ?float
private function convertLatestPrice(array $latestPriceByCurrency, string $targetCurrency, ?array $fxContext = null): ?float
{
foreach ($latestPriceByCurrency as $sourceCurrency => $price) {
$converted = $this->convertAmount((float) $price, (string) $sourceCurrency, $targetCurrency);
$converted = $this->convertAmount((float) $price, (string) $sourceCurrency, $targetCurrency, $fxContext);
if ($converted !== null) {
return $converted;
}
@@ -474,33 +490,32 @@ final class AnalyticsService
return is_string($derivedFirst) ? $derivedFirst : null;
}
private function measurementDerivedPrices(array $measurementRates, int $measurementId): array
private function measurementDerivedPrices(array $measurement, array $targetCurrencies): array
{
if ($measurementId <= 0 || $measurementRates === []) {
$rawPrice = is_numeric($measurement['price_per_coin'] ?? null) ? (float) $measurement['price_per_coin'] : null;
$rawCurrency = strtoupper(trim((string) ($measurement['price_currency'] ?? '')));
if ($rawPrice === null || $rawPrice <= 0 || $rawCurrency === '') {
return [];
}
$prices = [];
foreach ($measurementRates as $row) {
if ((int) ($row['measurement_id'] ?? 0) !== $measurementId) {
$prices = [$rawCurrency => $rawPrice];
foreach ($targetCurrencies as $targetCurrency) {
$targetCurrency = strtoupper(trim((string) $targetCurrency));
if ($targetCurrency === '' || isset($prices[$targetCurrency])) {
continue;
}
$baseCurrency = strtoupper(trim((string) ($row['base_currency'] ?? '')));
$quoteCurrency = strtoupper(trim((string) ($row['target_currency'] ?? $row['quote_currency'] ?? '')));
$rate = is_numeric($row['rate'] ?? null) ? (float) $row['rate'] : null;
if ($baseCurrency !== 'DOGE' || $quoteCurrency === '' || $rate === null || $rate <= 0) {
continue;
$converted = $this->convertAmount($rawPrice, $rawCurrency, $targetCurrency, $measurement);
if ($converted !== null && $converted > 0) {
$prices[$targetCurrency] = $converted;
}
$prices[$quoteCurrency] = $rate;
}
return $prices;
}
private function convertAmount(?float $amount, ?string $fromCurrency, ?string $toCurrency): ?float
private function convertAmount(?float $amount, ?string $fromCurrency, ?string $toCurrency, ?array $fxContext = null): ?float
{
if ($amount === null || $fromCurrency === null || $toCurrency === null) {
return null;
@@ -520,7 +535,12 @@ final class AnalyticsService
return null;
}
return $this->fx->convert($amount, $from, $to);
$fetchId = isset($fxContext['fx_fetch_id']) && is_numeric($fxContext['fx_fetch_id'])
? (int) $fxContext['fx_fetch_id']
: null;
$at = is_string($fxContext['measured_at'] ?? null) ? (string) $fxContext['measured_at'] : null;
return $this->fx->convertAt($amount, $from, $to, $at, null, $fetchId);
}
private function totalHashrateMh(array $entries): float
@@ -541,7 +561,7 @@ final class AnalyticsService
return $total;
}
private function totalPurchasedMinerCost(array $purchasedMiners, string $targetCurrency, ?int $measurementTs = null): ?float
private function totalPurchasedMinerCost(array $purchasedMiners, string $targetCurrency, ?int $measurementTs = null, ?array $fxContext = null): ?float
{
$target = strtoupper(trim($targetCurrency));
if ($target === '') {
@@ -564,7 +584,7 @@ final class AnalyticsService
continue;
}
$converted = $this->convertAmount($amount, $currency, $target);
$converted = $this->convertAmount($amount, $currency, $target, $fxContext);
if ($converted === null) {
continue;
}
@@ -576,7 +596,7 @@ final class AnalyticsService
return $matched ? $total : null;
}
private function totalInvestmentBasis(array $costPlans, array $purchasedMiners, int $measurementTs, string $targetCurrency): ?float
private function totalInvestmentBasis(array $costPlans, array $purchasedMiners, int $measurementTs, string $targetCurrency, ?array $fxContext = null): ?float
{
$target = strtoupper(trim($targetCurrency));
if ($target === '') {
@@ -586,7 +606,7 @@ final class AnalyticsService
$total = 0.0;
$matched = false;
$purchasedTotal = $this->totalPurchasedMinerCost($purchasedMiners, $target, $measurementTs);
$purchasedTotal = $this->totalPurchasedMinerCost($purchasedMiners, $target, $measurementTs, $fxContext);
if ($purchasedTotal !== null) {
$matched = true;
$total += $purchasedTotal;
@@ -616,7 +636,7 @@ final class AnalyticsService
continue;
}
$converted = $this->convertAmount($amount, $currency, $target);
$converted = $this->convertAmount($amount, $currency, $target, $fxContext);
if ($converted === null) {
continue;
}
@@ -688,7 +708,7 @@ final class AnalyticsService
: $basePriceCurrency;
$effectivePriceAmount = $basePriceAmount;
if ($basePriceAmount !== null && $basePriceAmount > 0 && $basePriceCurrency !== '' && $effectivePriceCurrency !== '' && strtoupper($basePriceCurrency) !== strtoupper($effectivePriceCurrency)) {
$convertedReference = $this->convertAmount($basePriceAmount, $basePriceCurrency, $effectivePriceCurrency);
$convertedReference = $this->convertAmount($basePriceAmount, $basePriceCurrency, $effectivePriceCurrency, $latest);
if ($convertedReference !== null && $convertedReference > 0) {
$effectivePriceAmount = $convertedReference;
}
@@ -701,7 +721,7 @@ final class AnalyticsService
$expectedDogePerDay = ((float) $latest['doge_per_day_interval'] / $currentHashrateMh) * $offerHashrateMh;
}
$offerCurrencyPrice = $effectivePriceCurrency !== '' ? ($latestPriceByCurrency[$effectivePriceCurrency] ?? $this->convertLatestPrice($latestPriceByCurrency, $effectivePriceCurrency)) : null;
$offerCurrencyPrice = $effectivePriceCurrency !== '' ? ($latestPriceByCurrency[$effectivePriceCurrency] ?? $this->convertLatestPrice($latestPriceByCurrency, $effectivePriceCurrency, $latest)) : null;
$expectedDailyRevenue = ($expectedDogePerDay !== null && $offerCurrencyPrice !== null)
? $expectedDogePerDay * $offerCurrencyPrice
: null;
@@ -752,7 +772,7 @@ final class AnalyticsService
$offerPriceAmount !== null &&
$offerPriceCurrency !== '' &&
$latestCurrency !== ''
) ? $this->convertAmount($offerPriceAmount, $offerPriceCurrency, $latestCurrency) : null;
) ? $this->convertAmount($offerPriceAmount, $offerPriceCurrency, $latestCurrency, $latest) : null;
$scenarioDogePerDay = (
$currentDogePerDay !== null &&
$expectedDogePerDay !== null
@@ -864,7 +884,7 @@ final class AnalyticsService
$runtimeMonths = (int) ($plan['runtime_months'] ?? 0);
if ($runtimeMonths > 0 && is_numeric($plan['total_cost_amount'] ?? null)) {
$runtimeDays = $runtimeMonths * 30.4375;
$dailyCost = $this->convertAmount((float) $plan['total_cost_amount'] / $runtimeDays, (string) ($plan['currency'] ?? ''), $currency);
$dailyCost = $this->convertAmount((float) $plan['total_cost_amount'] / $runtimeDays, (string) ($plan['currency'] ?? ''), $currency, $latest);
if ($dailyCost !== null) {
$cost += $dailyCost;
}
@@ -882,7 +902,7 @@ final class AnalyticsService
$runtimeMonths = (int) ($miner['runtime_months'] ?? 0);
if ($runtimeMonths > 0 && is_numeric($miner['total_cost_amount'] ?? null)) {
$runtimeDays = $runtimeMonths * 30.4375;
$dailyCost = $this->convertAmount((float) $miner['total_cost_amount'] / $runtimeDays, (string) ($miner['currency'] ?? ''), $currency);
$dailyCost = $this->convertAmount((float) $miner['total_cost_amount'] / $runtimeDays, (string) ($miner['currency'] ?? ''), $currency, $latest);
if ($dailyCost !== null) {
$cost += $dailyCost;
}