Skip to content

Commit 562f5bf

Browse files
authored
Merge pull request #2 from HalcyonByh/main
feature: add Windows support for workspace activation script
2 parents 99334d4 + 2025de9 commit 562f5bf

File tree

5 files changed

+152
-25
lines changed

5 files changed

+152
-25
lines changed

docs/70_WALKTHROUGH.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ mkdir -p ~/.qlib/qlib_data/cn_data
9393
# 使用 wget 下载最新版本数据
9494
wget https://github.com/chenditc/investment_data/releases/latest/download/qlib_bin.tar.gz
9595
# 解压到数据目录
96-
tar -xzf qlib_bin.tar.gz -C ~/.qlib/qlib_data/cn_data
96+
tar -zxvf qlib_bin.tar.gz -C ~/.qlib/qlib_data/cn_data --strip-components=1
9797
```
9898

9999
> 详见 https://github.com/chenditc/investment_data 了解数据更新频率和内容说明。
@@ -106,7 +106,12 @@ tar -xzf qlib_bin.tar.gz -C ~/.qlib/qlib_data/cn_data
106106
export QLIB_DATA_DIR="/your/custom/path/cn_data"
107107
export QLIB_REGION="cn"
108108
```
109+
如果使用Windows系统的PowerShell,可在工作区的`run_env.ps1`中配置:
109110

111+
```bash
112+
$env:QLIB_DATA_DIR = "X:/your/custom/path/cn_data"
113+
$env:QLIB_REGION = "cn"
114+
```
110115
---
111116

112117
## 3. 初始化工作区
@@ -123,10 +128,18 @@ QuantPits 采用 **Workspace(工作区)** 机制实现配置与数据的完
123128
# cd QuantPits
124129

125130
source workspaces/Demo_Workspace/run_env.sh
131+
132+
# For Windows
133+
. ./workspaces/Demo_Workspace/run_env.ps1
126134
```
127135

128136
输出 `Workspace activated: .../Demo_Workspace` 表示激活成功。
129137

138+
**特别提醒:**在 Windows 上,如果你要让 PowerShell 执行 .ps1 脚本,如果这是你第一次运行脚本,PowerShell 可能会报“禁止执行脚本”的错误。此时你需要以**管理员身份**运行 PowerShell,并输入:
139+
```bash
140+
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
141+
```
142+
130143
### 3.2 创建新工作区
131144

132145
```bash
@@ -144,6 +157,9 @@ python quantpits/scripts/init_workspace.py \
144157

145158
```bash
146159
source workspaces/MyWorkspace/run_env.sh
160+
161+
# For Windows
162+
. ./workspaces/Demo_Workspace/run_env.ps1
147163
```
148164

149165
### 3.4 工作区目录结构
@@ -162,7 +178,8 @@ workspaces/MyWorkspace/
162178
├── output/ # 输出文件
163179
├── archive/ # 历史归档
164180
├── mlruns/ # MLflow 追踪
165-
└── run_env.sh # 环境激活脚本
181+
├── run_env.ps1 # 环境激活脚本(Win)
182+
└── run_env.sh # 环境激活脚本(Linux)
166183
```
167184

168185
---

docs/en/70_WALKTHROUGH.md

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ mkdir -p ~/.qlib/qlib_data/cn_data
9393
# Download the latest version using wget
9494
wget https://github.com/chenditc/investment_data/releases/latest/download/qlib_bin.tar.gz
9595
# Extract to the data directory
96-
tar -xzf qlib_bin.tar.gz -C ~/.qlib/qlib_data/cn_data
96+
tar -zxvf qlib_bin.tar.gz -C ~/.qlib/qlib_data/cn_data --strip-components=1
9797
```
9898

9999
> See https://github.com/chenditc/investment_data for update frequency and data details.
@@ -106,6 +106,12 @@ If your data is stored elsewhere, configure it in your workspace's `run_env.sh`:
106106
export QLIB_DATA_DIR="/your/custom/path/cn_data"
107107
export QLIB_REGION="cn"
108108
```
109+
If using Windows PowerShell, configure it in your workspace's `run_env.ps1`:
110+
111+
```powershell
112+
$env:QLIB_DATA_DIR = "X:/your/custom/path/cn_data"
113+
$env:QLIB_REGION = "cn"
114+
```
109115

110116
---
111117

@@ -123,10 +129,18 @@ QuantPits uses a **Workspace** mechanism to fully isolate configurations and dat
123129
# cd QuantPits
124130

125131
source workspaces/Demo_Workspace/run_env.sh
132+
133+
# For Windows
134+
. ./workspaces/Demo_Workspace/run_env.ps1
126135
```
127136

128137
Output: `Workspace activated: .../Demo_Workspace` confirms successful activation.
129138

139+
**Note for Windows:** If this is your first time running a `.ps1` script, PowerShell may block execution. You need to run PowerShell as **Administrator** and execute:
140+
```powershell
141+
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
142+
```
143+
130144
### 3.2 Create a New Workspace
131145

132146
```bash
@@ -138,12 +152,15 @@ python quantpits/scripts/init_workspace.py \
138152
This will:
139153
- Clone all configuration files from `Demo_Workspace/config/`
140154
- Create empty `data/`, `output/`, `archive/`, and `mlruns/` directories
141-
- Generate a `run_env.sh` environment activation script
155+
- Generate a `run_env.sh` (Linux) and `run_env.ps1` (Windows) environment activation script
142156

143157
### 3.3 Activate the New Workspace
144158

145159
```bash
146160
source workspaces/MyWorkspace/run_env.sh
161+
162+
# For Windows
163+
. ./workspaces/MyWorkspace/run_env.ps1
147164
```
148165

149166
### 3.4 Workspace Directory Structure
@@ -162,7 +179,8 @@ workspaces/MyWorkspace/
162179
├── output/ # Output files
163180
├── archive/ # Historical archives
164181
├── mlruns/ # MLflow tracking
165-
└── run_env.sh # Environment activation script
182+
├── run_env.ps1 # Environment activation script (Win)
183+
└── run_env.sh # Environment activation script (Linux)
166184
```
167185

168186
---
@@ -403,7 +421,7 @@ The Post-Trade script processes settlement files exported from your broker, upda
403421

404422
1. Export settlement records (`.xlsx`) from your broker and place them in `data/`
405423
2. File naming convention: `YYYY-MM-DD-table.xlsx` (e.g., `2026-02-24-table.xlsx`)
406-
3. For non-trading days, use the empty template `emp-table.xlsx` (all empty cells)
424+
3. For non-trading days, use the empty template `emp-table.xlsx` (an empty Excel file)
407425

408426
### 9.2 Configure Cashflow (If Applicable)
409427

@@ -520,6 +538,9 @@ python quantpits/scripts/run_rolling_health_report.py
520538

521539
source workspaces/Demo_Workspace/run_env.sh
522540

541+
# For Windows
542+
. ./workspaces/Demo_Workspace/run_env.ps1
543+
523544
# ① Full training
524545
python quantpits/scripts/prod_train_predict.py
525546

@@ -536,10 +557,13 @@ python quantpits/scripts/ensemble_fusion.py --from-config-all
536557
cd QuantPits
537558
source workspaces/MyWorkspace/run_env.sh
538559

560+
# For Windows
561+
. ./workspaces/MyWorkspace/run_env.ps1
562+
539563
# ① Predict
540564
python quantpits/scripts/prod_predict_only.py --all-enabled
565+
```
541566

542-
# ② Fuse
543567
python quantpits/scripts/ensemble_fusion.py --from-config-all
544568

545569
# ③ Post-Trade (if live trading)

quantpits/scripts/init_workspace.py

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66
import argparse
77
import os
8+
import platform
89
import shutil
910
import stat
1011

@@ -63,24 +64,57 @@ def init_workspace(source, target):
6364
dir_path = os.path.join(target, d)
6465
print(f"Creating empty directory: {dir_path}")
6566
os.makedirs(dir_path)
66-
67+
# 3. Create activation script
68+
is_windows = platform.system() == "Windows"
69+
script_ext = ".ps1" if is_windows else ".sh"
70+
script_name = "run_env" + script_ext
71+
run_env_path = os.path.join(target, script_name)
72+
73+
with open(run_env_path, "w", encoding="utf-8") as f:
74+
if is_windows:
75+
f.write("# run this file to activate the workspace\n\n")
76+
f.write('$env:QLIB_WORKSPACE_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path\n')
77+
f.write("# Uncomment and modify to use a custom Qlib data directory:\n")
78+
f.write('# $env:QLIB_DATA_DIR = "D:\\data\\cn_data"\n')
79+
f.write('# $env:QLIB_REGION = "cn"\n\n')
80+
f.write('Write-Host "Workspace activated: $env:QLIB_WORKSPACE_DIR"\n')
81+
else:
82+
f.write("#!/bin/bash\n")
83+
f.write("# Source this file to activate the workspace\n")
84+
f.write('export QLIB_WORKSPACE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"\n')
85+
f.write("# Uncomment and modify to use a custom Qlib data directory:\n")
86+
f.write('# export QLIB_DATA_DIR="~/.qlib/qlib_data/cn_data"\n')
87+
f.write('# export QLIB_REGION="cn"\n')
88+
f.write('echo "Workspace activated: $QLIB_WORKSPACE_DIR"\n')
89+
90+
# Make it executable for Linux/macOS
91+
if not is_windows:
92+
os.chmod(run_env_path, os.stat(run_env_path).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
93+
94+
print("\nWorkspace initialization complete!")
95+
if is_windows:
96+
print(f"To use this workspace, run: .\\{script_name}")
97+
print(f"Or in PowerShell: $env:QLIB_WORKSPACE_DIR='{target}'; python ...")
98+
else:
99+
print(f"To use this workspace, run: source {run_env_path}")
100+
print(f"Or prepend commands with: QLIB_WORKSPACE_DIR={target} python ...")
67101
# 3. Create run_env.sh
68-
run_env_path = os.path.join(target, "run_env.sh")
69-
with open(run_env_path, "w") as f:
70-
f.write("#!/bin/bash\n")
71-
f.write("# Source this file to activate the workspace\n")
72-
f.write('export QLIB_WORKSPACE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"\n')
73-
f.write("# Uncomment and modify to use a custom Qlib data directory:\n")
74-
f.write('# export QLIB_DATA_DIR="~/.qlib/qlib_data/cn_data"\n')
75-
f.write('# export QLIB_REGION="cn"\n')
76-
f.write("echo \"Workspace activated: $QLIB_WORKSPACE_DIR\"\n")
102+
# run_env_path = os.path.join(target, "run_env.sh")
103+
# with open(run_env_path, "w") as f:
104+
# f.write("#!/bin/bash\n")
105+
# f.write("# Source this file to activate the workspace\n")
106+
# f.write('export QLIB_WORKSPACE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"\n')
107+
# f.write("# Uncomment and modify to use a custom Qlib data directory:\n")
108+
# f.write('# export QLIB_DATA_DIR="~/.qlib/qlib_data/cn_data"\n')
109+
# f.write('# export QLIB_REGION="cn"\n')
110+
# f.write("echo \"Workspace activated: $QLIB_WORKSPACE_DIR\"\n")
77111

78112
# Make it executable just in case, though it should be sourced
79-
os.chmod(run_env_path, os.stat(run_env_path).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
113+
# os.chmod(run_env_path, os.stat(run_env_path).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
80114

81-
print("\\nWorkspace initialization complete!")
82-
print(f"To use this workspace, run: source {run_env_path}")
83-
print(f"Or prepend commands with: QLIB_WORKSPACE_DIR={target} python ...")
115+
# print("\\nWorkspace initialization complete!")
116+
# print(f"To use this workspace, run: source {run_env_path}")
117+
# print(f"Or prepend commands with: QLIB_WORKSPACE_DIR={target} python ...")
84118

85119
def main():
86120
parser = argparse.ArgumentParser(description="Initialize a new Qlib Workspace")

tests/quantpits/scripts/test_init_workspace.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ def test_init_workspace_success(tmp_path):
1818

1919
target = tmp_path / "NewWorkspace"
2020

21-
init_workspace(str(source), str(target))
21+
# Mock Linux behavior to ensure run_env.sh is tested here
22+
with patch("platform.system", return_value="Linux"):
23+
init_workspace(str(source), str(target))
2224

2325
# Verify directory structure
2426
assert target.exists()
@@ -37,8 +39,9 @@ def test_init_workspace_success(tmp_path):
3739
assert run_env.exists()
3840
content = run_env.read_text()
3941
assert "QLIB_WORKSPACE_DIR" in content
40-
# Should have executable permission
41-
assert os.stat(run_env).st_mode & stat.S_IXUSR
42+
# Should have executable permission (only check on non-windows)
43+
if os.name != 'nt':
44+
assert os.stat(run_env).st_mode & stat.S_IXUSR
4245

4346

4447
def test_init_workspace_strategy_yaml_generated(tmp_path):
@@ -97,8 +100,49 @@ def test_init_workspace_no_source_config(tmp_path, capsys):
97100
assert "Warning" in captured.out
98101
assert (target / "config").exists()
99102

103+
104+
def test_init_workspace_windows_script(tmp_path):
105+
"""Should generate run_env.ps1 when running on Windows."""
106+
source = tmp_path / "Source"
107+
source.mkdir()
108+
(source / "config").mkdir()
109+
target = tmp_path / "NewWorkspacePS"
110+
111+
with patch("platform.system", return_value="Windows"):
112+
init_workspace(str(source), str(target))
113+
114+
run_env = target / "run_env.ps1"
115+
assert run_env.exists()
116+
content = run_env.read_text(encoding="utf-8")
117+
assert "$env:QLIB_WORKSPACE_DIR" in content
118+
assert "run_env.sh" not in [f.name for f in target.iterdir() if f.is_file() and f.suffix == ".sh"]
119+
120+
121+
def test_init_workspace_linux_script(tmp_path):
122+
"""Should generate run_env.sh when running on Linux."""
123+
source = tmp_path / "Source"
124+
source.mkdir()
125+
(source / "config").mkdir()
126+
target = tmp_path / "NewWorkspaceSH"
127+
128+
with patch("platform.system", return_value="Linux"):
129+
init_workspace(str(source), str(target))
130+
131+
run_env = target / "run_env.sh"
132+
assert run_env.exists()
133+
content = run_env.read_text()
134+
assert "export QLIB_WORKSPACE_DIR" in content
135+
# Should have executable permission (only check on non-windows)
136+
if os.name != 'nt':
137+
assert os.stat(run_env).st_mode & stat.S_IXUSR
138+
139+
100140
def test_main(tmp_path, monkeypatch):
101141
from quantpits.scripts import init_workspace as iw
142+
import platform
143+
is_windows = platform.system() == "Windows"
144+
script_name = "run_env.ps1" if is_windows else "run_env.sh"
145+
102146
source = tmp_path / "Source"
103147
source.mkdir()
104148
(source / "config").mkdir()
@@ -109,4 +153,4 @@ def test_main(tmp_path, monkeypatch):
109153
iw.main()
110154

111155
assert target.exists()
112-
assert (target / "run_env.sh").exists()
156+
assert (target / script_name).exists()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# run this file to activate the workspace
2+
3+
$env:QLIB_WORKSPACE_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
4+
# Uncomment and modify to use a custom Qlib data directory:
5+
$env:QLIB_DATA_DIR = "D:\data\cn_data"
6+
$env:QLIB_REGION = "cn"
7+
8+
Write-Host "Workspace activated: $env:QLIB_WORKSPACE_DIR"

0 commit comments

Comments
 (0)