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;
/**
* @brief Parse fetched prices by symbol
* @param string $symbol Given symbol associated with ID
* @param array<mixed> $prices Array of [N]([timestamp][price])for given year(s)
* @return array<string> Prices for all given symbols
* @brief Make daily average of given prices
* @param array<mixed> $prices Parsed [date => price] entries
* @return array<string> Daily averages in date and price format (one per line)
*/
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
@@ -300,7 +356,7 @@ namespace docker_finance\prices\internal
public function reader(string $symbols): array
{
// Fetched prices for given symbols
// Prepared price journal for given symbols
$stack = [];
// Timestamp based on given year
@@ -309,11 +365,11 @@ namespace docker_finance\prices\internal
utils\CLI::print_normal(" ─ Symbols");
foreach ($this->parse_symbols($symbols) as $id => $symbol) {
$parsed = $this->parse_prices(
$symbol,
$this->getter($id, $timestamp)
);
array_push($stack, $this->make_master($symbol, $parsed));
$parsed = $this->parse_prices($this->getter($id, $timestamp));
$master = $this->make_master($symbol, $this->make_average($parsed));
utils\CLI::print_debug($master);
array_push($stack, $master);
}
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
* @param string $symbol Given symbol associated with CoinGecko's ID
* @param array<mixed> $prices Array of [N]([timestamp][price])for given year(s)
* @brief Parse fetched date and prices
* @param array<mixed> $prices Fetched prices [N]([timestamp][price])
* @details
*
* 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
* 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;
$prev_date = "";
$date_counter = 0;
$prices_stack = []; // date => daily average price
$stack = [];
for($i = 0; $i < count($prices); $i++) {
/**
* Expectation:
*
* array[0] = oldest entry
* array[N][0] = timestamp
* array[N][1] = price
* array[0][0] = timestamp
* array[0][1] = price
*
* array[1] = next hour
* array[N][0] = timestamp
* array[N][1] = price
* array[1][0] = timestamp
* array[1][1] = price
*
* ...etc.
*/
$timestamp = $prices[$i][0] / 1000;
$date = date('Y/m/d', $timestamp);
// Isolate given year.
//
// Isolate given year
//
// If 'all', then all years are needed. Otherwise, for example,
// if the given year is for last year, and the beginning of last
// year was 375 days ago, then upstream will send 375 entries
@@ -129,56 +128,13 @@ namespace docker_finance\prices\internal\prices\crypto
continue;
}
$price = $prices[$i][1];
utils\CLI::print_debug("$date = $price");
//
// Get daily average
//
// 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;
// Push to stack
$stack += [$date => $price];
}
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
* @param string $symbol Given symbol associated with Mobula's ID
* @param array<mixed> $prices Array of [N]([timestamp][price])for given year(s)
* @return array<string> Prices for all given symbols
* @brief Parse fetched date and prices
* @param array<mixed> $prices Fetched prices [N]([timestamp][price])for given year(s)
* @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;
$prev_date = "";
$date_counter = 0;
$stack = []; // date => daily average price
$stack = [];
for($i = 0; $i < count($prices); $i++) {
/**
@@ -287,50 +238,10 @@ namespace docker_finance\prices\internal\prices\crypto
$date = date('Y/m/d', $timestamp);
$price = $prices[$i][1];
utils\CLI::print_debug("$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;
$stack += [$date => $price];
}
utils\CLI::print_debug($stack);
return $stack;
}