From 28fe4d4bb449e0aedf10a27ec4b0738a36430815 Mon Sep 17 00:00:00 2001 From: Young Date: Sun, 3 Oct 2021 05:31:43 +0000 Subject: [PATCH] update file strategy test --- qlib/backtest/position.py | 20 ++++++++------ qlib/workflow/record_temp.py | 2 +- tests/backtest/test_file_strategy.py | 40 ++++++++++++++++++---------- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/qlib/backtest/position.py b/qlib/backtest/position.py index 2bfb20893..7abe85381 100644 --- a/qlib/backtest/position.py +++ b/qlib/backtest/position.py @@ -345,15 +345,19 @@ class Position(BasePosition): if stock_id not in self.position: raise KeyError("{} not in current position".format(stock_id)) else: - # decrease the amount of stock - self.position[stock_id]["amount"] -= trade_amount - # check if to delete - if self.position[stock_id]["amount"] < -1e-5: - raise ValueError( - "only have {} {}, require {}".format(self.position[stock_id]["amount"], stock_id, trade_amount) - ) - elif abs(self.position[stock_id]["amount"]) <= 1e-5: + if np.isclose(self.position[stock_id]["amount"], trade_amount): + # Selling all the stocks + # we use np.isclose instead of abs() <= 1e-5 because `np.isclose` consider both ralative amount and absolute amount + # Using abs() <= 1e-5 will result in error when the amount is large self._del_stock(stock_id) + else: + # decrease the amount of stock + self.position[stock_id]["amount"] -= trade_amount + # check if to delete + if self.position[stock_id]["amount"] < -1e-5: + raise ValueError( + "only have {} {}, require {}".format(self.position[stock_id]["amount"], stock_id, trade_amount) + ) new_cash = trade_val - cost if self._settle_type == self.ST_CASH: diff --git a/qlib/workflow/record_temp.py b/qlib/workflow/record_temp.py index 4a3898a25..0d85311ee 100644 --- a/qlib/workflow/record_temp.py +++ b/qlib/workflow/record_temp.py @@ -102,7 +102,7 @@ class RecordTemp: def check(self, include_self: bool = False): """ Check if the records is properly generated and saved. - It is useful in fololwing examples + It is useful in following examples - checking if the depended files complete before genrating new things. - checking if the final files is completed diff --git a/tests/backtest/test_file_strategy.py b/tests/backtest/test_file_strategy.py index 83b179397..c0bb87e34 100644 --- a/tests/backtest/test_file_strategy.py +++ b/tests/backtest/test_file_strategy.py @@ -6,19 +6,20 @@ from qlib.backtest import backtest, decision from qlib.tests import TestAutoData import pandas as pd from pathlib import Path +from qlib.data import D +import numpy as np DIRNAME = Path(__file__).absolute().resolve().parent class FileStrTest(TestAutoData): - + # Assumption to ensure the correctness of the test + # - No price adjustment in these several trading days. TEST_INST = "SH600519" EXAMPLE_FILE = DIRNAME / "order_example.csv" - DEAL_NUM_FOR_1000 = 123.47105436976445 - - def _gen_orders(self) -> pd.DataFrame: + def _gen_orders(self, dealt_num_for_1000) -> pd.DataFrame: headers = [ "datetime", "instrument", @@ -37,18 +38,29 @@ class FileStrTest(TestAutoData): # test min_cost for selling ["20200109", self.TEST_INST, "1", "sell"], # test selling all stocks - ["20200110", self.TEST_INST, str(self.DEAL_NUM_FOR_1000), "sell"], + ["20200110", self.TEST_INST, str(dealt_num_for_1000), "sell"], ] return pd.DataFrame(orders, columns=headers).set_index(["datetime", "instrument"]) def test_file_str(self): + # 0) basic settings + account_money = 150000 - orders = self._gen_orders() + # 1) get information + df = D.features([self.TEST_INST], ["$close", "$factor"], start_time="20200103", end_time="20200103") + price = df["$close"].item() + factor = df["$factor"].item() + price_unit = price / factor * 100 + dealt_num_for_1000 = (account_money // price_unit) * (100 / factor) + + # 2) generate orders + orders = self._gen_orders(dealt_num_for_1000) print(orders) orders.to_csv(self.EXAMPLE_FILE) orders = pd.read_csv(self.EXAMPLE_FILE, index_col=["datetime", "instrument"]) + # 3) run the strategy strategy_config = { "class": "FileOrderStrategy", "module_path": "qlib.contrib.strategy.rule_strategy", @@ -63,7 +75,7 @@ class FileStrTest(TestAutoData): backtest_config = { "start_time": start_time, "end_time": end_time, - "account": 30000, + "account": account_money, "benchmark": None, # benchmark is not required here for trading "exchange_kwargs": { "freq": freq, @@ -73,7 +85,7 @@ class FileStrTest(TestAutoData): "close_cost": 0.0015, "min_cost": 500, "codes": codes, - "trade_unit": 2, + "trade_unit": 100, }, # "pos_type": "InfPosition" # Position with infinitive position } @@ -94,12 +106,12 @@ class FileStrTest(TestAutoData): # ffr valid ffr_dict = indicator_dict["1day"]["ffr"].to_dict() ffr_dict = {str(date).split()[0]: ffr_dict[date] for date in ffr_dict} - assert ffr_dict["2020-01-03"] == self.DEAL_NUM_FOR_1000 / 1000 - assert ffr_dict["2020-01-06"] == 0 - assert ffr_dict["2020-01-07"] == self.DEAL_NUM_FOR_1000 / 1000 - assert ffr_dict["2020-01-08"] == self.DEAL_NUM_FOR_1000 / 1000 - assert ffr_dict["2020-01-09"] == 0 - assert ffr_dict["2020-01-10"] == 1 + assert np.isclose(ffr_dict["2020-01-03"], dealt_num_for_1000 / 1000) + assert np.isclose(ffr_dict["2020-01-06"], 0) + assert np.isclose(ffr_dict["2020-01-07"], dealt_num_for_1000 / 1000) + assert np.isclose(ffr_dict["2020-01-08"], dealt_num_for_1000 / 1000) + assert np.isclose(ffr_dict["2020-01-09"], 0) + assert np.isclose(ffr_dict["2020-01-10"], 1) self.EXAMPLE_FILE.unlink()