diff --git a/READMEs/dev.md b/READMEs/dev.md index 3c0bbaebe..eea5a61d4 100644 --- a/READMEs/dev.md +++ b/READMEs/dev.md @@ -69,12 +69,12 @@ pylint pdr_backend/* black ./ ``` +======= Check code coverage: ```console coverage run --omit="*test*" -m pytest # Run all. For subset, add eg: pdr_backend/lake coverage report # show results ``` - ### Local Usage: Run a custom agent Let's say you want to change the trader agent, and use off-the-shelf agents for everything else. Here's how. diff --git a/pdr_backend/analytics/check_network.py b/pdr_backend/analytics/check_network.py index 8788362e9..2c05aa2b8 100644 --- a/pdr_backend/analytics/check_network.py +++ b/pdr_backend/analytics/check_network.py @@ -91,7 +91,6 @@ def get_expected_consume(for_ut: int, token_amt: int) -> Union[float, int]: @enforce_types def check_network_main(ppss: PPSS, lookback_hours: int): web3_pp = ppss.web3_pp - cur_ut = current_ut_s() start_ut = cur_ut - lookback_hours * 60 * 60 query = """ diff --git a/pdr_backend/analytics/get_predictions_info.py b/pdr_backend/analytics/get_predictions_info.py index 4749bc25c..090e8ce6e 100644 --- a/pdr_backend/analytics/get_predictions_info.py +++ b/pdr_backend/analytics/get_predictions_info.py @@ -1,57 +1,38 @@ from typing import Union from enforce_typing import enforce_types - -from pdr_backend.analytics.predictoor_stats import get_cli_statistics from pdr_backend.ppss.ppss import PPSS -from pdr_backend.subgraph.subgraph_predictions import ( - FilterMode, - fetch_filtered_predictions, - get_all_contract_ids_by_owner, -) -from pdr_backend.util.csvs import save_analysis_csv -from pdr_backend.util.networkutil import get_sapphire_postfix -from pdr_backend.util.timeutil import ms_to_seconds, timestr_to_ut +from pdr_backend.lake.gql_data_factory import GQLDataFactory +from pdr_backend.util.timeutil import timestr_to_ut +from pdr_backend.analytics.predictoor_stats import get_feed_summary_stats @enforce_types def get_predictions_info_main( - ppss: PPSS, - feed_addrs_str: Union[str, None], - start_timestr: str, - end_timestr: str, - pq_dir: str, + ppss: PPSS, start_timestr: str, end_timestr: str, feed_addrs_str: Union[str, None] ): - network = get_sapphire_postfix(ppss.web3_pp.network) - start_ut: int = ms_to_seconds(timestr_to_ut(start_timestr)) - end_ut: int = ms_to_seconds(timestr_to_ut(end_timestr)) - - # filter by feed contract address - feed_contract_list = get_all_contract_ids_by_owner( - owner_address=ppss.web3_pp.owner_addrs, - network=network, - ) - feed_contract_list = [f.lower() for f in feed_contract_list] - - if feed_addrs_str: - keep = feed_addrs_str.lower().split(",") - feed_contract_list = [f for f in feed_contract_list if f in keep] + gql_data_factory = GQLDataFactory(ppss) + gql_dfs = gql_data_factory.get_gql_dfs() - # fetch predictions - predictions = fetch_filtered_predictions( - start_ut, - end_ut, - feed_contract_list, - network, - FilterMode.CONTRACT, - payout_only=True, - trueval_only=True, - ) - - if not predictions: + if len(gql_dfs["pdr_predictions"]) == 0: print("No records found. Please adjust start and end times.") return + predictions_df = gql_dfs["pdr_predictions"] - save_analysis_csv(predictions, pq_dir) + # filter by feed addresses + if feed_addrs_str: + feed_addrs_list = feed_addrs_str.lower().split(",") + predictions_df = predictions_df.filter( + predictions_df["ID"] + .map_elements(lambda x: x.split("-")[0]) + .is_in(feed_addrs_list) + ) + + # filter by start and end dates + predictions_df = predictions_df.filter( + (predictions_df["timestamp"] >= timestr_to_ut(start_timestr) / 1000) + & (predictions_df["timestamp"] <= timestr_to_ut(end_timestr) / 1000) + ) - get_cli_statistics(predictions) + feed_summary_df = get_feed_summary_stats(predictions_df) + print(feed_summary_df) diff --git a/pdr_backend/analytics/get_predictoors_info.py b/pdr_backend/analytics/get_predictoors_info.py index e40b6f347..3d4a0f891 100644 --- a/pdr_backend/analytics/get_predictoors_info.py +++ b/pdr_backend/analytics/get_predictoors_info.py @@ -1,42 +1,36 @@ from typing import Union from enforce_typing import enforce_types - -from pdr_backend.analytics.predictoor_stats import get_cli_statistics +from pdr_backend.lake.gql_data_factory import GQLDataFactory +from pdr_backend.analytics.predictoor_stats import get_predictoor_summary_stats from pdr_backend.ppss.ppss import PPSS -from pdr_backend.subgraph.subgraph_predictions import ( - FilterMode, - fetch_filtered_predictions, -) -from pdr_backend.util.csvs import save_prediction_csv -from pdr_backend.util.networkutil import get_sapphire_postfix -from pdr_backend.util.timeutil import ms_to_seconds, timestr_to_ut +from pdr_backend.util.timeutil import timestr_to_ut @enforce_types def get_predictoors_info_main( - ppss: PPSS, - pdr_addrs_str: Union[str, None], - start_timestr: str, - end_timestr: str, - csv_output_dir: str, + ppss: PPSS, start_timestr: str, end_timestr: str, pdr_addrs_str: Union[str, None] ): - network = get_sapphire_postfix(ppss.web3_pp.network) - start_ut: int = ms_to_seconds(timestr_to_ut(start_timestr)) - end_ut: int = ms_to_seconds(timestr_to_ut(end_timestr)) + gql_data_factory = GQLDataFactory(ppss) + gql_dfs = gql_data_factory.get_gql_dfs() - pdr_addrs_filter = [] - if pdr_addrs_str: - pdr_addrs_filter = pdr_addrs_str.lower().split(",") + if len(gql_dfs) == 0: + print("No records found. Please adjust start and end times.") + return + predictions_df = gql_dfs["pdr_predictions"] - predictions = fetch_filtered_predictions( - start_ut, - end_ut, - pdr_addrs_filter, - network, - FilterMode.PREDICTOOR, + # filter by user addresses + if pdr_addrs_str: + pdr_addrs_list = pdr_addrs_str.lower().split(",") + predictions_df = predictions_df.filter( + predictions_df["user"].is_in(pdr_addrs_list) + ) + + # filter by start and end dates + predictions_df = predictions_df.filter( + (predictions_df["timestamp"] >= timestr_to_ut(start_timestr) / 1000) + & (predictions_df["timestamp"] <= timestr_to_ut(end_timestr) / 1000) ) - save_prediction_csv(predictions, csv_output_dir) - - get_cli_statistics(predictions) + predictoor_summary_df = get_predictoor_summary_stats(predictions_df) + print(predictoor_summary_df) diff --git a/pdr_backend/analytics/get_traction_info.py b/pdr_backend/analytics/get_traction_info.py index 5633f2395..54ce096dd 100644 --- a/pdr_backend/analytics/get_traction_info.py +++ b/pdr_backend/analytics/get_traction_info.py @@ -13,25 +13,31 @@ ) from pdr_backend.lake.gql_data_factory import GQLDataFactory from pdr_backend.ppss.ppss import PPSS +from pdr_backend.util.timeutil import timestr_to_ut @enforce_types def get_traction_info_main( ppss: PPSS, start_timestr: str, end_timestr: str, pq_dir: str ): - lake_ss = ppss.lake_ss - lake_ss.d["st_timestr"] = start_timestr - lake_ss.d["fin_timestr"] = end_timestr - gql_data_factory = GQLDataFactory(ppss) gql_dfs = gql_data_factory.get_gql_dfs() - - if len(gql_dfs) == 0: - print("No records found. Please adjust start and end times.") + if len(gql_dfs) == 0 or gql_dfs["pdr_predictions"].shape[0] == 0: + print("No records found. Please adjust start and end times inside ppss.yaml.") return predictions_df = gql_dfs["pdr_predictions"] + # filter by start and end dates + predictions_df = predictions_df.filter( + (predictions_df["timestamp"] >= timestr_to_ut(start_timestr) / 1000) + & (predictions_df["timestamp"] <= timestr_to_ut(end_timestr) / 1000) + ) + + if predictions_df.shape[0] == 0: + print("No records found. Please adjust start and end times params.") + return + # calculate predictoor traction statistics and draw plots stats_df = get_traction_statistics(predictions_df) plot_traction_cum_sum_statistics(stats_df, pq_dir) diff --git a/pdr_backend/analytics/predictoor_stats.py b/pdr_backend/analytics/predictoor_stats.py index 43bb355ed..8880a128a 100644 --- a/pdr_backend/analytics/predictoor_stats.py +++ b/pdr_backend/analytics/predictoor_stats.py @@ -1,11 +1,10 @@ import os -from typing import Dict, List, Set, Tuple, TypedDict +from typing import Set, Tuple, TypedDict import matplotlib.pyplot as plt import polars as pl from enforce_typing import enforce_types -from pdr_backend.subgraph.prediction import Prediction from pdr_backend.util.csvs import get_plots_dir @@ -13,6 +12,7 @@ class PairTimeframeStat(TypedDict): pair: str timeframe: str accuracy: float + exchange: str stake: float payout: float number_of_predictions: int @@ -28,107 +28,41 @@ class PredictoorStat(TypedDict): @enforce_types -def aggregate_prediction_statistics( - all_predictions: List[Prediction], -) -> Tuple[Dict[str, Dict], int]: - """ - Aggregates statistics from a list of prediction objects. It organizes statistics - by currency pair and timeframe and predictor address. For each category, it - tallies the total number of predictions, the number of correct predictions, - and the total stakes and payouts. It also returns the total number of correct - predictions across all categories. - - Args: - all_predictions (List[Prediction]): A list of Prediction objects to aggregate. - - Returns: - Tuple[Dict[str, Dict], int]: A tuple containing a dictionary of aggregated - statistics and the total number of correct predictions. - """ - stats: Dict[str, Dict] = {"pair_timeframe": {}, "predictor": {}} - correct_predictions = 0 - - for prediction in all_predictions: - pair_timeframe_key = (prediction.pair, prediction.timeframe) - predictor_key = prediction.user - source = prediction.source - - is_correct = prediction.prediction == prediction.trueval - - if pair_timeframe_key not in stats["pair_timeframe"]: - stats["pair_timeframe"][pair_timeframe_key] = { - "correct": 0, - "total": 0, - "stake": 0, - "payout": 0.0, - } - - if predictor_key not in stats["predictor"]: - stats["predictor"][predictor_key] = { - "correct": 0, - "total": 0, - "stake": 0, - "payout": 0.0, - "details": set(), - } - - if is_correct: - correct_predictions += 1 - stats["pair_timeframe"][pair_timeframe_key]["correct"] += 1 - stats["predictor"][predictor_key]["correct"] += 1 - - stats["pair_timeframe"][pair_timeframe_key]["total"] += 1 - stats["pair_timeframe"][pair_timeframe_key]["stake"] += prediction.stake - stats["pair_timeframe"][pair_timeframe_key]["payout"] += prediction.payout - - stats["predictor"][predictor_key]["total"] += 1 - stats["predictor"][predictor_key]["stake"] += prediction.stake - stats["predictor"][predictor_key]["payout"] += prediction.payout - stats["predictor"][predictor_key]["details"].add( - (prediction.pair, prediction.timeframe, source) - ) - - return stats, correct_predictions - +def get_feed_summary_stats(predictions_df: pl.DataFrame) -> pl.DataFrame: + # 1 - filter from lake only the rows that you're looking for + df = predictions_df.filter( + ~((pl.col("trueval").is_null()) | (pl.col("payout").is_null())) + ) -@enforce_types -def get_cli_statistics(all_predictions: List[Prediction]) -> None: - total_predictions = len(all_predictions) + # Group by pair + df = df.group_by(["pair", "timeframe"]).agg( + pl.col("source").first().alias("source"), + pl.col("payout").sum().alias("sum_payout"), + pl.col("stake").sum().alias("sum_stake"), + pl.col("prediction").count().alias("num_predictions"), + (pl.col("prediction").sum() / pl.col("pair").count() * 100).alias("accuracy"), + ) - stats, correct_predictions = aggregate_prediction_statistics(all_predictions) + return df - if total_predictions == 0: - print("No predictions found.") - return - if correct_predictions == 0: - print("No correct predictions found.") - return +@enforce_types +def get_predictoor_summary_stats(predictions_df: pl.DataFrame) -> pl.DataFrame: + # 1 - filter from lake only the rows that you're looking for + df = predictions_df.filter( + ~((pl.col("trueval").is_null()) | (pl.col("payout").is_null())) + ) - print(f"Overall Accuracy: {correct_predictions/total_predictions*100:.2f}%") + # Group by pair + df = df.group_by(["user", "pair", "timeframe"]).agg( + pl.col("source").first().alias("source"), + pl.col("payout").sum().alias("sum_payout"), + pl.col("stake").sum().alias("sum_stake"), + pl.col("prediction").count().alias("num_predictions"), + (pl.col("prediction").sum() / pl.col("pair").count() * 100).alias("accuracy"), + ) - for key, stat_pair_timeframe_item in stats["pair_timeframe"].items(): - pair, timeframe = key - accuracy = ( - stat_pair_timeframe_item["correct"] - / stat_pair_timeframe_item["total"] - * 100 - ) - print(f"Accuracy for Pair: {pair}, Timeframe: {timeframe}: {accuracy:.2f}%") - print(f"Total stake: {stat_pair_timeframe_item['stake']}") - print(f"Total payout: {stat_pair_timeframe_item['payout']}") - print(f"Number of predictions: {stat_pair_timeframe_item['total']}\n") - - for predictoor_addr, stat_predictoor_item in stats["predictor"].items(): - accuracy = stat_predictoor_item["correct"] / stat_predictoor_item["total"] * 100 - print(f"Accuracy for Predictoor Address: {predictoor_addr}: {accuracy:.2f}%") - print(f"Stake: {stat_predictoor_item['stake']}") - print(f"Payout: {stat_predictoor_item['payout']}") - print(f"Number of predictions: {stat_predictoor_item['total']}") - print("Details of Predictions:") - for detail in stat_predictoor_item["details"]: - print(f"Pair: {detail[0]}, Timeframe: {detail[1]}, Source: {detail[2]}") - print("\n") + return df @enforce_types diff --git a/pdr_backend/analytics/test/test_get_predictions_info.py b/pdr_backend/analytics/test/test_get_predictions_info.py index 61c4663cc..06ae42aa9 100644 --- a/pdr_backend/analytics/test/test_get_predictions_info.py +++ b/pdr_backend/analytics/test/test_get_predictions_info.py @@ -1,67 +1,174 @@ -from unittest.mock import Mock, patch - +from unittest.mock import patch from enforce_typing import enforce_types - +import polars as pl +from pdr_backend.lake.table_pdr_predictions import ( + _object_list_to_df, + predictions_schema, +) +from pdr_backend.util.timeutil import timestr_to_ut from pdr_backend.analytics.get_predictions_info import get_predictions_info_main from pdr_backend.ppss.ppss import mock_ppss -from pdr_backend.subgraph.subgraph_predictions import FilterMode @enforce_types +@patch( + "pdr_backend.analytics.get_predictions_info.get_feed_summary_stats", + spec=pl.DataFrame, +) +@patch("pdr_backend.analytics.get_predictions_info.GQLDataFactory.get_gql_dfs") def test_get_predictions_info_main_mainnet( + mock_get_polars, + mock_get_stats, + _sample_first_predictions, + tmpdir, +): + ppss = mock_ppss(["binance BTC/USDT c 5m"], "sapphire-mainnet", str(tmpdir)) + predictions_df = _object_list_to_df(_sample_first_predictions, predictions_schema) + mock_get_polars.return_value = {"pdr_predictions": predictions_df} + + st_timestr = "2023-12-03" + fin_timestr = "2023-12-05" + feed_addr = "0x2d8e2267779d27c2b3ed5408408ff15d9f3a3152" + + get_predictions_info_main( + ppss, + st_timestr, + fin_timestr, + feed_addr, + ) + + # manualy filter predictions for latter check Predictions + predictions_df = predictions_df.filter( + predictions_df["ID"].map_elements(lambda x: x.split("-")[0]).is_in([feed_addr]) + ) + + preds_df = predictions_df.filter( + (predictions_df["timestamp"] >= timestr_to_ut(st_timestr) / 1000) + & (predictions_df["timestamp"] <= timestr_to_ut(fin_timestr) / 1000) + ) + + # number of rows from data frames are the same + assert mock_get_stats.call_args[0][0][0].shape[0] == preds_df.shape[0] + + # the data frame was filtered by feed address + assert mock_get_stats.call_args[0][0][0]["ID"][0].split("-")[0] == feed_addr + + # data frame after filtering is same as manual filtered dataframe + pl.DataFrame.equals(mock_get_stats.call_args, preds_df) + + assert mock_get_polars.call_count == 1 + assert mock_get_stats.call_count == 1 + + +@enforce_types +@patch( + "pdr_backend.analytics.get_predictions_info.get_feed_summary_stats", + spec=pl.DataFrame, +) +@patch("pdr_backend.analytics.get_predictions_info.GQLDataFactory.get_gql_dfs") +def test_empty_data_frame_timeframe_filter_mainnet( + mock_get_polars, + mock_get_stats, _sample_first_predictions, tmpdir, ): ppss = mock_ppss(["binance BTC/USDT c 5m"], "sapphire-mainnet", str(tmpdir)) - mock_getids = Mock(return_value=["0x123", "0x234"]) - mock_fetch = Mock(return_value=_sample_first_predictions) - mock_save = Mock() - mock_getstats = Mock() - - PATH = "pdr_backend.analytics.get_predictions_info" - with patch(f"{PATH}.get_all_contract_ids_by_owner", mock_getids), patch( - f"{PATH}.fetch_filtered_predictions", mock_fetch - ), patch(f"{PATH}.save_analysis_csv", mock_save), patch( - f"{PATH}.get_cli_statistics", mock_getstats - ): - st_timestr = "2023-11-02" - fin_timestr = "2023-11-05" - - get_predictions_info_main( - ppss, "0x123", st_timestr, fin_timestr, "parquet_data/" - ) - - mock_fetch.assert_called_with( - 1698883200, - 1699142400, - ["0x123"], - "mainnet", - FilterMode.CONTRACT, - payout_only=True, - trueval_only=True, - ) - mock_save.assert_called() - mock_getstats.assert_called_with(_sample_first_predictions) + predictions_df = _object_list_to_df(_sample_first_predictions, predictions_schema) + mock_get_polars.return_value = {"pdr_predictions": predictions_df} + + st_timestr = "2023-12-20" + fin_timestr = "2023-12-21" + feed_addr = "0x2d8e2267779d27c2b3ed5408408ff15d9f3a3152" + + get_predictions_info_main( + ppss, + st_timestr, + fin_timestr, + feed_addr, + ) + + # manualy filter predictions for latter check Predictions + predictions_df = predictions_df.filter( + predictions_df["ID"].map_elements(lambda x: x.split("-")[0]).is_in([feed_addr]) + ) + + preds_df = predictions_df.filter( + (predictions_df["timestamp"] >= timestr_to_ut(st_timestr) / 1000) + & (predictions_df["timestamp"] <= timestr_to_ut(fin_timestr) / 1000) + ) + + # number of rows from data frames are the same + assert mock_get_stats.call_args[0][0][0].shape[0] == preds_df.shape[0] + + # the data frame is empy + assert mock_get_stats.call_args[0][0][0].shape[0] == 0 + + assert mock_get_polars.call_count == 1 + assert mock_get_stats.call_count == 1 @enforce_types -def test_get_predictions_info_empty(tmpdir, capfd): +@patch( + "pdr_backend.analytics.get_predictions_info.get_feed_summary_stats", + spec=pl.DataFrame, +) +@patch("pdr_backend.analytics.get_predictions_info.GQLDataFactory.get_gql_dfs") +def test_empty_data_frame_feed_addr_filter_mainnet( + mock_get_polars, + mock_get_stats, + _sample_first_predictions, + tmpdir, +): ppss = mock_ppss(["binance BTC/USDT c 5m"], "sapphire-mainnet", str(tmpdir)) - mock_getids = Mock(return_value=[]) - mock_fetch = Mock(return_value={}) + predictions_df = _object_list_to_df(_sample_first_predictions, predictions_schema) + mock_get_polars.return_value = {"pdr_predictions": predictions_df} + + st_timestr = "2023-12-03" + fin_timestr = "2023-12-05" + feed_addr = "0x8e0we267779d27c2b3ed5408408ff15d9f3a3152" + + get_predictions_info_main( + ppss, + st_timestr, + fin_timestr, + feed_addr, + ) + + # manualy filter predictions for latter check Predictions + predictions_df = predictions_df.filter( + predictions_df["ID"].map_elements(lambda x: x.split("-")[0]).is_in([feed_addr]) + ) + + preds_df = predictions_df.filter( + (predictions_df["timestamp"] >= timestr_to_ut(st_timestr) / 1000) + & (predictions_df["timestamp"] <= timestr_to_ut(fin_timestr) / 1000) + ) + + # number of rows from data frames are the same + assert mock_get_stats.call_args[0][0][0].shape[0] == preds_df.shape[0] + + # the data frame is empy + assert mock_get_stats.call_args[0][0][0].shape[0] == 0 - PATH = "pdr_backend.analytics.get_predictions_info" - with patch(f"{PATH}.get_all_contract_ids_by_owner", mock_getids), patch( - f"{PATH}.fetch_filtered_predictions", mock_fetch - ): - st_timestr = "2023-11-02" - fin_timestr = "2023-11-05" + assert mock_get_polars.call_count == 1 + assert mock_get_stats.call_count == 1 - get_predictions_info_main( - ppss, "0x123", st_timestr, fin_timestr, "parquet_data/" - ) + +@enforce_types +@patch("pdr_backend.analytics.get_predictions_info.GQLDataFactory.get_gql_dfs") +def test_get_predictions_info_empty(mock_get_polars, tmpdir, capfd): + ppss = mock_ppss(["binance BTC/USDT c 5m"], "sapphire-mainnet", str(tmpdir)) + + mock_get_polars.return_value = {"pdr_predictions": pl.DataFrame()} + + get_predictions_info_main( + ppss, + "2023-11-03", + "2023-11-05", + "0x2d8e2267779d27c2b3ed5408408ff15d9f3a3152", + ) assert ( "No records found. Please adjust start and end times" in capfd.readouterr().out diff --git a/pdr_backend/analytics/test/test_get_predictoors_info.py b/pdr_backend/analytics/test/test_get_predictoors_info.py index 2b736f336..3662f6ddf 100644 --- a/pdr_backend/analytics/test/test_get_predictoors_info.py +++ b/pdr_backend/analytics/test/test_get_predictoors_info.py @@ -1,38 +1,144 @@ -from unittest.mock import Mock, patch +from unittest.mock import patch from enforce_typing import enforce_types +import polars as pl +from pdr_backend.lake.table_pdr_predictions import ( + _object_list_to_df, + predictions_schema, +) from pdr_backend.analytics.get_predictoors_info import get_predictoors_info_main from pdr_backend.ppss.ppss import mock_ppss -from pdr_backend.subgraph.subgraph_predictions import FilterMode +from pdr_backend.util.timeutil import timestr_to_ut @enforce_types -def test_get_predictoors_info_main_mainnet(tmpdir): +@patch( + "pdr_backend.analytics.get_predictoors_info.get_predictoor_summary_stats", + spec=pl.DataFrame, +) +@patch("pdr_backend.analytics.get_predictoors_info.GQLDataFactory.get_gql_dfs") +def test_get_predictoors_info_main_mainnet( + mock_get_polars, mock_get_stats, _sample_first_predictions, tmpdir +): ppss = mock_ppss(["binance BTC/USDT c 5m"], "sapphire-mainnet", str(tmpdir)) - mock_fetch = Mock(return_value=[]) - mock_save = Mock() - mock_getstats = Mock() - - PATH = "pdr_backend.analytics.get_predictoors_info" - with patch(f"{PATH}.fetch_filtered_predictions", mock_fetch), patch( - f"{PATH}.save_prediction_csv", mock_save - ), patch(f"{PATH}.get_cli_statistics", mock_getstats): - get_predictoors_info_main( - ppss, - "0x123", - "2023-01-01", - "2023-01-02", - "parquet_data/", - ) - - mock_fetch.assert_called_with( - 1672531200, - 1672617600, - ["0x123"], - "mainnet", - FilterMode.PREDICTOOR, - ) - mock_save.assert_called_with([], "parquet_data/") - mock_getstats.assert_called_with([]) + predictions_df = _object_list_to_df(_sample_first_predictions, predictions_schema) + mock_get_polars.return_value = {"pdr_predictions": predictions_df} + + st_timestr = "2023-12-03" + fin_timestr = "2023-12-05" + user_addr = "0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" + get_predictoors_info_main( + ppss, + st_timestr, + fin_timestr, + user_addr, + ) + + # manualy filter predictions for latter check Predictions + predictions_df = predictions_df.filter(predictions_df["user"].is_in([user_addr])) + preds_df = predictions_df.filter( + (predictions_df["timestamp"] >= timestr_to_ut(st_timestr) / 1000) + & (predictions_df["timestamp"] <= timestr_to_ut(fin_timestr) / 1000) + ) + + # data frame after filtering is same as manual filtered dataframe + pl.DataFrame.equals(mock_get_stats.call_args, preds_df) + + # number of rows from data frames are the same + assert mock_get_stats.call_args[0][0][0].shape[0] == preds_df.shape[0] + + # the data frame was filtered by user address + assert mock_get_stats.call_args[0][0][0]["user"][0] == user_addr + + assert mock_get_polars.call_count == 1 + assert mock_get_stats.call_count == 1 + + +@enforce_types +@patch( + "pdr_backend.analytics.get_predictoors_info.get_predictoor_summary_stats", + spec=pl.DataFrame, +) +@patch("pdr_backend.analytics.get_predictoors_info.GQLDataFactory.get_gql_dfs") +def test_empty_data_frame_timeframe_filter_mainnet( + mock_get_polars, mock_get_stats, _sample_first_predictions, tmpdir +): + ppss = mock_ppss(["binance BTC/USDT c 5m"], "sapphire-mainnet", str(tmpdir)) + + predictions_df = _object_list_to_df(_sample_first_predictions, predictions_schema) + mock_get_polars.return_value = {"pdr_predictions": predictions_df} + + st_timestr = "2023-12-20" + fin_timestr = "2023-12-30" + user_addr = "0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" + get_predictoors_info_main( + ppss, + st_timestr, + fin_timestr, + user_addr, + ) + + # manualy filter predictions for latter check Predictions + predictions_df = predictions_df.filter(predictions_df["user"].is_in([user_addr])) + preds_df = predictions_df.filter( + (predictions_df["timestamp"] >= timestr_to_ut(st_timestr) / 1000) + & (predictions_df["timestamp"] <= timestr_to_ut(fin_timestr) / 1000) + ) + + # data frame after filtering is same as manual filtered dataframe + pl.DataFrame.equals(mock_get_stats.call_args, preds_df) + + # number of rows from data frames are the same + assert mock_get_stats.call_args[0][0][0].shape[0] == preds_df.shape[0] + + # the data frame is empy + assert mock_get_stats.call_args[0][0][0].shape[0] == 0 + + assert mock_get_polars.call_count == 1 + assert mock_get_stats.call_count == 1 + + +@enforce_types +@patch( + "pdr_backend.analytics.get_predictoors_info.get_predictoor_summary_stats", + spec=pl.DataFrame, +) +@patch("pdr_backend.analytics.get_predictoors_info.GQLDataFactory.get_gql_dfs") +def test_empty_data_frame_user_address_filter_mainnet( + mock_get_polars, mock_get_stats, _sample_first_predictions, tmpdir +): + ppss = mock_ppss(["binance BTC/USDT c 5m"], "sapphire-mainnet", str(tmpdir)) + + predictions_df = _object_list_to_df(_sample_first_predictions, predictions_schema) + mock_get_polars.return_value = {"pdr_predictions": predictions_df} + + st_timestr = "2023-12-03" + fin_timestr = "2023-12-05" + user_addr = "0xbbbb4cb4ff2584bad80ff5f109034a891c3d223" + get_predictoors_info_main( + ppss, + st_timestr, + fin_timestr, + user_addr, + ) + + # manualy filter predictions for latter check Predictions + predictions_df = predictions_df.filter(predictions_df["user"].is_in([user_addr])) + preds_df = predictions_df.filter( + (predictions_df["timestamp"] >= timestr_to_ut(st_timestr) / 1000) + & (predictions_df["timestamp"] <= timestr_to_ut(fin_timestr) / 1000) + ) + + # data frame after filtering is same as manual filtered dataframe + pl.DataFrame.equals(mock_get_stats.call_args, preds_df) + + # number of rows from data frames are the same + assert mock_get_stats.call_args[0][0][0].shape[0] == preds_df.shape[0] + + # the data frame is empy + assert mock_get_stats.call_args[0][0][0].shape[0] == 0 + + assert mock_get_polars.call_count == 1 + assert mock_get_stats.call_count == 1 diff --git a/pdr_backend/analytics/test/test_get_traction_info.py b/pdr_backend/analytics/test/test_get_traction_info.py index d13ccb180..6e520bd94 100644 --- a/pdr_backend/analytics/test/test_get_traction_info.py +++ b/pdr_backend/analytics/test/test_get_traction_info.py @@ -1,100 +1,68 @@ -from unittest.mock import Mock, patch +from unittest.mock import patch import polars as pl -import pytest from enforce_typing import enforce_types from pdr_backend.analytics.get_traction_info import get_traction_info_main from pdr_backend.ppss.ppss import mock_ppss -from pdr_backend.subgraph.subgraph_predictions import FilterMode from pdr_backend.util.timeutil import timestr_to_ut +from pdr_backend.lake.table_pdr_predictions import ( + _object_list_to_df, + predictions_schema, +) @enforce_types +@patch("pdr_backend.analytics.get_traction_info.get_traction_statistics") +@patch("pdr_backend.analytics.get_traction_info.plot_traction_cum_sum_statistics") +@patch("pdr_backend.analytics.get_traction_info.plot_traction_daily_statistics") +@patch("pdr_backend.analytics.get_traction_info.GQLDataFactory.get_gql_dfs") def test_get_traction_info_main_mainnet( + mock_predictions_df, + mock_plot_daily, + mock_plot_cumsum, + mock_traction_stat, _sample_daily_predictions, tmpdir, ): ppss = mock_ppss(["binance BTC/USDT c 5m"], "sapphire-mainnet", str(tmpdir)) + predictions_df = _object_list_to_df(_sample_daily_predictions, predictions_schema) - mock_traction_stat = Mock() - mock_plot_cumsum = Mock() - mock_plot_daily = Mock() - mock_getids = Mock(return_value=["0x123"]) - mock_fetch = Mock(return_value=_sample_daily_predictions) + mock_predictions_df.return_value = {"pdr_predictions": predictions_df} - PATH = "pdr_backend.analytics.get_traction_info" - PATH2 = "pdr_backend.lake" - with patch(f"{PATH}.get_traction_statistics", mock_traction_stat), patch( - f"{PATH}.plot_traction_cum_sum_statistics", mock_plot_cumsum - ), patch(f"{PATH}.plot_traction_daily_statistics", mock_plot_daily), patch( - f"{PATH2}.gql_data_factory.get_all_contract_ids_by_owner", mock_getids - ), patch( - f"{PATH2}.table_pdr_predictions.fetch_filtered_predictions", mock_fetch - ): - st_timestr = "2023-11-02" - fin_timestr = "2023-11-05" + st_timestr = "2023-11-02" + fin_timestr = "2023-11-05" - get_traction_info_main(ppss, st_timestr, fin_timestr, "parquet_data/") + get_traction_info_main(ppss, st_timestr, fin_timestr, "parquet_data/") - mock_fetch.assert_called_with( - 1698883200, - 1699142400, - ["0x123"], - "mainnet", - FilterMode.CONTRACT_TS, - payout_only=False, - trueval_only=False, - ) - - # calculate ms locally so we can filter raw Predictions - st_ut = timestr_to_ut(st_timestr) - fin_ut = timestr_to_ut(fin_timestr) - st_ut_sec = st_ut // 1000 - fin_ut_sec = fin_ut // 1000 - - # Get all predictions into a dataframe - preds = [ - x - for x in _sample_daily_predictions - if st_ut_sec <= x.timestamp <= fin_ut_sec - ] - preds = [pred.__dict__ for pred in preds] - preds_df = pl.DataFrame(preds) - preds_df = preds_df.with_columns( - [ - pl.col("timestamp").mul(1000).alias("timestamp"), - ] - ) + # calculate ms locally so we can filter raw Predictions + preds_df = predictions_df.filter( + (predictions_df["timestamp"] >= timestr_to_ut(st_timestr) / 1000) + & (predictions_df["timestamp"] <= timestr_to_ut(fin_timestr) / 1000) + ) - # Assert calls and values - pl.DataFrame.equals(mock_traction_stat.call_args, preds_df) - mock_plot_cumsum.assert_called() - mock_plot_daily.assert_called() + # Assert calls and values + pl.DataFrame.equals(mock_traction_stat.call_args, preds_df) + mock_plot_cumsum.assert_called() + mock_plot_daily.assert_called() @enforce_types -def test_get_traction_info_empty(tmpdir, capfd): +@patch("pdr_backend.analytics.get_traction_info.GQLDataFactory.get_gql_dfs") +def test_get_traction_info_empty( + mock_predictions_df, + tmpdir, + capfd, +): ppss = mock_ppss(["binance BTC/USDT c 5m"], "sapphire-mainnet", str(tmpdir)) - mock_empty = Mock(return_value=[]) - - PATH = "pdr_backend.analytics.get_traction_info" - with patch(f"{PATH}.GQLDataFactory.get_gql_dfs", mock_empty): - st_timestr = "2023-11-02" - fin_timestr = "2023-11-05" + mock_predictions_df.return_value = {"pdr_predictions": pl.DataFrame()} + st_timestr = "2023-11-02" + fin_timestr = "2023-11-05" - get_traction_info_main(ppss, st_timestr, fin_timestr, "parquet_data/") + get_traction_info_main(ppss, st_timestr, fin_timestr, "parquet_data/") assert ( - "No records found. Please adjust start and end times." in capfd.readouterr().out + "No records found. Please adjust start and end times inside ppss.yaml." + in capfd.readouterr().out ) - - with patch("requests.post") as mock_post: - mock_post.return_value.status_code = 503 - # don't actually sleep in tests - with patch("time.sleep"): - with pytest.raises(Exception): - get_traction_info_main(ppss, st_timestr, fin_timestr, "parquet_data/") - - assert mock_post.call_count == 3 diff --git a/pdr_backend/analytics/test/test_predictoor_stats.py b/pdr_backend/analytics/test/test_predictoor_stats.py index c04370a8e..0f385e231 100644 --- a/pdr_backend/analytics/test/test_predictoor_stats.py +++ b/pdr_backend/analytics/test/test_predictoor_stats.py @@ -2,11 +2,17 @@ import polars as pl from enforce_typing import enforce_types +from pdr_backend.lake.table_pdr_predictions import ( + _object_list_to_df, + predictions_schema, + predictoor_summary_df_schema, + feed_summary_df_schema, +) from pdr_backend.analytics.predictoor_stats import ( - aggregate_prediction_statistics, calculate_slot_daily_statistics, - get_cli_statistics, + get_feed_summary_stats, + get_predictoor_summary_stats, get_slot_statistics, get_traction_statistics, plot_slot_daily_statistics, @@ -16,35 +22,21 @@ @enforce_types -def test_aggregate_prediction_statistics(_sample_first_predictions): - stats, correct_predictions = aggregate_prediction_statistics( - _sample_first_predictions - ) - assert isinstance(stats, dict) - assert "pair_timeframe" in stats - assert "predictor" in stats - assert correct_predictions == 1 # Adjust based on your sample data +def test_get_feed_statistics(_sample_first_predictions): + predictions_df = _object_list_to_df(_sample_first_predictions, predictions_schema) + feed_summary_df = get_feed_summary_stats(predictions_df) + + assert isinstance(feed_summary_df, pl.DataFrame) + assert len(feed_summary_df.schema) == len(feed_summary_df_schema) @enforce_types -def test_get_cli_statistics(capsys, _sample_first_predictions): - get_cli_statistics(_sample_first_predictions) - captured = capsys.readouterr() - output = captured.out - assert "Overall Accuracy" in output - assert "Accuracy for Pair" in output - assert "Accuracy for Predictoor Address" in output - - get_cli_statistics([]) - assert "No predictions found" in capsys.readouterr().out - - with patch( - "pdr_backend.analytics.predictoor_stats.aggregate_prediction_statistics" - ) as mock: - mock.return_value = ({}, 0) - get_cli_statistics(_sample_first_predictions) - - assert "No correct predictions found" in capsys.readouterr().out +def test_get_predictoor_statistics(_sample_first_predictions): + predictions_df = _object_list_to_df(_sample_first_predictions, predictions_schema) + predictoor_summary_df = get_predictoor_summary_stats(predictions_df) + + assert isinstance(predictoor_summary_df, pl.DataFrame) + assert len(predictoor_summary_df.schema) == len(predictoor_summary_df_schema) @enforce_types diff --git a/pdr_backend/cli/cli_module.py b/pdr_backend/cli/cli_module.py index 81ee76185..0d6dc404b 100644 --- a/pdr_backend/cli/cli_module.py +++ b/pdr_backend/cli/cli_module.py @@ -113,13 +113,13 @@ def do_claim_ROSE(args): @enforce_types def do_get_predictoors_info(args): ppss = PPSS(yaml_filename=args.PPSS_FILE, network=args.NETWORK) - get_predictoors_info_main(ppss, args.PDRS, args.ST, args.END, args.PQDIR) + get_predictoors_info_main(ppss, args.ST, args.END, args.PDRS) @enforce_types def do_get_predictions_info(args): ppss = PPSS(yaml_filename=args.PPSS_FILE, network=args.NETWORK) - get_predictions_info_main(ppss, args.FEEDS, args.ST, args.END, args.PQDIR) + get_predictions_info_main(ppss, args.ST, args.END, args.FEEDS) @enforce_types diff --git a/pdr_backend/lake/gql_data_factory.py b/pdr_backend/lake/gql_data_factory.py index d2348965b..856799106 100644 --- a/pdr_backend/lake/gql_data_factory.py +++ b/pdr_backend/lake/gql_data_factory.py @@ -13,8 +13,8 @@ get_pdr_subscriptions_df, subscriptions_schema, ) -from pdr_backend.ppss.ppss import PPSS from pdr_backend.subgraph.subgraph_predictions import get_all_contract_ids_by_owner +from pdr_backend.ppss.ppss import PPSS from pdr_backend.util.networkutil import get_sapphire_postfix from pdr_backend.util.timeutil import current_ut_ms, pretty_timestr diff --git a/pdr_backend/lake/table_pdr_predictions.py b/pdr_backend/lake/table_pdr_predictions.py index eac364e4b..b3767a48d 100644 --- a/pdr_backend/lake/table_pdr_predictions.py +++ b/pdr_backend/lake/table_pdr_predictions.py @@ -27,6 +27,29 @@ "user": Utf8, } +# PREDICTOOR_SUMMARY_SCHEMA +predictoor_summary_df_schema = { + "timeframe": Utf8, + "pair": Utf8, + "source": Utf8, + "accuracy": Float64, + "sum_stake": Float64, + "sum_payout": Float64, + "n_predictions": Int64, + "user": Utf8, +} + +# FEED_SUMMARY_SCHEMA +feed_summary_df_schema = { + "timeframe": Utf8, + "pair": Utf8, + "source": Utf8, + "accuracy": Float64, + "sum_stake": Float64, + "sum_payout": Float64, + "n_predictions": Int64, +} + def _transform_timestamp_to_ms(df: pl.DataFrame) -> pl.DataFrame: df = df.with_columns( diff --git a/pdr_backend/publisher/publish_assets.py b/pdr_backend/publisher/publish_assets.py index 20f07a0bd..f70cffb32 100644 --- a/pdr_backend/publisher/publish_assets.py +++ b/pdr_backend/publisher/publish_assets.py @@ -42,5 +42,4 @@ def publish_assets(web3_pp: Web3PP, publisher_ss: PublisherSS): cut=_CUT, web3_pp=web3_pp, ) - print("Done publishing.") diff --git a/pdr_backend/subgraph/core_subgraph.py b/pdr_backend/subgraph/core_subgraph.py index 1ead768c3..052bdecaf 100644 --- a/pdr_backend/subgraph/core_subgraph.py +++ b/pdr_backend/subgraph/core_subgraph.py @@ -31,5 +31,4 @@ def query_subgraph( ) result = response.json() - return result diff --git a/pdr_backend/subgraph/prediction.py b/pdr_backend/subgraph/prediction.py index ed1cccf24..904be4b27 100644 --- a/pdr_backend/subgraph/prediction.py +++ b/pdr_backend/subgraph/prediction.py @@ -18,6 +18,7 @@ def __init__( source: str, payout: Union[float, None], slot: int, # slot/epoch timestamp + address: str, user: str, ) -> None: self.ID = ID @@ -30,6 +31,7 @@ def __init__( self.source = source self.payout = payout self.slot = slot + self.address = (address,) self.user = user @@ -49,10 +51,11 @@ def mock_prediction(prediction_tuple: tuple) -> Prediction: source, payout, slot, + address, user, ) = prediction_tuple - ID = f"{pair_str}-{timeframe_str}-{slot}-{user}" + ID = f"{address}-{slot}-{user}" return Prediction( ID=ID, pair=pair_str, @@ -63,6 +66,7 @@ def mock_prediction(prediction_tuple: tuple) -> Prediction: timestamp=timestamp, source=source, payout=payout, + address=address, slot=slot, user=user, ) @@ -101,6 +105,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0, 1701503100, + "0x18f54cc21b7a2fdd011bea06bba7801b280e3151", "0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -113,6 +118,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0, 1701589500, + "0x2d8e2267779d27c2b3ed5408408ff15d9f3a3152", "0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd", ), ] @@ -128,6 +134,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0500, 1701675900, + "0x30f1c55e72fe105e4a1fbecdff3145fc14177695", "0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -140,6 +147,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0, 1701503000, + "0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd", "0xbbbb4cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -152,6 +160,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0500, 1701589500, + "0x18f54cc21b7a2fdd011bea06bba7801b280e3151", "0xbbbb4cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -164,6 +173,7 @@ def mock_daily_predictions() -> List[Prediction]: "kraken", 0.0500, 1701675900, + "0x31fabe1fc9887af45b77c7d1e13c5133444ebfbd", "0xbbbb4cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -176,6 +186,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0, 1701589500, + "0x30f1c55e72fe105e4a1fbecdff3145fc14177695", "0xcccc4cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -188,6 +199,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0500, 1701675900, + "0x30f1c55e72fe105e4a1fbecdff3145fc14177695", "0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd", ), ] @@ -203,6 +215,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0500, 1698865200, + "0x30f1c55e72fe105e4a1fbecdff3145fc14177695", "0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -215,6 +228,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0, 1698951600, + "0xe66421fd29fc2d27d0724f161f01b8cbdcd69690", "0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -227,6 +241,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0500, 1699038000, + "0x18f54cc21b7a2fdd011bea06bba7801b280e3151", "0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -239,6 +254,7 @@ def mock_daily_predictions() -> List[Prediction]: "kraken", 0.0500, 1699124400, + "0x31fabe1fc9887af45b77c7d1e13c5133444ebfbd", "0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -251,6 +267,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0, 1701589500, + "0xaa6515c138183303b89b98aea756b54f711710c5", "0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd", ), ( @@ -263,6 +280,7 @@ def mock_daily_predictions() -> List[Prediction]: "binance", 0.0500, 1699300800, + "0x30f1c55e72fe105e4a1fbecdff3145fc14177695", "0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd", ), ] diff --git a/pdr_backend/subgraph/subgraph_predictions.py b/pdr_backend/subgraph/subgraph_predictions.py index 9ddef8474..491bacb54 100644 --- a/pdr_backend/subgraph/subgraph_predictions.py +++ b/pdr_backend/subgraph/subgraph_predictions.py @@ -139,7 +139,7 @@ def fetch_filtered_predictions( timestamp = prediction_sg_dict["timestamp"] slot = prediction_sg_dict["slot"]["slot"] user = prediction_sg_dict["user"]["id"] - + address = prediction_sg_dict["id"].split("-")[0] trueval = None payout = None predicted_value = None @@ -167,6 +167,7 @@ def fetch_filtered_predictions( timestamp=timestamp, source=source, payout=payout, + address=address, slot=slot, user=user, ) diff --git a/pdr_backend/subgraph/test/test_prediction.py b/pdr_backend/subgraph/test/test_prediction.py index a73e584aa..0946fbfa7 100644 --- a/pdr_backend/subgraph/test/test_prediction.py +++ b/pdr_backend/subgraph/test/test_prediction.py @@ -6,15 +6,17 @@ @enforce_types def test_predictions(): predictions = mock_first_predictions() + contract_address_1 = "0x18f54cc21b7a2fdd011bea06bba7801b280e3151" + contract_address_2 = "0x2d8e2267779d27c2b3ed5408408ff15d9f3a3152" assert len(predictions) == 2 assert isinstance(predictions[0], Prediction) assert isinstance(predictions[1], Prediction) assert ( predictions[0].ID - == "ADA/USDT-5m-1701503100-0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" + == contract_address_1 + "-1701503100-0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" ) assert ( predictions[1].ID - == "BTC/USDT-5m-1701589500-0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" + == contract_address_2 + "-1701589500-0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" ) diff --git a/pdr_backend/subgraph/test/test_subgraph_predictions.py b/pdr_backend/subgraph/test/test_subgraph_predictions.py index d42053400..50d96782e 100644 --- a/pdr_backend/subgraph/test/test_subgraph_predictions.py +++ b/pdr_backend/subgraph/test/test_subgraph_predictions.py @@ -12,6 +12,8 @@ get_all_contract_ids_by_owner, ) +ADA_CONTRACT_ADDRESS = "0x18f54cc21b7a2fdd011bea06bba7801b280e3151" + SAMPLE_PREDICTION = Prediction( # pylint: disable=line-too-long ID="0x18f54cc21b7a2fdd011bea06bba7801b280e3151-1698527100-0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd", @@ -24,6 +26,7 @@ source="binance", payout=0.0, slot=1698527100, + address="0x18f54cc21b7a2fdd011bea06bba7801b280e3151", user="0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd", ) @@ -116,6 +119,7 @@ def test_fetch_filtered_predictions(mock_query_subgraph): assert isinstance(predictions[0], Prediction) assert predictions[0].user == "0xd2a24cb4ff2584bad80ff5f109034a891c3d88dd" assert predictions[0].pair == "ADA/USDT" + assert predictions[0].address[0] == "0x18f54cc21b7a2fdd011bea06bba7801b280e3151" assert predictions[0].trueval is False assert predictions[0].prediction is True assert mock_query_subgraph.call_count == 1 diff --git a/system_tests/test_get_predictions_info_system.py b/system_tests/test_get_predictions_info_system.py index e3ca742e4..0bc507a49 100644 --- a/system_tests/test_get_predictions_info_system.py +++ b/system_tests/test_get_predictions_info_system.py @@ -1,41 +1,41 @@ import sys from unittest.mock import Mock, patch, MagicMock - +from pdr_backend.lake.table_pdr_predictions import ( + _object_list_to_df, + predictions_schema, +) +from pdr_backend.subgraph.prediction import Prediction from pdr_backend.cli import cli_module from pdr_backend.ppss.web3_pp import Web3PP -from pdr_backend.subgraph.prediction import Prediction -from pdr_backend.subgraph.subgraph_predictions import FilterMode from pdr_backend.util.web3_config import Web3Config -@patch("pdr_backend.analytics.get_predictions_info.get_cli_statistics") -@patch("pdr_backend.analytics.get_predictions_info.fetch_filtered_predictions") -@patch("pdr_backend.analytics.get_predictions_info.save_analysis_csv") -@patch("pdr_backend.analytics.get_predictions_info.get_all_contract_ids_by_owner") -def test_topup( - mock_get_all_contract_ids_by_owner, - mock_save_analysis_csv, - mock_fetch_filtered_predictions, - mock_get_cli_statistics, -): - mock_get_all_contract_ids_by_owner.return_value = ["0xfeed"] +@patch("pdr_backend.analytics.get_predictions_info.get_feed_summary_stats") +@patch("pdr_backend.analytics.get_predictions_info.GQLDataFactory.get_gql_dfs") +def test_topup(mock_get_polars, mock_get_stats): + feed_addr = "0x2d8e2267779d27c2b3ed5408408ff15d9f3a3152" + user_addr = "0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" mock_predictions = [ Prediction( - "0xfeed", + "{feed_addr}-31232-{user_addr}", "BTC", "5m", True, 100.0, False, - 100, + 1701532572, "binance", 10.0, 10, - "0xuser", + feed_addr, + user_addr, ) ] - mock_fetch_filtered_predictions.return_value = mock_predictions + predictions_df = _object_list_to_df(mock_predictions, predictions_schema) + + mock_get_stats.return_value = predictions_df + mock_get_polars.return_value = {"pdr_predictions": predictions_df} mock_web3_pp = MagicMock(spec=Web3PP) mock_web3_pp.network = "sapphire-mainnet" @@ -46,6 +46,7 @@ def test_topup( mock_web3_config = Mock(spec=Web3Config) mock_web3_config.w3 = Mock() mock_web3_pp.web3_config = mock_web3_config + mock_web3_pp.owner_addrs = user_addr with patch("pdr_backend.ppss.ppss.Web3PP", return_value=mock_web3_pp): # Mock sys.argv @@ -58,7 +59,7 @@ def test_topup( "ppss.yaml", "development", "--FEEDS", - "0xfeed", + "{feed_addr}", ] with patch("builtins.print") as mock_print: @@ -69,17 +70,8 @@ def test_topup( mock_print.assert_any_call("Arguments:") mock_print.assert_any_call("PPSS_FILE=ppss.yaml") mock_print.assert_any_call("NETWORK=development") - mock_print.assert_any_call("FEEDS=0xfeed") + mock_print.assert_any_call("FEEDS={feed_addr}") # Additional assertions - mock_save_analysis_csv.assert_called_with(mock_predictions, "./dir") - mock_get_cli_statistics.assert_called_with(mock_predictions) - mock_fetch_filtered_predictions.assert_called_with( - 1701388800, - 1703980800, - ["0xfeed"], - "mainnet", - FilterMode.CONTRACT, - payout_only=True, - trueval_only=True, - ) + mock_get_polars.assert_called_with() + mock_get_stats.call_args[0][0].equals(predictions_df) diff --git a/system_tests/test_get_predictoors_info_system.py b/system_tests/test_get_predictoors_info_system.py index 83408a40d..070c20802 100644 --- a/system_tests/test_get_predictoors_info_system.py +++ b/system_tests/test_get_predictoors_info_system.py @@ -1,18 +1,19 @@ import sys from unittest.mock import Mock, patch, MagicMock - +from pdr_backend.lake.table_pdr_predictions import ( + _object_list_to_df, + predictions_schema, +) +from pdr_backend.subgraph.prediction import Prediction from pdr_backend.cli import cli_module from pdr_backend.ppss.web3_pp import Web3PP from pdr_backend.util.web3_config import Web3Config -@patch("pdr_backend.analytics.get_predictoors_info.fetch_filtered_predictions") -@patch("pdr_backend.analytics.get_predictoors_info.get_cli_statistics") -@patch("pdr_backend.analytics.get_predictoors_info.save_prediction_csv") -def test_topup( - mock_fetch_filtered_predictions, mock_get_cli_statistics, mock_save_prediction_csv -): +@patch("pdr_backend.analytics.get_predictoors_info.get_predictoor_summary_stats") +@patch("pdr_backend.analytics.get_predictoors_info.GQLDataFactory.get_gql_dfs") +def test_topup(mock_get_polars, mock_get_stats): mock_web3_pp = MagicMock(spec=Web3PP) mock_web3_pp.network = "sapphire-mainnet" mock_web3_pp.subgraph_url = ( @@ -23,22 +24,37 @@ def test_topup( mock_web3_config.w3 = Mock() mock_web3_config.w3.eth.get_balance.return_value = 100 mock_web3_pp.web3_config = mock_web3_config - mock_web3_pp.web3_config.owner = "0xowner" + mock_web3_pp.web3_config.owner = "0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" + mock_web3_pp.owner_addrs = "0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" mock_token = MagicMock() mock_token.balanceOf.return_value = int(5e18) mock_token.transfer.return_value = True + feed_addr = "0x2d8e2267779d27c2b3ed5408408ff15d9f3a3152" + user_addr = "0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" + + mock_predictions = [ + Prediction( + "{feed_addr}-31232-{user_addr}", + "BTC", + "5m", + True, + 100.0, + False, + 1701532572, + "binance", + 10.0, + 10, + feed_addr, + user_addr, + ) + ] + + predictions_df = _object_list_to_df(mock_predictions, predictions_schema) + + mock_get_stats.return_value = predictions_df + mock_get_polars.return_value = {"pdr_predictions": predictions_df} - mock_query_subgraph = Mock() - mock_query_subgraph.return_value = { - "data": { - "predictContracts": [ - {}, - {}, - {}, - ] - } - } with patch("pdr_backend.contract.token.Token", return_value=mock_token), patch( "pdr_backend.ppss.ppss.Web3PP", return_value=mock_web3_pp ): @@ -52,7 +68,7 @@ def test_topup( "ppss.yaml", "development", "--PDRS", - "0xpredictoor", + "{user_addr}", ] with patch("builtins.print") as mock_print: @@ -63,9 +79,8 @@ def test_topup( mock_print.assert_any_call("Arguments:") mock_print.assert_any_call("PPSS_FILE=ppss.yaml") mock_print.assert_any_call("NETWORK=development") - mock_print.assert_any_call("PDRS=0xpredictoor") + mock_print.assert_any_call("PDRS={user_addr}") # Additional assertions - mock_fetch_filtered_predictions.assert_called() - mock_get_cli_statistics.assert_called() - mock_save_prediction_csv.assert_called() + mock_get_polars.assert_called() + mock_get_stats.call_args[0][0].equals(predictions_df) diff --git a/system_tests/test_get_traction_info_system.py b/system_tests/test_get_traction_info_system.py index 6a0d360e3..c59b9ea84 100644 --- a/system_tests/test_get_traction_info_system.py +++ b/system_tests/test_get_traction_info_system.py @@ -1,21 +1,41 @@ import sys - from unittest.mock import Mock, patch, MagicMock +from pdr_backend.lake.table_pdr_predictions import ( + _object_list_to_df, + predictions_schema, +) +from pdr_backend.subgraph.prediction import Prediction from pdr_backend.cli import cli_module from pdr_backend.ppss.web3_pp import Web3PP from pdr_backend.util.web3_config import Web3Config @patch("pdr_backend.analytics.get_traction_info.plot_slot_daily_statistics") -@patch("pdr_backend.lake.gql_data_factory.get_all_contract_ids_by_owner") -@patch("pdr_backend.analytics.predictoor_stats.plt.savefig") -def test_topup( - mock_savefig, - mock_get_all_contract_ids_by_owner, - mock_plot_slot_daily_statistics, -): - mock_get_all_contract_ids_by_owner.return_value = ["0xfeed"] +@patch("pdr_backend.analytics.get_traction_info.GQLDataFactory.get_gql_dfs") +def test_topup(mock_get_polars, mock_plot_stats): + feed_addr = "0x2d8e2267779d27c2b3ed5408408ff15d9f3a3152" + user_addr = "0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd" + mock_predictions = [ + Prediction( + "{feed_addr}-31232-{0xaaaa4cb4ff2584bad80ff5f109034a891c3d88dd}", + "BTC", + "5m", + True, + 100.0, + False, + 1701532572, + "binance", + 10.0, + 10, + feed_addr, + user_addr, + ) + ] + + predictions_df = _object_list_to_df(mock_predictions, predictions_schema) + + mock_get_polars.return_value = {"pdr_predictions": predictions_df} mock_web3_pp = MagicMock(spec=Web3PP) mock_web3_pp.network = "sapphire-mainnet" @@ -49,20 +69,9 @@ def test_topup( mock_print.assert_any_call("Arguments:") mock_print.assert_any_call("PPSS_FILE=ppss.yaml") mock_print.assert_any_call("NETWORK=development") - mock_print.assert_any_call( - "Get predictions data across many feeds and timeframes." - ) - mock_print.assert_any_call( - " Data start: timestamp=1701388800000, dt=2023-12-01_00:00:00.000" - ) - mock_print.assert_any_call( - " Data fin: timestamp=1703980800000, dt=2023-12-31_00:00:00.000" - ) mock_print.assert_any_call( "Chart created:", "./dir/plots/daily_unique_predictoors.png" ) # Additional assertions - mock_get_all_contract_ids_by_owner.assert_called() - mock_plot_slot_daily_statistics.assert_called() - mock_savefig.assert_called_with("./dir/plots/daily_unique_predictoors.png") + mock_plot_stats.assert_called()