From c82eb8f938e18673f6ab42e468fdbf0c6d376fc7 Mon Sep 17 00:00:00 2001 From: Martin Molinero Date: Thu, 26 Sep 2024 20:07:31 -0300 Subject: [PATCH] Fix live existing cash or holdings state - Minor fix for missing live existing cash holdings state check. Adding unit test reproducing issue --- lean/components/util/live_utils.py | 12 ++++++---- tests/commands/test_live.py | 37 +++++++++++++++++++++++++++++- tests/test_helpers.py | 3 +++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/lean/components/util/live_utils.py b/lean/components/util/live_utils.py index 41e0d0dd..96448a24 100644 --- a/lean/components/util/live_utils.py +++ b/lean/components/util/live_utils.py @@ -99,11 +99,13 @@ def get_last_portfolio_cash_holdings(api_client: APIClient, brokerage_instance: if cash_balance_option != LiveInitialStateInput.NotSupported or holdings_option != LiveInitialStateInput.NotSupported: last_portfolio = _get_last_portfolio(api_client, project_id, project) if last_portfolio is not None: - for key, value in last_portfolio["cash"].items(): - last_cash[key] = InsensitiveCaseDict(value) - for key, value in last_portfolio["holdings"].items(): - last_holdings[key] = InsensitiveCaseDict(value) - last_holdings[key]["symbol"] = InsensitiveCaseDict(last_holdings[key]["symbol"]) + if "cash" in last_portfolio: + for key, value in last_portfolio["cash"].items(): + last_cash[key] = InsensitiveCaseDict(value) + if "holdings" in last_portfolio: + for key, value in last_portfolio["holdings"].items(): + last_holdings[key] = InsensitiveCaseDict(value) + last_holdings[key]["symbol"] = InsensitiveCaseDict(last_holdings[key]["symbol"]) else: last_cash = None last_holdings = None diff --git a/tests/commands/test_live.py b/tests/commands/test_live.py index 1b332edb..92debba7 100644 --- a/tests/commands/test_live.py +++ b/tests/commands/test_live.py @@ -1139,11 +1139,16 @@ def test_live_non_interactive_deploy_with_real_brokerage_without_credentials() - assert "--oanda-environment" in error_msg assert "--iex-price-plan" not in error_msg -def create_lean_option(brokerage_name: str, data_provider_live_name: str, data_provider_historical_name: str, api_client: any) -> Result: + +def create_lean_option(brokerage_name: str, data_provider_live_name: str, data_provider_historical_name: str, + api_client: any, environment_modifier=None) -> Result: reset_state_installed_modules() create_fake_lean_cli_directory() create_fake_environment("live-paper", True) + if environment_modifier: + environment_modifier() + initialize_container(api_client_to_use=api_client) option = ["--brokerage", brokerage_name] @@ -1249,3 +1254,33 @@ def test_live_non_interactive_deploy_paper_brokerage_different_live_data_provide is_exist = True assert is_exist + + +@pytest.mark.parametrize("brokerage_name,data_provider_live_name,existing_cash,existing_holdings", + [("Paper Trading", "Polygon", "True", "True"), + ("Paper Trading", "Polygon", "True", "False"), + ("Paper Trading", "Polygon", "False", "True"), + ("Paper Trading", "Polygon", "False", "False")]) +def test_live_state_file(brokerage_name: str, data_provider_live_name: str, + existing_cash: bool, existing_holdings: bool) -> None: + api_client = mock.MagicMock() + + def environment_modifier(): + result_directory = Path.cwd() / "Python Project" / "live" / "2024-09-26_17-25-28" + result_file = result_directory / f"L-3875119070.json" + result_file.parent.mkdir(parents=True, exist_ok=True) + with open(result_file, "w+") as out_file: + state = {} + if existing_cash: + state["cash"] = {"USD": {"symbol": "USD", "amount": 100000.0}} + if existing_holdings: + state["holdings"] = {"BTCUSD 2XR":{"symbol":{"value":"BTCUSD","id":"BTCUSD 2XR","permtick":"BTCUSD"}, + "type":7,"currencySymbol":"$","averagePrice":64778.92,"quantity":0.3, + "marketPrice":63425.05,"conversionRate":1.0,"marketValue":19027.515, + "unrealizedPnl":-25.98,"unrealizedPnLPercent":-0.13}} + json.dump(state, out_file) + with open(result_directory / "config", "w+") as out_file: + json.dump({"algorithm-language": "Python", "parameters": {}, "id": "3875119070"}, out_file) + + create_lean_option(brokerage_name, data_provider_live_name, None, api_client, + environment_modifier=environment_modifier) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 1738797f..2b13b329 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -104,6 +104,7 @@ def create_fake_lean_cli_directory() -> None: _write_fake_directory(files) + def create_fake_lean_cli_project(name: str, language: str) -> None: """Creates a directory structure similar to the one created by `lean init` with a given project info""" (Path.cwd() / "data").mkdir() @@ -121,6 +122,7 @@ def create_fake_lean_cli_project(name: str, language: str) -> None: _write_fake_directory(files) + def create_fake_lean_cli_directory_with_subdirectories(depth: int) -> None: """Creates a directory structure similar to the one created by `lean init` with a Python and a C# project, and a Python and a C# library""" @@ -229,6 +231,7 @@ def create_lean_environments() -> List[QCLeanEnvironment]: public=True) ] + def reset_state_installed_modules() -> None: for data_provider in (cli_brokerages + cli_data_downloaders + cli_data_queue_handlers + cli_addon_modules + cli_history_provider):