feat: add comprehensive short selling support to qlib backtest framework
This commit introduces a complete short selling framework for qlib that enables:
Core Components:
- ShortableExchange: Exchange supporting short positions with proper fee calculation
- ShortablePosition: Position class handling negative holdings and borrowing costs
- ShortableBacktest: Integration module with ShortableExecutor and LongShortStrategy
- BorrowFeeModel: Configurable borrowing cost calculation framework
Key Features:
- Full short selling support with negative position tracking
- Cross-zero position handling (e.g., long -> flat -> short transitions)
- Proper fee calculation for both legs when crossing zero
- Borrowing cost management with daily settlement
- Risk management with leverage and exposure controls
- Support for crypto and traditional markets with different trading calendars
- Production-grade stability matching qlib standards
Technical Improvements:
- Enhanced position metrics (leverage, net exposure, gross value)
- Robust price validation and fallback mechanisms
- Proper cash settlement for T+1 and immediate modes
- Integration with existing qlib infrastructure
- Comprehensive test coverage with real crypto data
Description
Motivation and Context
How Has This Been Tested?
- [ ] Pass the test by running:
pytest qlib/tests/test_all_pipeline.pyunder upper directory ofqlib. - [ ] If you are adding a new feature, test on your own test scripts.
Screenshots of Test Results (if appropriate):
- Pipeline test:
- Your own tests:
Types of changes
- [ ] Fix bugs
- [ ] Add new feature
- [ ] Update documentation
@JakobWong please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
@microsoft-github-policy-service agree [company="{your company}"]Options:
- (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
- (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"Contributor License Agreement
Contribution License Agreement
This Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”), and conveys certain license rights to Microsoft Corporation and its affiliates (“Microsoft”) for Your contributions to Microsoft open source projects. This Agreement is effective as of the latest signature date below.
- Definitions. “Code” means the computer software code, whether in human-readable or machine-executable form, that is delivered by You to Microsoft under this Agreement. “Project” means any of the projects owned or managed by Microsoft and offered under a license approved by the Open Source Initiative (www.opensource.org). “Submit” is the act of uploading, submitting, transmitting, or distributing code or other content to any Project, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Project for the purpose of discussing and improving that Project, but excluding communication that is conspicuously marked or otherwise designated in writing by You as “Not a Submission.” “Submission” means the Code and any other copyrightable material Submitted by You, including any associated comments and documentation.
- Your Submission. You must agree to the terms of this Agreement before making a Submission to any Project. This Agreement covers any and all Submissions that You, now or in the future (except as described in Section 4 below), Submit to any Project.
- Originality of Work. You represent that each of Your Submissions is entirely Your original work. Should You wish to Submit materials that are not Your original work, You may Submit them separately to the Project if You (a) retain all copyright and license information that was in the materials as You received them, (b) in the description accompanying Your Submission, include the phrase “Submission containing materials of a third party:” followed by the names of the third party and any licenses or other restrictions of which You are aware, and (c) follow any other instructions in the Project’s written guidelines concerning Submissions.
- Your Employer. References to “employer” in this Agreement include Your employer or anyone else for whom You are acting in making Your Submission, e.g. as a contractor, vendor, or agent. If Your Submission is made in the course of Your work for an employer or Your employer has intellectual property rights in Your Submission by contract or applicable law, You must secure permission from Your employer to make the Submission before signing this Agreement. In that case, the term “You” in this Agreement will refer to You and the employer collectively. If You change employers in the future and desire to Submit additional Submissions for the new employer, then You agree to sign a new Agreement and secure permission from the new employer before Submitting those Submissions.
- Licenses.
- Copyright License. You grant Microsoft, and those who receive the Submission directly or indirectly from Microsoft, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license in the Submission to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute the Submission and such derivative works, and to sublicense any or all of the foregoing rights to third parties.
- Patent License. You grant Microsoft, and those who receive the Submission directly or indirectly from Microsoft, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license under Your patent claims that are necessarily infringed by the Submission or the combination of the Submission with the Project to which it was Submitted to make, have made, use, offer to sell, sell and import or otherwise dispose of the Submission alone or with the Project.
- Other Rights Reserved. Each party reserves all rights not expressly granted in this Agreement. No additional licenses or rights whatsoever (including, without limitation, any implied licenses) are granted by implication, exhaustion, estoppel or otherwise.
- Representations and Warranties. You represent that You are legally entitled to grant the above licenses. You represent that each of Your Submissions is entirely Your original work (except as You may have disclosed under Section 3). You represent that You have secured permission from Your employer to make the Submission in cases where Your Submission is made in the course of Your work for Your employer or Your employer has intellectual property rights in Your Submission by contract or applicable law. If You are signing this Agreement on behalf of Your employer, You represent and warrant that You have the necessary authority to bind the listed employer to the obligations contained in this Agreement. You are not expected to provide support for Your Submission, unless You choose to do so. UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, AND EXCEPT FOR THE WARRANTIES EXPRESSLY STATED IN SECTIONS 3, 4, AND 6, THE SUBMISSION PROVIDED UNDER THIS AGREEMENT IS PROVIDED WITHOUT WARRANTY OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY OF NONINFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
- Notice to Microsoft. You agree to notify Microsoft in writing of any facts or circumstances of which You later become aware that would make Your representations in this Agreement inaccurate in any respect.
- Information about Submissions. You agree that contributions to Projects and information about contributions may be maintained indefinitely and disclosed publicly, including Your name and other information that You submit with Your Submission.
- Governing Law/Jurisdiction. This Agreement is governed by the laws of the State of Washington, and the parties consent to exclusive jurisdiction and venue in the federal courts sitting in King County, Washington, unless no federal subject matter jurisdiction exists, in which case the parties consent to exclusive jurisdiction and venue in the Superior Court of King County, Washington. The parties waive all defenses of lack of personal jurisdiction and forum non-conveniens.
- Entire Agreement/Assignment. This Agreement is the entire agreement between the parties, and supersedes any and all prior agreements, understandings or communications, written or oral, between the parties relating to the subject matter hereof. This Agreement may be assigned by Microsoft.
@microsoft-github-policy-service agree
Hi, @JakobWong
First of all, thank you for your contribution to qlib, secondly, I would like to know where did you get this data ~/.qlib/qlib_data/crypto_data_perp from? I think it would be very helpful for my testing.
Hi, @JakobWong First of all, thank you for your contribution to qlib, secondly, I would like to know where did you get this data
~/.qlib/qlib_data/crypto_data_perpfrom? I think it would be very helpful for my testing.
Hello, I implemented my own crypto data collector and it was not included in this pr. You may test it with regular regions or if it's necessary I can include it in this pr as well
Hi, @JakobWong
I tried to test the code qlib/example/workflow_by_code_longshort_crypto.py, and found a problem with the circular import.
workflow_by_code_longshort_crypto.py → qlib.workflow.record_temp → qlib.contrib.evaluate → qlib.strategy.base → qlib.backtest → qlib.workflow.record_temp
And a lot of circular import, eventually lead to Memory Error, I think this problem may have something to do with the system, macOS/Linux multiprocessing uses fork mode, and will not trigger this problem, windows multiprocessing does not support fork mode, will trigger the circular import problem. Please fix this bug.
Also, could you move the three samples codes from qlib/examples to the examples/ folder in the project root?
Hi, @JakobWong I tried to test the code
qlib/example/workflow_by_code_longshort_crypto.py, and found a problem with the circular import.
workflow_by_code_longshort_crypto.py → qlib.workflow.record_temp → qlib.contrib.evaluate → qlib.strategy.base → qlib.backtest → qlib.workflow.record_tempAnd a lot of circular import, eventually lead to Memory Error, I think this problem may have something to do with the system, macOS/Linux multiprocessing uses fork mode, and will not trigger this problem, windows multiprocessing does not support fork mode, will trigger the circular import problem. Please fix this bug.
Also, could you move the three samples codes from
qlib/examplesto theexamples/folder in the project root?
Hi @SunsetWolf,
Thanks for catching this! I've done updates as follows:
Fixed:
- Circular imports: Moved problematic imports inside
if __name__ == "__main__":and addedmultiprocessing.freeze_support()for Windows compatibility - File locations: Moved all three example files from
qlib/examples/to project rootexamples/as requested
The main culprit was runtime imports triggering the circular
chain you identified. I've also added a FAST_DEBUG mode
for quicker testing.
Changes are pushed to feature/shortable-trading-support.
Let me know if you spot any other issues!
Hi, @JakobWong
Thanks for fixing bug, I'm trying to run the sample code on the latest modification. Some issues were found:
-
workflow_by_code_longshort_crypto.pyreports:crypto_qlib_config.pyfile is missing. Here is the output of my terminal:[18652:MainThread](2025-08-22 12:43:00,535) INFO - qlib.Initialization - [config.py:457] - default_conf: client. [18652:MainThread](2025-08-22 12:43:00,583) INFO - qlib.Initialization - [__init__.py:75] - qlib successfully initialized based on client settings. [18652:MainThread](2025-08-22 12:43:00,583) INFO - qlib.Initialization - [__init__.py:77] - data_path={'__DEFAULT_FREQ': WindowsPath('C:/Users/admin/.qlib/qlib_data/cn_data')} [18652:MainThread](2025-08-22 12:51:49,007) INFO - qlib.timer - [log.py:127] - Time cost: 508.061s | Loading data Done [18652:MainThread](2025-08-22 12:51:51,077) INFO - qlib.timer - [log.py:127] - Time cost: 0.531s | DropnaLabel Done [18652:MainThread](2025-08-22 12:52:00,301) INFO - qlib.timer - [log.py:127] - Time cost: 9.224s | CSZScoreNorm Done [18652:MainThread](2025-08-22 12:52:00,412) INFO - qlib.timer - [log.py:127] - Time cost: 11.404s | fit & process data Done [18652:MainThread](2025-08-22 12:52:00,412) INFO - qlib.timer - [log.py:127] - Time cost: 519.465s | Init data Done [18652:MainThread](2025-08-22 12:52:00,417) ERROR - qlib.workflow - [utils.py:41] - An exception has been raised[FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\admin\\Desktop\\qlib\\1986\\crypto-qlib\\crypto_qlib_config.py']. File "C:\Users\admin\Desktop\qlib\1986\qlib\examples\workflow_by_code_longshort_crypto.py", line 121, in <module> spec.loader.exec_module(crypto_cfg) File "<frozen importlib._bootstrap_external>", line 879, in exec_module File "<frozen importlib._bootstrap_external>", line 1016, in get_code File "<frozen importlib._bootstrap_external>", line 1073, in get_data FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\admin\\Desktop\\qlib\\1986\\crypto-qlib\\crypto_qlib_config.py'- I noticed that
crypto_qlib_config.pyis not mentioned in this PR. How do I get this file?
- I noticed that
-
shortable_debug_day.pyreports:ShortableExecutorhas nocommon_infraattribute. Here is the output of my terminal:[35616:MainThread](2025-08-22 13:15:52,257) INFO - qlib.Initialization - [config.py:457] - default_conf: client. [35616:MainThread](2025-08-22 13:15:56,045) INFO - qlib.Initialization - [__init__.py:75] - qlib successfully initialized based on client settings. [35616:MainThread](2025-08-22 13:15:56,045) INFO - qlib.Initialization - [__init__.py:77] - data_path={'__DEFAULT_FREQ': WindowsPath('C:/Users/admin/.qlib/qlib_data/cn_data')} [35616:MainThread](2025-08-22 13:15:57,937) WARNING - qlib.data - [data.py:665] - load calendar error: freq=day, future=True; return current calendar! [35616:MainThread](2025-08-22 13:15:57,942) WARNING - qlib.data - [data.py:668] - You can get future calendar by referring to the following document: https://github.com/microsoft/qlib/blob/main/scripts/data_collector/contrib/README.md [35616:MainThread](2025-08-22 13:15:57,994) WARNING - qlib.BaseExecutor - [executor.py:121] - `common_infra` is not set for <qlib.backtest.shortable_backtest.ShortableExecutor object at 0x00000282F202FC40> [35616:MainThread](2025-08-22 13:15:58,036) ERROR - qlib.workflow - [utils.py:41] - An exception has been raised[AttributeError: 'ShortableExecutor' object has no attribute 'common_infra']. File "C:\Users\admin\Desktop\qlib\1986\qlib\examples\shortable_backtest_crypto_loop.py", line 91, in <module> main() File "C:\Users\admin\Desktop\qlib\1986\qlib\examples\shortable_backtest_crypto_loop.py", line 77, in main strat.reset(level_infra=exe.get_level_infra(), common_infra=exe.common_infra) AttributeError: 'ShortableExecutor' object has no attribute 'common_infra'I think the reason for this problem is that the
common_infraparameter is not passed during construction. Please fix this bug.
Hi, @JakobWong
Thanks for fixing bug, I'm trying to run the sample code on the latest modification. Some issues were found:
workflow_by_code_longshort_crypto.pyreports:crypto_qlib_config.pyfile is missing. Here is the output of my terminal:[18652:MainThread](2025-08-22 12:43:00,535) INFO - qlib.Initialization - [config.py:457] - default_conf: client. [18652:MainThread](2025-08-22 12:43:00,583) INFO - qlib.Initialization - [__init__.py:75] - qlib successfully initialized based on client settings. [18652:MainThread](2025-08-22 12:43:00,583) INFO - qlib.Initialization - [__init__.py:77] - data_path={'__DEFAULT_FREQ': WindowsPath('C:/Users/admin/.qlib/qlib_data/cn_data')} [18652:MainThread](2025-08-22 12:51:49,007) INFO - qlib.timer - [log.py:127] - Time cost: 508.061s | Loading data Done [18652:MainThread](2025-08-22 12:51:51,077) INFO - qlib.timer - [log.py:127] - Time cost: 0.531s | DropnaLabel Done [18652:MainThread](2025-08-22 12:52:00,301) INFO - qlib.timer - [log.py:127] - Time cost: 9.224s | CSZScoreNorm Done [18652:MainThread](2025-08-22 12:52:00,412) INFO - qlib.timer - [log.py:127] - Time cost: 11.404s | fit & process data Done [18652:MainThread](2025-08-22 12:52:00,412) INFO - qlib.timer - [log.py:127] - Time cost: 519.465s | Init data Done [18652:MainThread](2025-08-22 12:52:00,417) ERROR - qlib.workflow - [utils.py:41] - An exception has been raised[FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\admin\\Desktop\\qlib\\1986\\crypto-qlib\\crypto_qlib_config.py']. File "C:\Users\admin\Desktop\qlib\1986\qlib\examples\workflow_by_code_longshort_crypto.py", line 121, in <module> spec.loader.exec_module(crypto_cfg) File "<frozen importlib._bootstrap_external>", line 879, in exec_module File "<frozen importlib._bootstrap_external>", line 1016, in get_code File "<frozen importlib._bootstrap_external>", line 1073, in get_data FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\admin\\Desktop\\qlib\\1986\\crypto-qlib\\crypto_qlib_config.py'
- I noticed that
crypto_qlib_config.pyis not mentioned in this PR. How do I get this file?
shortable_debug_day.pyreports:ShortableExecutorhas nocommon_infraattribute. Here is the output of my terminal:[35616:MainThread](2025-08-22 13:15:52,257) INFO - qlib.Initialization - [config.py:457] - default_conf: client. [35616:MainThread](2025-08-22 13:15:56,045) INFO - qlib.Initialization - [__init__.py:75] - qlib successfully initialized based on client settings. [35616:MainThread](2025-08-22 13:15:56,045) INFO - qlib.Initialization - [__init__.py:77] - data_path={'__DEFAULT_FREQ': WindowsPath('C:/Users/admin/.qlib/qlib_data/cn_data')} [35616:MainThread](2025-08-22 13:15:57,937) WARNING - qlib.data - [data.py:665] - load calendar error: freq=day, future=True; return current calendar! [35616:MainThread](2025-08-22 13:15:57,942) WARNING - qlib.data - [data.py:668] - You can get future calendar by referring to the following document: https://github.com/microsoft/qlib/blob/main/scripts/data_collector/contrib/README.md [35616:MainThread](2025-08-22 13:15:57,994) WARNING - qlib.BaseExecutor - [executor.py:121] - `common_infra` is not set for <qlib.backtest.shortable_backtest.ShortableExecutor object at 0x00000282F202FC40> [35616:MainThread](2025-08-22 13:15:58,036) ERROR - qlib.workflow - [utils.py:41] - An exception has been raised[AttributeError: 'ShortableExecutor' object has no attribute 'common_infra']. File "C:\Users\admin\Desktop\qlib\1986\qlib\examples\shortable_backtest_crypto_loop.py", line 91, in <module> main() File "C:\Users\admin\Desktop\qlib\1986\qlib\examples\shortable_backtest_crypto_loop.py", line 77, in main strat.reset(level_infra=exe.get_level_infra(), common_infra=exe.common_infra) AttributeError: 'ShortableExecutor' object has no attribute 'common_infra'I think the reason for this problem is that the
common_infraparameter is not passed during construction. Please fix this bug.
Hi @SunsetWolf ,
Thanks again for testing! Both issues you reported have been addressed:
- Missing
crypto_qlib_config.py
I added a new crypto_record_temp.py in qlib.contrib.
The examples now use CryptoPortAnaRecord directly from contrib instead of relying on external config files.
ShortableExecutormissingcommon_infra
Updated the executor initialization to correctly handle the common_infra attribute and other required parameters.
With these fixes, the latest version should run without problems.
Thanks for the thorough Windows testing—it’s been really helpful in catching these issues!
Hi, @JakobWong
Thanks for fixing the previously mentioned bugs, I tried to run shortable_backtest_crypto_loop.py, and the problem disappeared, which is good news.
But workflow_by_code_longshort_crypto.py found a new problem during backtest. The benchmark csi300 during the backtest is not included in the datasource, but it is included in the official dataset of qlib. Please fix this bug.
Here is the output of my terminal.
>>>python examples/workflow_by_code_longshort_crypto.py
[45604:MainThread](2025-08-25 13:35:04,482) INFO - qlib.Initialization - [config.py:457] - default_conf: client.
[45604:MainThread](2025-08-25 13:35:04,494) INFO - qlib.Initialization - [__init__.py:75] - qlib successfully initialized based on client settings.
[45604:MainThread](2025-08-25 13:35:04,494) INFO - qlib.Initialization - [__init__.py:77] - data_path={'__DEFAULT_FREQ': WindowsPath('C:/Users/admin/.qlib/qlib_data/cn_data')}
ModuleNotFoundError. CatBoostModel are skipped. (optional: maybe installing CatBoostModel can fix it.)
ModuleNotFoundError. XGBModel is skipped(optional: maybe installing xgboost can fix it).
[45604:MainThread](2025-08-25 13:44:16,060) INFO - qlib.timer - [log.py:127] - Time cost: 536.249s | Loading data Done
[45604:MainThread](2025-08-25 13:44:17,825) INFO - qlib.timer - [log.py:127] - Time cost: 0.494s | DropnaLabel Done
[45604:MainThread](2025-08-25 13:44:26,304) INFO - qlib.timer - [log.py:127] - Time cost: 8.479s | CSZScoreNorm Done
[45604:MainThread](2025-08-25 13:44:26,413) INFO - qlib.timer - [log.py:127] - Time cost: 10.353s | fit & process data Done
[45604:MainThread](2025-08-25 13:44:26,413) INFO - qlib.timer - [log.py:127] - Time cost: 546.603s | Init data Done
Using contrib's crypto version of CryptoPortAnaRecord as PortAnaRecord
KMID KLEN KMID2 ... VSUMD30 VSUMD60 Ref($close, -2) / Ref($close, -1) - 1
datetime instrument ...
2008-01-02 SH600000 0.010374 0.061129 0.169699 ... 0.028076 -0.020675 0.042064
SH600004 0.057280 0.059661 0.960094 ... 0.152031 -0.029863 0.000000
SH600006 0.012673 0.040323 0.314283 ... 0.010894 -0.049861 -0.008830
SH600007 0.066977 0.084186 0.795580 ... 0.294774 0.159104 0.007495
SH600008 0.051163 0.082326 0.621469 ... 0.199027 0.060235 -0.015446
[5 rows x 159 columns]
[45604:MainThread](2025-08-25 13:44:28,753) INFO - qlib.workflow - [exp.py:258] - Experiment 751081396690558920 starts running ...
[45604:MainThread](2025-08-25 13:44:31,656) INFO - qlib.workflow - [recorder.py:345] - Recorder a047b47665574221be0ad1ffff5bf27d starts running under Experiment 751081396690558920 ...
Training until validation scores don't improve for 50 rounds
[20] train's l2: 0.981256 valid's l2: 0.99289
[40] train's l2: 0.973505 valid's l2: 0.992926
[60] train's l2: 0.966997 valid's l2: 0.993487
[80] train's l2: 0.961038 valid's l2: 0.993884
Early stopping, best iteration is:
[35] train's l2: 0.975252 valid's l2: 0.992748
[45604:MainThread](2025-08-25 13:44:53,791) INFO - qlib.workflow - [record_temp.py:198] - Signal record 'pred.pkl' has been saved as the artifact of the Experiment 751081396690558920
'The following are prediction results of the LGBModel model.'
score
datetime instrument
2017-01-03 SH600000 -0.054091
SH600008 0.014351
SH600009 0.046024
SH600010 0.015651
SH600015 -0.099635
Downloading artifacts: 0%| | 0/1 [00:00<?, ?it/s]
Downloading artifacts: 100%|█████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 32.14it/s]
Downloading artifacts: 0%| | 0/1 [00:00<?, ?it/s]
Downloading artifacts: 100%|█████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 32.40it/s]
{'IC': 0.04669529053598672,
'ICIR': 0.38483657235226576,
'Rank IC': 0.04792825961375113,
'Rank ICIR': 0.4153376423651454}
Downloading artifacts: 0%| | 0/1 [00:00<?, ?it/s]
Downloading artifacts: 100%|█████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 96.65it/s]
[45604:MainThread](2025-08-25 13:44:57,976) INFO - qlib.timer - [log.py:127] - Time cost: 0.000s | waiting `async_log` Done
[45604:MainThread](2025-08-25 13:44:57,986) ERROR - qlib.workflow - [utils.py:41] - An exception has been raised[ValueError: The benchmark ['csi300'] does not exist. Please provide the right benchmark].
File "C:\Users\admin\Desktop\qlib\1986\qlib\examples\workflow_by_code_longshort_crypto.py", line 197, in <module>
par.generate()
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\workflow\record_temp.py", line 236, in generate
artifact_dict = self._generate(*args, **kwargs)
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\contrib\workflow\crypto_record_temp.py", line 98, in _generate
portfolio_metric_dict, indicator_dict = normal_backtest(
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\__init__.py", line 301, in backtest
trade_strategy, trade_executor = get_strategy_executor(
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\__init__.py", line 211, in get_strategy_executor
trade_account = create_account_instance(
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\__init__.py", line 179, in create_account_instance
return Account(
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\account.py", line 109, in __init__
self.init_vars(init_cash, position_dict, freq, benchmark_config)
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\account.py", line 130, in init_vars
self.reset(freq=freq, benchmark_config=benchmark_config)
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\account.py", line 175, in reset
self.reset_report(self.freq, self.benchmark_config)
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\account.py", line 143, in reset_report
self.portfolio_metrics = PortfolioMetrics(freq, benchmark_config)
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\report.py", line 76, in __init__
self.init_bench(freq=freq, benchmark_config=benchmark_config)
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\report.py", line 94, in init_bench
self.bench = self._cal_benchmark(self.benchmark_config, self.freq)
File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\report.py", line 116, in _cal_benchmark
raise ValueError(f"The benchmark {_codes} does not exist. Please provide the right benchmark")
ValueError: The benchmark ['csi300'] does not exist. Please provide the right benchmark
Hi, @JakobWong Thanks for fixing the previously mentioned bugs, I tried to run
shortable_backtest_crypto_loop.py, and the problem disappeared, which is good news. Butworkflow_by_code_longshort_crypto.pyfound a new problem during backtest. The benchmarkcsi300during the backtest is not included in the datasource, but it is included in the official dataset of qlib. Please fix this bug. Here is the output of my terminal.>>>python examples/workflow_by_code_longshort_crypto.py [45604:MainThread](2025-08-25 13:35:04,482) INFO - qlib.Initialization - [config.py:457] - default_conf: client. [45604:MainThread](2025-08-25 13:35:04,494) INFO - qlib.Initialization - [__init__.py:75] - qlib successfully initialized based on client settings. [45604:MainThread](2025-08-25 13:35:04,494) INFO - qlib.Initialization - [__init__.py:77] - data_path={'__DEFAULT_FREQ': WindowsPath('C:/Users/admin/.qlib/qlib_data/cn_data')} ModuleNotFoundError. CatBoostModel are skipped. (optional: maybe installing CatBoostModel can fix it.) ModuleNotFoundError. XGBModel is skipped(optional: maybe installing xgboost can fix it). [45604:MainThread](2025-08-25 13:44:16,060) INFO - qlib.timer - [log.py:127] - Time cost: 536.249s | Loading data Done [45604:MainThread](2025-08-25 13:44:17,825) INFO - qlib.timer - [log.py:127] - Time cost: 0.494s | DropnaLabel Done [45604:MainThread](2025-08-25 13:44:26,304) INFO - qlib.timer - [log.py:127] - Time cost: 8.479s | CSZScoreNorm Done [45604:MainThread](2025-08-25 13:44:26,413) INFO - qlib.timer - [log.py:127] - Time cost: 10.353s | fit & process data Done [45604:MainThread](2025-08-25 13:44:26,413) INFO - qlib.timer - [log.py:127] - Time cost: 546.603s | Init data Done Using contrib's crypto version of CryptoPortAnaRecord as PortAnaRecord KMID KLEN KMID2 ... VSUMD30 VSUMD60 Ref($close, -2) / Ref($close, -1) - 1 datetime instrument ... 2008-01-02 SH600000 0.010374 0.061129 0.169699 ... 0.028076 -0.020675 0.042064 SH600004 0.057280 0.059661 0.960094 ... 0.152031 -0.029863 0.000000 SH600006 0.012673 0.040323 0.314283 ... 0.010894 -0.049861 -0.008830 SH600007 0.066977 0.084186 0.795580 ... 0.294774 0.159104 0.007495 SH600008 0.051163 0.082326 0.621469 ... 0.199027 0.060235 -0.015446 [5 rows x 159 columns] [45604:MainThread](2025-08-25 13:44:28,753) INFO - qlib.workflow - [exp.py:258] - Experiment 751081396690558920 starts running ... [45604:MainThread](2025-08-25 13:44:31,656) INFO - qlib.workflow - [recorder.py:345] - Recorder a047b47665574221be0ad1ffff5bf27d starts running under Experiment 751081396690558920 ... Training until validation scores don't improve for 50 rounds [20] train's l2: 0.981256 valid's l2: 0.99289 [40] train's l2: 0.973505 valid's l2: 0.992926 [60] train's l2: 0.966997 valid's l2: 0.993487 [80] train's l2: 0.961038 valid's l2: 0.993884 Early stopping, best iteration is: [35] train's l2: 0.975252 valid's l2: 0.992748 [45604:MainThread](2025-08-25 13:44:53,791) INFO - qlib.workflow - [record_temp.py:198] - Signal record 'pred.pkl' has been saved as the artifact of the Experiment 751081396690558920 'The following are prediction results of the LGBModel model.' score datetime instrument 2017-01-03 SH600000 -0.054091 SH600008 0.014351 SH600009 0.046024 SH600010 0.015651 SH600015 -0.099635 Downloading artifacts: 0%| | 0/1 [00:00<?, ?it/s] Downloading artifacts: 100%|█████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 32.14it/s] Downloading artifacts: 0%| | 0/1 [00:00<?, ?it/s] Downloading artifacts: 100%|█████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 32.40it/s] {'IC': 0.04669529053598672, 'ICIR': 0.38483657235226576, 'Rank IC': 0.04792825961375113, 'Rank ICIR': 0.4153376423651454} Downloading artifacts: 0%| | 0/1 [00:00<?, ?it/s] Downloading artifacts: 100%|█████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 96.65it/s] [45604:MainThread](2025-08-25 13:44:57,976) INFO - qlib.timer - [log.py:127] - Time cost: 0.000s | waiting `async_log` Done [45604:MainThread](2025-08-25 13:44:57,986) ERROR - qlib.workflow - [utils.py:41] - An exception has been raised[ValueError: The benchmark ['csi300'] does not exist. Please provide the right benchmark]. File "C:\Users\admin\Desktop\qlib\1986\qlib\examples\workflow_by_code_longshort_crypto.py", line 197, in <module> par.generate() File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\workflow\record_temp.py", line 236, in generate artifact_dict = self._generate(*args, **kwargs) File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\contrib\workflow\crypto_record_temp.py", line 98, in _generate portfolio_metric_dict, indicator_dict = normal_backtest( File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\__init__.py", line 301, in backtest trade_strategy, trade_executor = get_strategy_executor( File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\__init__.py", line 211, in get_strategy_executor trade_account = create_account_instance( File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\__init__.py", line 179, in create_account_instance return Account( File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\account.py", line 109, in __init__ self.init_vars(init_cash, position_dict, freq, benchmark_config) File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\account.py", line 130, in init_vars self.reset(freq=freq, benchmark_config=benchmark_config) File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\account.py", line 175, in reset self.reset_report(self.freq, self.benchmark_config) File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\account.py", line 143, in reset_report self.portfolio_metrics = PortfolioMetrics(freq, benchmark_config) File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\report.py", line 76, in __init__ self.init_bench(freq=freq, benchmark_config=benchmark_config) File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\report.py", line 94, in init_bench self.bench = self._cal_benchmark(self.benchmark_config, self.freq) File "C:\Users\admin\Desktop\qlib\1986\qlib\qlib\backtest\report.py", line 116, in _cal_benchmark raise ValueError(f"The benchmark {_codes} does not exist. Please provide the right benchmark") ValueError: The benchmark ['csi300'] does not exist. Please provide the right benchmark
Hey @SunsetWolf,
Thanks for checking this. I updated the script so that if the initialized data path contains cn_data, we use SH000300; otherwise we keep BTCUSDT. The problem of the example script should be fixed now.
Hey, @JakobWong Thanks for fixing these issues, the sample code in examples currently works. This is good news. I see that there are still some Chinese comments, please revert them to English. Also, I see that the CI hasn't passed yet, please fix the problems in the CI.
Hey, @JakobWong Thanks for fixing these issues, the sample code in examples currently works. This is good news. I see that there are still some Chinese comments, please revert them to English. Also, I see that the CI hasn't passed yet, please fix the problems in the CI.
Hey @SunsetWolf,
Thanks for the feedback! Just pushed the fixes:
All Chinese comments across the codebase have been converted to English.
The linting problems should be resolved now with the code formatting improvements.
Hey @SunsetWolf,
It looks like there's a workflow awaiting your approval
@SunsetWolf, could you run a CI test for this new push? hopefully that would settle some errors in the previous version. Would be great to have it merge soon so people can start using it
Hey, @JakobWong
I apologize for the long time between replies, I reopened this PR today, tested the test_shortable_crypto_real.py script, and found two errors:
AttributeError: ‘ShortableExecutor’ object has no attribute ‘position’. AttributeError: 'ShortableExecutor' object has no attribute 'position'
AttributeError: ' LongShortStrategy' object has no attribute 'trade_calendar'
Solution:
- For the first problem, try executing the following before calling
exe.position
common_infra = CommonInfrastructure(trade_account=account, trade_exchange=ex)
exe.reset_common_infra( common_infra=common_infra)
I think this solution is only temporary, is there a more elegant solution?
- The second problem, I think, is that
LongShortStrategydoes not inherit fromBaseStrategy.
Please change the code so that test_shortable_crypto_real.py passes.