From 5a18b941455d1710b409946f7085b2094e614022 Mon Sep 17 00:00:00 2001 From: Thomas o Date: Fri, 19 Nov 2021 17:35:00 +0000 Subject: [PATCH 1/4] Add limits by default to the API position calculations. --- infertrade/api.py | 11 ++++++----- tests/test_allocations.py | 8 ++++++++ tests/test_api.py | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/infertrade/api.py b/infertrade/api.py index 679a2217..6e8d7b4e 100644 --- a/infertrade/api.py +++ b/infertrade/api.py @@ -25,7 +25,7 @@ # InferTrade packages from infertrade.algos import algorithm_functions, ta_adaptor -from infertrade.utilities.operations import ReturnsFromPositions +from infertrade.utilities.operations import ReturnsFromPositions, restrict_allocation, limit_allocation from infertrade.PandasEnum import PandasEnum @@ -159,13 +159,14 @@ def _get_raw_callable(name_of_strategy_or_signal: str) -> callable: @staticmethod def calculate_allocations( - df: pd.DataFrame, name_of_strategy: str, name_of_price_series: str = PandasEnum.MID.value + df: pd.DataFrame, name_of_strategy: str, name_of_price_series: str = PandasEnum.MID.value, + allocation_lower_limit=-1.0, allocation_upper_limit=1.0 ) -> pd.DataFrame: """Calculates the allocations using the supplied strategy.""" if name_of_price_series is not PandasEnum.MID.value: df[PandasEnum.MID.value] = df[name_of_price_series] rule_function = Api._get_raw_callable(name_of_strategy) - df_with_positions = rule_function(df) + df_with_positions = limit_allocation(rule_function(df), allocation_lower_limit, allocation_upper_limit) return df_with_positions @staticmethod @@ -176,10 +177,10 @@ def calculate_returns(df: pd.DataFrame) -> pd.DataFrame: @staticmethod def calculate_allocations_and_returns( - df: pd.DataFrame, name_of_strategy: str, name_of_price_series: str = PandasEnum.MID.value + df: pd.DataFrame, name_of_strategy: str, name_of_price_series: str = PandasEnum.MID.value, *args, **kwargs ) -> pd.DataFrame: """Calculates the returns using the supplied strategy.""" - df_with_positions = Api.calculate_allocations(df, name_of_strategy, name_of_price_series) + df_with_positions = Api.calculate_allocations(df, name_of_strategy, name_of_price_series, *args, **kwargs) df_with_returns = ReturnsFromPositions().transform(df_with_positions) return df_with_returns diff --git a/tests/test_allocations.py b/tests/test_allocations.py index 22a81ed4..e922f565 100644 --- a/tests/test_allocations.py +++ b/tests/test_allocations.py @@ -37,6 +37,7 @@ from infertrade.algos import algorithm_functions from infertrade.algos.community import allocations from infertrade.algos.community.allocations import create_infertrade_export_allocations +from infertrade.api import Api def test_under_minimum_length_to_calculate(): @@ -115,3 +116,10 @@ def test_create_infertrade_export_allocations(): """Checks that a valid dictionary can be created.""" dictionary_algorithms = create_infertrade_export_allocations() assert isinstance(dictionary_algorithms, dict) # could add checks for contents too + + +def test_all_allocations_list_required_series(): + """Checks that all allocation rules list required series.""" + for ii_rule in Api.available_algorithms(filter_by_category="allocation"): + assert isinstance(Api.required_inputs_for_algorithm(ii_rule), list) + diff --git a/tests/test_api.py b/tests/test_api.py index c4421951..2d15f289 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -54,7 +54,7 @@ def test_get_available_algorithms(algorithm): assert Api.determine_package_of_algorithm(algorithm) in Api.available_packages() try: Api.determine_package_of_algorithm("not_available_algo") - except (NameError): + except NameError: pass inputs = Api.required_inputs_for_algorithm(algorithm) From 2fcbceef6e4cf505edff64b1d8fd1ee2519ea807 Mon Sep 17 00:00:00 2001 From: NikolaR01 Date: Sun, 21 Nov 2021 00:43:35 +0100 Subject: [PATCH 2/4] Test for allocation limits Added test --- infertrade/api.py | 2 +- tests/test_api.py | 34 ++++++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/infertrade/api.py b/infertrade/api.py index 6e8d7b4e..539904ac 100644 --- a/infertrade/api.py +++ b/infertrade/api.py @@ -160,7 +160,7 @@ def _get_raw_callable(name_of_strategy_or_signal: str) -> callable: @staticmethod def calculate_allocations( df: pd.DataFrame, name_of_strategy: str, name_of_price_series: str = PandasEnum.MID.value, - allocation_lower_limit=-1.0, allocation_upper_limit=1.0 + allocation_lower_limit: float = -1.0, allocation_upper_limit: float = 1.0 ) -> pd.DataFrame: """Calculates the allocations using the supplied strategy.""" if name_of_price_series is not PandasEnum.MID.value: diff --git a/tests/test_api.py b/tests/test_api.py index 2d15f289..f4df00e6 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -19,6 +19,8 @@ """ # External imports +import copy + import pandas as pd import pytest @@ -172,8 +174,8 @@ def test_return_representations(algorithm): ) for representation in dict_of_properties[algorithm]["available_representation_types"]: assert ( - returned_representations[representation] - == dict_of_properties[algorithm]["available_representation_types"][representation] + returned_representations[representation] + == dict_of_properties[algorithm]["available_representation_types"][representation] ) # Check if the if the function returns the correct representation when given a string @@ -185,8 +187,8 @@ def test_return_representations(algorithm): type(returned_representations), ) assert ( - returned_representations[representation] - == dict_of_properties[algorithm]["available_representation_types"][representation] + returned_representations[representation] + == dict_of_properties[algorithm]["available_representation_types"][representation] ) # Check if the function returns the correct representations when given a list @@ -198,8 +200,8 @@ def test_return_representations(algorithm): ) for representation in algorithm_representations: assert ( - returned_representations[representation] - == dict_of_properties[algorithm]["available_representation_types"][representation] + returned_representations[representation] + == dict_of_properties[algorithm]["available_representation_types"][representation] ) @@ -357,3 +359,23 @@ def test_export_cross_prediction(): ) assert isinstance(sorted_dict, dict) + + +@pytest.mark.parametrize("test_df", test_dfs) +def test_allocation_limit(test_df): + """Test used to see if calculated allocation values are inside of specified limit""" + + test_df_copy = copy.deepcopy(test_df) + df_with_allocations = Api.calculate_allocations( + df=test_df_copy, name_of_strategy=available_allocation_algorithms[0], name_of_price_series="close", + allocation_lower_limit=0, allocation_upper_limit=0 + ) + if all(df_with_allocations["allocation"] != 0): + raise ValueError("Allocation limits breached") + + df_with_allocations = Api.calculate_allocations( + df=test_df_copy, name_of_strategy=available_allocation_algorithms[0], name_of_price_series="close", + allocation_lower_limit=-0.1, allocation_upper_limit=0.1 + ) + if all(-0.1 > df_with_allocations["allocation"]) or all(df_with_allocations["allocation"] > 0.1): + raise ValueError("Allocation limits breached") From a668ac6aa7cba6975f0c3e8fc5fff4010db15b59 Mon Sep 17 00:00:00 2001 From: NikolaR01 <74156271+NikolaR01@users.noreply.github.com> Date: Thu, 25 Nov 2021 15:48:10 +0100 Subject: [PATCH 3/4] Update tests/test_api.py Co-authored-by: Thomas Oliver <29981664+ta-oliver@users.noreply.github.com> --- tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_api.py b/tests/test_api.py index f4df00e6..030e6bdb 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -377,5 +377,5 @@ def test_allocation_limit(test_df): df=test_df_copy, name_of_strategy=available_allocation_algorithms[0], name_of_price_series="close", allocation_lower_limit=-0.1, allocation_upper_limit=0.1 ) - if all(-0.1 > df_with_allocations["allocation"]) or all(df_with_allocations["allocation"] > 0.1): + if any(-0.1 > df_with_allocations["allocation"]) or any(df_with_allocations["allocation"] > 0.1): raise ValueError("Allocation limits breached") From 0b5486f3d280452b0a9749c20c17ed616c6276fd Mon Sep 17 00:00:00 2001 From: NikolaR01 <74156271+NikolaR01@users.noreply.github.com> Date: Thu, 25 Nov 2021 16:06:23 +0100 Subject: [PATCH 4/4] Update tests/test_api.py Co-authored-by: Thomas Oliver <29981664+ta-oliver@users.noreply.github.com> --- tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_api.py b/tests/test_api.py index 030e6bdb..945c4683 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -370,7 +370,7 @@ def test_allocation_limit(test_df): df=test_df_copy, name_of_strategy=available_allocation_algorithms[0], name_of_price_series="close", allocation_lower_limit=0, allocation_upper_limit=0 ) - if all(df_with_allocations["allocation"] != 0): + if not all(df_with_allocations["allocation"] == 0.0): raise ValueError("Allocation limits breached") df_with_allocations = Api.calculate_allocations(