diff --git a/container/src/finance/lib/internal/fetch/prices/internal/prices/crypto.php b/container/src/finance/lib/internal/fetch/prices/internal/prices/crypto.php index 8f31057..fa282d7 100644 --- a/container/src/finance/lib/internal/fetch/prices/internal/prices/crypto.php +++ b/container/src/finance/lib/internal/fetch/prices/internal/prices/crypto.php @@ -214,6 +214,145 @@ namespace docker_finance\prices\internal\prices\crypto return 'max'; } } + + /** + * @brief Mobula price aggregator API + * @since docker-finance 1.0.0 + */ + final class Mobula extends internal\Impl + { + public function __construct(utils\Env $env) + { + parent::__construct($env); + } + + /** + * @brief REST API request generator + * @param string $id Symbol's ID to request + * @param string $timestamp Timestamp to request + * @return mixed REST API response data + */ + protected function request(string $id, string $timestamp): mixed + { + // If `key` exists, append to header (used in all plans) + $key = $this->get_env()->get_env('API_PRICES_KEY'); + $domain = 'api.mobula.io'; + $header = []; + if ($key != 'None') { + $header = ["Authorization: $key"]; + } + $url = "https://{$domain}/api/1/market/history?asset={$id}&from={$timestamp}"; + + $response = $this->request_impl($url, $header); + if (array_key_exists('error', $response)) { + throw new \Exception($response['error']); + } + + $prices = $response['data']['price_history']; + utils\CLI::print_debug($prices); + + return $prices; + } + + /** + * @brief Parse given prices for given symbol + * @param string $symbol Given symbol associated with Mobula's ID + * @param array $prices Array of [N]([timestamp][price])for given year(s) + * @return array Prices for all given symbols + */ + protected function parse_prices(string $symbol, array $prices): array + { + $total = 0; + $prev_date = ""; + $date_counter = 0; + + $stack = []; // date => daily average price + + for($i = 0; $i < count($prices); $i++) { + /** + * Expectation: + * + * array[0] = oldest entry + * array[0][0] = timestamp + * array[0][1] = price + * + * array[1] = next entry + * array[1][0] = timestamp + * array[1][1] = price + * + * ...etc. + */ + + $timestamp = $prices[$i][0] / 1000; + $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; + } + + return $stack; + } + + /** + * @brief Make Mobula timestamp used in request + * @param string $year Given year + * @return mixed Unix timestamp in milliseconds + */ + protected function make_timestamp(string $year): mixed + { + // Number of days back to beginning of given year + if ($year != 'all') { + $timestamp = strtotime($this->get_env()->get_env('API_FETCH_YEAR') . '-01-01') * 1000; + utils\CLI::print_debug($timestamp); + return $timestamp; + } + + // From genesis to present + return ""; + } + } + } // namespace docker_finance\prices\internal\prices\crypto //! @since docker-finance 1.0.0 @@ -244,6 +383,26 @@ namespace docker_finance\prices\internal\prices $this->api->fetcher(); } } + + /** + * @brief Facade for Mobula implementation + * @ingroup php_prices + * @since docker-finance 1.0.0 + */ + final class Mobula extends \docker_finance\prices\API + { + private crypto\Mobula $api; //!< Internal API + + public function __construct(utils\Env $env) + { + $this->api = new crypto\Mobula($env); + } + + public function fetch(): void + { + $this->api->fetcher(); + } + } } // namespace docker_finance\prices\internal\prices # vim: sw=4 sts=4 si ai et