php: fetch: prices: refactor parsing; common impl

- Move daily average into common impl
- Update parsing function signature
- Update documentation
This commit is contained in:
2024-06-16 03:17:59 -07:00
parent 9d1c0c9c57
commit bf4c9bf61a
2 changed files with 90 additions and 123 deletions

View File

@@ -192,12 +192,68 @@ namespace docker_finance\prices\internal
abstract protected function make_timestamp(string $year): mixed; abstract protected function make_timestamp(string $year): mixed;
/** /**
* @brief Parse fetched prices by symbol * @brief Make daily average of given prices
* @param string $symbol Given symbol associated with ID * @param array<mixed> $prices Parsed [date => price] entries
* @param array<mixed> $prices Array of [N]([timestamp][price])for given year(s) * @return array<string> Daily averages in date and price format (one per line)
* @return array<string> Prices for all given symbols
*/ */
abstract protected function parse_prices(string $symbol, array $prices): array; private function make_average(array $prices): array
{
$total = 0;
$prev_date = "";
$date_counter = 0;
$stack = [];
foreach ($prices as $date => $price) {
// Either a kick-off date or a single daily average (per their API)
// NOTE: this *MUST* be overwritten below if computing non-daily
if ($prev_date == "") {
$stack[$date] = $price;
}
// Average will be based on received dates
if ($prev_date == $date) {
$total += $price;
$date_counter++;
} else {
// Aggregator provided a single date, treat as the daily average
if (!$total) {
$total = $price;
}
// Avoid divide by zero (if only a single date was provided)
if (!$date_counter) {
$date_counter = 1;
}
// Finished previous date, calculate and push to stack
$average = $total / $date_counter;
// Always overwrite with most recent daily average
if ($date_counter > 1) {
$stack[$prev_date] = $average;
} else {
$stack[$date] = $average;
}
$total = 0;
$date_counter = 0;
}
$prev_date = $date;
}
utils\CLI::print_debug($stack);
return $stack;
}
/**
* @brief Parse fetched date and prices
* @param array<mixed> $prices Fetched prices [N]([timestamp][price])
* @return array<string> Date and prices without ID or symbol
*/
abstract protected function parse_prices(array $prices): array;
/** /**
* @brief Create data for master price journal file * @brief Create data for master price journal file
@@ -300,7 +356,7 @@ namespace docker_finance\prices\internal
public function reader(string $symbols): array public function reader(string $symbols): array
{ {
// Fetched prices for given symbols // Prepared price journal for given symbols
$stack = []; $stack = [];
// Timestamp based on given year // Timestamp based on given year
@@ -309,11 +365,11 @@ namespace docker_finance\prices\internal
utils\CLI::print_normal(" ─ Symbols"); utils\CLI::print_normal(" ─ Symbols");
foreach ($this->parse_symbols($symbols) as $id => $symbol) { foreach ($this->parse_symbols($symbols) as $id => $symbol) {
$parsed = $this->parse_prices( $parsed = $this->parse_prices($this->getter($id, $timestamp));
$symbol, $master = $this->make_master($symbol, $this->make_average($parsed));
$this->getter($id, $timestamp)
); utils\CLI::print_debug($master);
array_push($stack, $this->make_master($symbol, $parsed)); array_push($stack, $master);
} }
utils\CLI::print_custom(" \e[32m│\e[0m\n"); utils\CLI::print_custom(" \e[32m│\e[0m\n");

View File

@@ -76,9 +76,8 @@ namespace docker_finance\prices\internal\prices\crypto
} }
/** /**
* @brief Parse given prices for given symbol * @brief Parse fetched date and prices
* @param string $symbol Given symbol associated with CoinGecko's ID * @param array<mixed> $prices Fetched prices [N]([timestamp][price])
* @param array<mixed> $prices Array of [N]([timestamp][price])for given year(s)
* @details * @details
* *
* Parses historical market data include price, market cap * Parses historical market data include price, market cap
@@ -89,36 +88,36 @@ namespace docker_finance\prices\internal\prices\crypto
* - 1 - 90 days from current time = hourly data * - 1 - 90 days from current time = hourly data
* above 90 days from current time = daily data (00:00 UTC) * above 90 days from current time = daily data (00:00 UTC)
* *
* @return array<string> Prices for all given symbols * @return array<string> Date and prices without ID or symbol
*/ */
protected function parse_prices(string $symbol, array $prices): array protected function parse_prices(array $prices): array
{ {
$total = 0; $total = 0;
$prev_date = ""; $prev_date = "";
$date_counter = 0; $date_counter = 0;
$prices_stack = []; // date => daily average price $stack = [];
for($i = 0; $i < count($prices); $i++) { for($i = 0; $i < count($prices); $i++) {
/** /**
* Expectation: * Expectation:
*
* array[0] = oldest entry * array[0] = oldest entry
* array[N][0] = timestamp * array[0][0] = timestamp
* array[N][1] = price * array[0][1] = price
* *
* array[1] = next hour * array[1] = next hour
* array[N][0] = timestamp * array[1][0] = timestamp
* array[N][1] = price * array[1][1] = price
*
* ...etc. * ...etc.
*/ */
$timestamp = $prices[$i][0] / 1000; $timestamp = $prices[$i][0] / 1000;
$date = date('Y/m/d', $timestamp); $date = date('Y/m/d', $timestamp);
// Isolate given year.
// //
// Isolate given year
//
// If 'all', then all years are needed. Otherwise, for example, // If 'all', then all years are needed. Otherwise, for example,
// if the given year is for last year, and the beginning of last // if the given year is for last year, and the beginning of last
// year was 375 days ago, then upstream will send 375 entries // year was 375 days ago, then upstream will send 375 entries
@@ -129,56 +128,13 @@ namespace docker_finance\prices\internal\prices\crypto
continue; continue;
} }
$price = $prices[$i][1]; $price = $prices[$i][1];
utils\CLI::print_debug("$date = $price");
// // Push to stack
// Get daily average $stack += [$date => $price];
//
// Either a kick-off date for hourlies or a single average (per their API)
// NOTE: this *MUST* be overwritten below if computing hourly
if ($prev_date == "") {
$prices_stack[$date] = $price;
}
// Average will be based on hourly
// TODO: they volume-weight their real-time average. However,
// based on the data they provid to API clients, how do they
// calculate their *daily* average? It's not opening/closing
// (based on what they provide) or averaging their hourly.
// This can be confirmed when given a previous year or 'max'.
if ($prev_date == $date) {
$total += $price;
$date_counter++;
} else {
// They provided a single date, and *that* is the daily average
if (!$total) {
$total = $price;
}
// Avoid divide by zero (if only a single date was provided)
if (!$date_counter) {
$date_counter = 1;
}
// Finished previous date, calculate and push to stack
$average = $total / $date_counter;
// Always overwrite with most recent daily average
if ($date_counter > 1) {
$prices_stack[$prev_date] = $average;
} else {
$prices_stack[$date] = $average;
}
$total = 0;
$date_counter = 0;
}
$prev_date = $date;
} }
return $prices_stack; utils\CLI::print_debug($stack);
return $stack;
} }
/** /**
@@ -255,18 +211,13 @@ namespace docker_finance\prices\internal\prices\crypto
} }
/** /**
* @brief Parse given prices for given symbol * @brief Parse fetched date and prices
* @param string $symbol Given symbol associated with Mobula's ID * @param array<mixed> $prices Fetched prices [N]([timestamp][price])for given year(s)
* @param array<mixed> $prices Array of [N]([timestamp][price])for given year(s) * @return array<string> Date and prices without ID or symbol
* @return array<string> Prices for all given symbols
*/ */
protected function parse_prices(string $symbol, array $prices): array protected function parse_prices(array $prices): array
{ {
$total = 0; $stack = [];
$prev_date = "";
$date_counter = 0;
$stack = []; // date => daily average price
for($i = 0; $i < count($prices); $i++) { for($i = 0; $i < count($prices); $i++) {
/** /**
@@ -287,50 +238,10 @@ namespace docker_finance\prices\internal\prices\crypto
$date = date('Y/m/d', $timestamp); $date = date('Y/m/d', $timestamp);
$price = $prices[$i][1]; $price = $prices[$i][1];
utils\CLI::print_debug("$date = $price"); $stack += [$date => $price];
//
// Get daily average
//
// Either a kick-off date or a single daily average (per their API)
// NOTE: this *MUST* be overwritten below if computing non-daily
if ($prev_date == "") {
$stack[$date] = $price;
}
// Average will be based on given dates (currently every 6 hours or 5 minutes)
if ($prev_date == $date) {
$total += $price;
$date_counter++;
} else {
// They provided a single date, and *that* is the daily average
if (!$total) {
$total = $price;
}
// Avoid divide by zero (if only a single date was provided)
if (!$date_counter) {
$date_counter = 1;
}
// Finished previous date, calculate and push to stack
$average = $total / $date_counter;
// Always overwrite with most recent daily average
if ($date_counter > 1) {
$stack[$prev_date] = $average;
} else {
$stack[$date] = $average;
}
$total = 0;
$date_counter = 0;
}
$prev_date = $date;
} }
utils\CLI::print_debug($stack);
return $stack; return $stack;
} }