diff --git a/README.md b/README.md index 7f4413c..16c802e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # pycot -[![PyPI Version](https://badge.fury.io/py/pycot-reports.svg)](https://badge.fury.io/py/pycot-reports) +[![PyPI version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=py&r=r&ts=1683906897&type=6e&v=0.0.7&x2=0)](https://badge.fury.io/py/pycot-reports) [![License: MIT](https://img.shields.io/badge/License-MIT-red.svg)](https://github.com/philsv/pycot/blob/main/LICENSE) [![Weekly Downloads](https://static.pepy.tech/personalized-badge/pycot-reports?period=week&units=international_system&left_color=grey&right_color=blue&left_text=downloads/week)](https://pepy.tech/project/pycot-reports) [![Monthly Downloads](https://static.pepy.tech/personalized-badge/pycot-reports?period=month&units=international_system&left_color=grey&right_color=blue&left_text=downloads/month)](https://pepy.tech/project/pycot-reports) @@ -22,10 +22,28 @@ pip install pycot-reports ## How to use ```python -from pycot.reports import legacy_report, disaggregated_report, financial_report +from pycot import reports + +df = reports.legacy_report(report_type="legacy_fut", contract_name=("FED FUNDS - CHICAGO BOARD OF TRADE", "30-DAY FEDERAL FUNDS - CHICAGO BOARD OF TRADE")) +``` + +## How do I get cached results? + +If you want to retrieve data from the same report multiple times, you can use the `cot_report` function. This will cache the results of the previous function call. + +Lets have a look at an example: + +```python +from pycot.reports import cot_report, legacy_report + +fed_funds_contract = ("FED FUNDS - CHICAGO BOARD OF TRADE", "30-DAY FEDERAL FUNDS - CHICAGO BOARD OF TRADE") +fed_funds_df = cot_report(legacy_report(), fed_funds_contract) # will load the full report (~ 10-15 seconds) + +bbg_contract = ("BBG COMMODITY - CHICAGO BOARD OF TRADE", "BLOOMBERG COMMODITY INDEX - CHICAGO BOARD OF TRADE") +bbg_df = cot_report(legacy_report(), bbg_contract) # cached, will not load the full report again ``` -Lets have a look at some examples. +## Report Types ### Legacy Report (All Contracts) diff --git a/pycot/reports.py b/pycot/reports.py index 6e313df..2a0de9a 100644 --- a/pycot/reports.py +++ b/pycot/reports.py @@ -9,14 +9,15 @@ @lru_cache def legacy_report( - report_type: str, + report_type: str | None = None, contract_name: str | tuple | None = None, ) -> pd.DataFrame: """ " Retrieves the legacy Commitment of Traders Reports data. Args: - report_type (str): The type of cot report to extract. + report_type (str): The type of cot report to extract. + Defaults to "legacy_futopt". Returns: A pandas DataFrame with the legacy futures Commitment of Traders data. @@ -25,6 +26,8 @@ def legacy_report( >>> from pycot.reports import legacy_report >>> legacy_report("legacy_fut", ("FED FUNDS - CHICAGO BOARD OF TRADE", "30-DAY FEDERAL FUNDS - CHICAGO BOARD OF TRADE")) """ + report_type = "legacy_futopt" if report_type is None else report_type + if report_type not in ["legacy_fut", "legacy_futopt"]: raise InvalidReportType("Please use one of the following report types: ['legacy_fut', 'legacy_futopt']") @@ -42,11 +45,15 @@ def legacy_report( @lru_cache def disaggregated_report( - report_type: str, + report_type: str | None = None, contract_name: str | tuple | None = None, ) -> pd.DataFrame: """ Adjusted disaggregated futures and options report data. + + Args: + report_type (str): The type of cot report to extract. + Defaults to "disaggregated_futopt". Returns: A pandas DataFrame with the disaggregated futures and options report. @@ -55,6 +62,8 @@ def disaggregated_report( >>> from pycot.reports import disaggregated_report >>> disaggregated_report("disaggregated_fut", ("BRENT LAST DAY - NEW YORK MERCANTILE EXCHANGE", "BRENT CRUDE OIL LAST DAY - NEW YORK MERCANTILE EXCHANGE")) """ + report_type = "disaggregated_futopt" if report_type is None else report_type + if report_type not in ["disaggregated_fut", "disaggregated_futopt"]: raise InvalidReportType("Please use one of the following report types: ['disaggregated_fut', 'disaggregated_futopt']") @@ -79,11 +88,15 @@ def disaggregated_report( @lru_cache def financial_report( - report_type: str, + report_type: str | None = None, contract_name: str | tuple | None = None, ) -> pd.DataFrame: """ Adjusted financial futures and options report data. + + Args: + report_type (str): The type of cot report to extract. + Defaults to "traders_in_financial_futures_futopt". Returns: A pandas DataFrame with the financial futures and options report. @@ -92,6 +105,8 @@ def financial_report( >>> from pycot.reports import financial_report >>> financial_report("financial_fut", ("UST 10Y NOTE - CHICAGO BOARD OF TRADE", "10-YEAR U.S. TREASURY NOTES - CHICAGO BOARD OF TRADE", "10 YEAR U.S. TREASURY NOTES - CHICAGO BOARD OF TRADE")) """ + report_type = "traders_in_financial_futures_futopt" if report_type is None else report_type + if report_type not in ["traders_in_financial_futures_fut", "traders_in_financial_futures_futopt"]: raise InvalidReportType("Please use one of the following report types: ['traders_in_financial_futures_fut', 'traders_in_financial_futures_futopt']") @@ -113,8 +128,37 @@ def financial_report( return get_contract(df, contract_name) if contract_name else df -if __name__ == "__main__": - contract_name = ("FED FUNDS - CHICAGO BOARD OF TRADE", "30-DAY FEDERAL FUNDS - CHICAGO BOARD OF TRADE") - df = legacy_report("legacy_fut", contract_name) - print(df) - print(df.info()) +def cot_report( + report: pd.DataFrame, + contract_name: str | tuple, +) -> pd.DataFrame: + """ + Retrieves data from a selected Commitment of Traders report. + Results will be cached after the first call. + + Args: + report (pd.DataFrame): The report to extract data from. Must be one of the following: + + - `legacy_report()` + - `disaggregated_report()` + - `financial_report()` + + contract_name (str | tuple): The name of the contract to extract data from. + + Returns: + A pandas DataFrame with the Commitment of Traders data. + + Example: + >>> from pycot.reports import legacy_report, cot_report + >>> report = legacy_report("legacy_fut") + >>> df_1 = cot_report(report, ("FED FUNDS - CHICAGO BOARD OF TRADE", "30-DAY FEDERAL FUNDS - CHICAGO BOARD OF TRADE")) + >>> df_2 = cot_report(report, ("BBG COMMODITY - CHICAGO BOARD OF TRADE", "BLOOMBERG COMMODITY INDEX - CHICAGO BOARD OF TRADE")) + >>> ... + """ + if not isinstance(report, pd.DataFrame): + raise TypeError("report must be a pandas DataFrame") + + if not isinstance(contract_name, (str, tuple)): + raise TypeError("contract_name must be a string or a tuple") + + return get_contract(report, contract_name) diff --git a/pycot/version.py b/pycot/version.py index 034f46c..6526deb 100644 --- a/pycot/version.py +++ b/pycot/version.py @@ -1 +1 @@ -__version__ = "0.0.6" +__version__ = "0.0.7" diff --git a/tests/test_reports.py b/tests/test_reports.py index d2c531e..11f184a 100644 --- a/tests/test_reports.py +++ b/tests/test_reports.py @@ -1,3 +1,5 @@ +import time + import pandas as pd import pytest @@ -54,3 +56,26 @@ def test_disaggregated_report(report_type, contract_name): def test_financial_report(report_type, contract_name): df = reports.financial_report(report_type, contract_name) assert isinstance(df, pd.DataFrame) + + +@pytest.mark.parametrize( + "contract_1,contract_2", + [ + ( + ("FED FUNDS - CHICAGO BOARD OF TRADE", "30-DAY FEDERAL FUNDS - CHICAGO BOARD OF TRADE"), + ("BBG COMMODITY - CHICAGO BOARD OF TRADE", "BLOOMBERG COMMODITY INDEX - CHICAGO BOARD OF TRADE"), + ), + ], +) +def test_cot_report(contract_1, contract_2): + start_time_1 = time.perf_counter() + df_1 = reports.cot_report(reports.legacy_report(), contract_1) + end_time_1 = time.perf_counter() - start_time_1 + + start_time_2 = time.perf_counter() + df_2 = reports.cot_report(reports.legacy_report(), contract_2) + end_time_2 = time.perf_counter() - start_time_2 + + assert end_time_1 > end_time_2 + assert isinstance(df_1, pd.DataFrame) + assert isinstance(df_2, pd.DataFrame)