Skip to content

Commit

Permalink
Merge branch 'mainsail-develop' into test/fix-intermittent-view-model…
Browse files Browse the repository at this point in the history
…-test
  • Loading branch information
alexbarnsley authored Feb 4, 2025
2 parents 61b5182 + bb2da61 commit fa7db49
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 156 deletions.
29 changes: 5 additions & 24 deletions app/Services/Cache/CryptoDataCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use App\Contracts\Cache as Contract;
use App\Services\Cache\Concerns\ManagesCache;
use Closure;
use Illuminate\Cache\TaggedCache;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
Expand All @@ -15,24 +14,6 @@ final class CryptoDataCache implements Contract
{
use ManagesCache;

public function setHistorical(string $source, string $target, string $format, Closure $callback): Collection
{
$data = $callback();

$this->put(sprintf('historical/%s/%s/%s', $source, $target, $format), $data);

return $data;
}

public function setHistoricalHourly(string $source, string $target, string $format, int $limit, Closure $callback): Collection
{
$data = $callback();

$this->put(sprintf('historical/%s/%s/%s/%s', $source, $target, $format, $limit), $data);

return $data;
}

public function getPrices(string $currency): Collection
{
return $this->get(sprintf('prices/%s', $currency), collect([]));
Expand All @@ -45,11 +26,6 @@ public function setPrices(string $currency, Collection $prices): Collection
return $prices;
}

public function getCache(): TaggedCache
{
return Cache::tags('crypto_compare');
}

// Add caches for volume in all currencies
public function getVolume(string $currency): ?string
{
Expand Down Expand Up @@ -86,4 +62,9 @@ public function getHistoricalHourlyFullResponse(string $source, string $target):
{
return $this->get(sprintf('historical_full/hourly/%s/%s', $source, $target), []);
}

public function getCache(): TaggedCache
{
return Cache::tags('crypto_data');
}
}
134 changes: 63 additions & 71 deletions app/Services/MarketDataProviders/CoinGecko.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,81 +19,73 @@ final class CoinGecko extends AbstractMarketDataProvider
{
public function historical(string $source, string $target, string $format = 'Y-m-d'): Collection
{
$cache = new CryptoDataCache();

return $cache->setHistorical($source, $target, $format, function () use ($source, $target, $format, $cache): Collection {
$params = [
'vs_currency' => Str::lower($target),
'days' => min(365, Network::epoch()->diffInDays() + 1), // +1 to handle edge case where first day is not returned in full depending on time of day
'interval' => 'daily',
];

$data = null;

try {
$data = Http::get(
'https://api.coingecko.com/api/v3/coins/'.Str::lower($source).'/market_chart',
$params
)->json();
} catch (\Throwable) {
//
}

if ($this->isEmptyResponse($data) || $this->isThrottledResponse($data)) {
/** @var Collection<int, mixed> */
return collect([]);
}

// Store value in cache for others to use (statistics)
$cache->setHistoricalFullResponse($source, $target, $data);

/** @var array<int, array<int, string>> */
$prices = $data['prices'];

return collect($prices)
->mapWithKeys(fn ($item) => [Carbon::createFromTimestampMs($item[0])->format($format) => $item[1]]);
});
$params = [
'vs_currency' => Str::lower($target),
'days' => min(365, Network::epoch()->diffInDays() + 1), // +1 to handle edge case where first day is not returned in full depending on time of day
'interval' => 'daily',
];

$data = null;

try {
$data = Http::get(
'https://api.coingecko.com/api/v3/coins/'.Str::lower($source).'/market_chart',
$params
)->json();
} catch (\Throwable) {
//
}

if ($this->isEmptyResponse($data) || $this->isThrottledResponse($data)) {
/** @var Collection<int, mixed> */
return collect([]);
}

// Store value in cache for others to use (statistics)
(new CryptoDataCache())->setHistoricalFullResponse($source, $target, $data);

/** @var array<int, array<int, string>> */
$prices = $data['prices'];

return collect($prices)
->mapWithKeys(fn ($item) => [Carbon::createFromTimestampMs($item[0])->format($format) => $item[1]]);
}

public function historicalHourly(string $source, string $target, int $limit = 23, string $format = 'Y-m-d H:i:s'): Collection
{
$cache = new CryptoDataCache();

return $cache->setHistoricalHourly($source, $target, $format, $limit, function () use ($source, $target, $format, $limit, $cache): Collection {
$params = [
'vs_currency' => Str::lower($target),
'days' => strval(ceil($limit / 24)),
];

$data = null;

try {
$data = Http::get(
'https://api.coingecko.com/api/v3/coins/'.Str::lower($source).'/market_chart',
$params
)->json();
} catch (\Throwable) {
//
}

if ($this->isEmptyResponse($data) || $this->isThrottledResponse($data)) {
/** @var Collection<int, mixed> */
return collect([]);
}

// Store value in cache for others to use (statistics)
$cache->setHistoricalHourlyFullResponse($source, $target, $data);

/** @var array<string, array<string, string>> $data */
return collect($data['prices'])
->groupBy(fn ($item) => Carbon::createFromTimestampMsUTC($item[0])->format('Y-m-d H:00:00'))
->mapWithKeys(fn ($items, $day) => [
/* @phpstan-ignore-next-line */
Carbon::createFromFormat('Y-m-d H:i:s', $day)->format($format) => collect($items)->average(fn ($item) => $item[1]),
])
// Take the last $limit items (since the API returns a whole days and the limit is per hour)
->splice(-$limit - 1);
});
$params = [
'vs_currency' => Str::lower($target),
'days' => strval(ceil($limit / 24)),
];

$data = null;

try {
$data = Http::get(
'https://api.coingecko.com/api/v3/coins/'.Str::lower($source).'/market_chart',
$params
)->json();
} catch (\Throwable) {
//
}

if ($this->isEmptyResponse($data) || $this->isThrottledResponse($data)) {
/** @var Collection<int, mixed> */
return collect([]);
}

// Store value in cache for others to use (statistics)
(new CryptoDataCache())->setHistoricalHourlyFullResponse($source, $target, $data);

/** @var array<string, array<string, string>> $data */
return collect($data['prices'])
->groupBy(fn ($item) => Carbon::createFromTimestampMsUTC($item[0])->format('Y-m-d H:00:00'))
->mapWithKeys(fn ($items, $day) => [
/* @phpstan-ignore-next-line */
Carbon::createFromFormat('Y-m-d H:i:s', $day)->format($format) => collect($items)->average(fn ($item) => $item[1]),
])
// Take the last $limit items (since the API returns a whole days and the limit is per hour)
->splice(-$limit - 1);
}

public function priceAndPriceChange(string $baseCurrency, Collection $targetCurrencies): Collection
Expand Down
117 changes: 56 additions & 61 deletions app/Services/MarketDataProviders/CryptoCompare.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use App\DTO\MarketData;
use App\Facades\Network;
use App\Models\Exchange;
use App\Services\Cache\CryptoDataCache;
use ARKEcosystem\Foundation\NumberFormatter\ResolveScientificNotation;
use Carbon\Carbon;
use Illuminate\Support\Arr;
Expand All @@ -18,70 +17,66 @@ final class CryptoCompare extends AbstractMarketDataProvider
{
public function historical(string $source, string $target, string $format = 'Y-m-d'): Collection
{
return (new CryptoDataCache())->setHistorical($source, $target, $format, function () use ($source, $target, $format): Collection {
$data = null;

try {
$data = Http::get(
'https://min-api.cryptocompare.com/data/histoday',
[
'fsym' => $source,
'tsym' => $target,
'toTs' => Carbon::now()->unix(),
'limit' => Network::epoch()->diffInDays(),
]
)->json();
} catch (\Throwable) {
//
}

if ($this->isEmptyResponse($data) || $this->isThrottledResponse($data)) {
/** @var Collection<int, mixed> */
return collect([]);
}

/** @var array<int, array<string, string>> */
$prices = $data['Data'];

return collect($prices)
->groupBy(fn ($day) => Carbon::createFromTimestamp($day['time'])->format($format))
->mapWithKeys(fn ($transactions, $day) => [$day => $transactions->sum('close')]);
});
$data = null;

try {
$data = Http::get(
'https://min-api.cryptocompare.com/data/histoday',
[
'fsym' => $source,
'tsym' => $target,
'toTs' => Carbon::now()->unix(),
'limit' => Network::epoch()->diffInDays(),
]
)->json();
} catch (\Throwable) {
//
}

if ($this->isEmptyResponse($data) || $this->isThrottledResponse($data)) {
/** @var Collection<int, mixed> */
return collect([]);
}

/** @var array<int, array<string, string>> */
$prices = $data['Data'];

return collect($prices)
->groupBy(fn ($day) => Carbon::createFromTimestamp($day['time'])->format($format))
->mapWithKeys(fn ($transactions, $day) => [$day => $transactions->sum('close')]);
}

public function historicalHourly(string $source, string $target, int $limit = 23, string $format = 'Y-m-d H:i:s'): Collection
{
return (new CryptoDataCache())->setHistoricalHourly($source, $target, $format, $limit, function () use ($source, $target, $format, $limit): Collection {
$data = null;

try {
$data = Http::get(
'https://min-api.cryptocompare.com/data/histohour',
[
'fsym' => $source,
'tsym' => $target,
'toTs' => Carbon::now()->unix(),
'limit' => $limit,
]
)->json();
} catch (\Throwable) {
//
}

if ($this->isEmptyResponse($data) || $this->isThrottledResponse($data)) {
/** @var Collection<int, mixed> */
return collect([]);
}

/** @var array<int, array<string, string>> */
$prices = $data['Data'];

return collect($prices)
->groupBy(fn ($day) => Carbon::createFromTimestamp($day['time'])->format($format))
->mapWithKeys(fn ($transactions, $day) => [
$day => ResolveScientificNotation::execute($transactions->sum('close')),
]);
});
$data = null;

try {
$data = Http::get(
'https://min-api.cryptocompare.com/data/histohour',
[
'fsym' => $source,
'tsym' => $target,
'toTs' => Carbon::now()->unix(),
'limit' => $limit,
]
)->json();
} catch (\Throwable) {
//
}

if ($this->isEmptyResponse($data) || $this->isThrottledResponse($data)) {
/** @var Collection<int, mixed> */
return collect([]);
}

/** @var array<int, array<string, string>> */
$prices = $data['Data'];

return collect($prices)
->groupBy(fn ($day) => Carbon::createFromTimestamp($day['time'])->format($format))
->mapWithKeys(fn ($transactions, $day) => [
$day => ResolveScientificNotation::execute($transactions->sum('close')),
]);
}

public function priceAndPriceChange(string $baseCurrency, Collection $targetCurrencies): Collection
Expand Down

0 comments on commit fa7db49

Please sign in to comment.