auto-sync: 2026-03-28 12:07:55
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
# sanguo_quant_live CI/CD 自动化回测
|
||||
|
||||
## 📋 概述
|
||||
|
||||
这是 sanguo_quant_live 全自动化回测 CI/CD 流程,**全程零干预,一条命令搞定**。
|
||||
|
||||
---
|
||||
|
||||
## 👥 明确职责分工
|
||||
|
||||
| 角色 | 你是谁 | 你需要做什么 | 你永远不需要做什么 |
|
||||
|------|------|-------------|-----------------|
|
||||
| **各位将军** | 策略开发者 | 写策略 / 改策略 → 一条命令触发回测 → 看结果 | **永远不要碰 vnpy 框架**,永远不要执行 `sanguo_nas_ci_cd.sh` |
|
||||
| **姜维** | CI/CD 负责人 / vnpy 维护者 | 维护 vnpy 框架 / 修复 bug / 新增功能 → 触发完整重建 | 不需要写策略 |
|
||||
|
||||
> 💡 **记住:**
|
||||
> - 如果你是将军写策略,你永远只用 `./sync_and_redeploy.sh`
|
||||
> - 如果你是姜维维护框架,你才用 `./sanguo_nas_ci_cd.sh`
|
||||
|
||||
---
|
||||
|
||||
## 🚀 使用方法(给各位将军)
|
||||
|
||||
### 📝 你写完策略/改完策略,要回测了:
|
||||
|
||||
```bash
|
||||
# 记住!你只用这个命令!
|
||||
cd management/cicd
|
||||
./sync_and_redeploy.sh
|
||||
```
|
||||
|
||||
**全自动完成:**
|
||||
1. ✅ 同步你本地最新策略代码到 NAS
|
||||
2. ✅ 重启 Docker 容器
|
||||
3. ✅ 自动运行回测
|
||||
4. ✅ 自动保存结果
|
||||
5. ✅ 输出回测统计结果
|
||||
|
||||
**全程不用你管,等着看结果就行!** 🎉
|
||||
|
||||
---
|
||||
|
||||
## 🚀 使用方法(给姜维)
|
||||
|
||||
### 🔧 你修改了 vnpy 框架/修复了 bug/新增了功能,需要重建环境:
|
||||
|
||||
```bash
|
||||
# 只有姜维能用这个命令!
|
||||
cd management/cicd
|
||||
./sanguo_nas_ci_cd.sh
|
||||
```
|
||||
|
||||
**全自动完成:**
|
||||
1. ✅ 同步本地最新完整代码(包括 vnpy 框架)到 NAS
|
||||
2. ✅ 停止旧容器
|
||||
3. ✅ 完全重新构建 Docker 镜像(包含所有修改)
|
||||
4. ✅ 启动新容器
|
||||
5. ✅ 运行回测验证
|
||||
6. ✅ 保存结果
|
||||
|
||||
---
|
||||
|
||||
## 🛟 特殊情况:rsync 同步有权限问题
|
||||
|
||||
```bash
|
||||
cd management/cicd
|
||||
./sync_with_tar.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 回测结果在哪里
|
||||
|
||||
结果自动保存到**两个地方**:
|
||||
|
||||
1. **全局汇总**:
|
||||
- NAS: `/volume1/stock/sanguo_vnpy/backtest_results/`
|
||||
- 本地: `./backtest_results/`
|
||||
|
||||
2. **和策略放在一起(便于查看)**:
|
||||
- `./strategies/{your_strategy}/backtest_results/`
|
||||
- 结果和策略代码放在一起,找起来方便!
|
||||
|
||||
每个回测生成两个文件:
|
||||
- `{strategy}_{YYYYMMDD_HHMMSS}.txt` - 回测统计结果(年化收益、夏普比率、最大回撤、胜率等)
|
||||
- `{strategy}_{YYYYMMDD_HHMMSS}.png` - 资金曲线图
|
||||
|
||||
---
|
||||
|
||||
## 🔄 完整工作流
|
||||
|
||||
```
|
||||
1. 各位将军开发
|
||||
↓
|
||||
✍️ 在本地 sanguo_quant_live 写策略 / 改策略
|
||||
↓
|
||||
🚀 触发回测(记住只用这个):
|
||||
cd management/cicd
|
||||
./sync_and_redeploy.sh
|
||||
↓
|
||||
🔄 自动化执行
|
||||
✓ 同步最新策略到 NAS
|
||||
✓ 重启容器
|
||||
✓ 运行回测
|
||||
✓ 保存结果(全局一份 + 策略目录一份)
|
||||
↓
|
||||
👀 各位将军看结果
|
||||
去你的策略目录下 backtest_results/ 看结果
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 维护流程(给姜维)
|
||||
|
||||
```
|
||||
1. 发现 vnpy 框架有 bug / 需要新增功能
|
||||
↓
|
||||
🔧 在本地 sanguo_vnpy 修改代码
|
||||
↓
|
||||
🚀 触发完整重建:
|
||||
cd management/cicd
|
||||
./sanguo_nas_ci_cd.sh
|
||||
↓
|
||||
🔄 自动化执行
|
||||
✓ 同步完整代码到 NAS
|
||||
✓ 重建 Docker 镜像
|
||||
✓ 启动新容器
|
||||
↓
|
||||
✅ 完成,各位将军继续用
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 配置信息
|
||||
|
||||
- **NAS 地址:** `192.168.2.154`
|
||||
- **NAS 目标目录:** `/volume1/stock/sanguo_vnpy/`
|
||||
- **本地项目根目录:** `/Users/chufeng/.openclaw/sanguo_projects/sanguo_quant_live/`
|
||||
|
||||
---
|
||||
|
||||
## ✨ 方案特点
|
||||
|
||||
- 🤖 **全程零干预** - 你触发后全自动,不需要任何手动操作
|
||||
- ⚡ **增量更新** - 策略修改不用重建 Docker,更快出结果
|
||||
- 🔒 **职责隔离** - 将军只管策略,姜维只管框架,互不干扰
|
||||
- 📂 **结果好查找** - 结果同时保存到全局和策略目录,方便查看
|
||||
- 📊 **结果自动保存** - 统计结果 + 资金曲线图都自动保存
|
||||
- 🎯 **分工清晰** - 每个人只干自己擅长的,命令记一个就行
|
||||
|
||||
---
|
||||
|
||||
**版本:** 1.0
|
||||
**最后更新:** 2026-03-28
|
||||
**维护负责人:** 姜维
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# 配置定时自动回测
|
||||
# 安装:./auto_backtest_crontab.sh install
|
||||
# 查看:crontab -l
|
||||
# 卸载:./auto_backtest_crontab.sh uninstall
|
||||
# ============================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CI_CD_SCRIPT="$SCRIPT_DIR/sanguo_nas_ci_cd.sh"
|
||||
CRON_ENTRY="0 8 * * * $CI_CD_SCRIPT >> $SCRIPT_DIR/auto_backtest.log 2>&1"
|
||||
|
||||
install() {
|
||||
echo "安装定时自动回测..."
|
||||
echo ""
|
||||
|
||||
# 添加到 crontab
|
||||
(crontab -l 2>/dev/null; echo "$CRON_ENTRY") | crontab -
|
||||
|
||||
echo ""
|
||||
echo "✅ 安装完成!"
|
||||
echo "每天早上 8:00 自动运行一次全流程回测"
|
||||
echo "日志位置: $SCRIPT_DIR/auto_backtest.log"
|
||||
echo ""
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
echo "卸载定时自动回测..."
|
||||
echo ""
|
||||
|
||||
# 移除 crontab
|
||||
crontab -l 2>/dev/null | grep -v "sanguo_nas_ci_cd.sh" | crontab -
|
||||
|
||||
echo ""
|
||||
echo "✅ 卸载完成!"
|
||||
echo ""
|
||||
}
|
||||
|
||||
status() {
|
||||
echo "当前定时任务:"
|
||||
echo ""
|
||||
crontab -l | grep "sanguo"
|
||||
echo ""
|
||||
}
|
||||
|
||||
case "${1:-help}" in
|
||||
install)
|
||||
install
|
||||
;;
|
||||
uninstall)
|
||||
uninstall
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
help)
|
||||
echo "使用方法:"
|
||||
echo " $0 install - 安装每天 8:00 自动回测"
|
||||
echo " $0 uninstall - 卸载定时回测"
|
||||
echo " $0 status - 查看当前状态"
|
||||
echo ""
|
||||
;;
|
||||
esac
|
||||
Executable
+218
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
通用自动化回测脚本
|
||||
自动保存结果到:
|
||||
1. 全局目录: /volume1/stock/sanguo_vnpy/backtest_results/
|
||||
2. 策略目录: ./strategies/{your_strategy}/backtest_results/ 便于和策略放在一起查看
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# 添加路径
|
||||
sys.path.insert(0, '/volume1/stock/sanguo_vnpy')
|
||||
sys.path.insert(0, '/volume1/stock/sanguo_vnpy/strategies')
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from vnpy.trader.constant import Interval
|
||||
from vnpy_ctabacktester import BacktesterEngine
|
||||
|
||||
|
||||
def run_backtest(
|
||||
strategy_class,
|
||||
strategy_name: str,
|
||||
vt_symbol: str = "IF888.CFFEX",
|
||||
interval: Interval = Interval.DAILY,
|
||||
start_date: str = "20200101",
|
||||
end_date: str = "20251231",
|
||||
capital: int = 1000000,
|
||||
rate: float = 0.3 / 10000,
|
||||
slippage: float = 0.2,
|
||||
size: int = 300,
|
||||
pricetick: float = 0.2,
|
||||
strategy_config: dict = None,
|
||||
):
|
||||
"""运行单个策略回测"""
|
||||
|
||||
# 创建结果保存目录
|
||||
# 1. 全局目录
|
||||
GLOBAL_RESULT_DIR = Path('/volume1/stock/sanguo_vnpy/backtest_results')
|
||||
GLOBAL_RESULT_DIR.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
# 2. 策略目录(和策略放在一起便于查看)
|
||||
# 找到策略目录
|
||||
strategy_file = sys.modules[strategy_class.__module__].__file__
|
||||
strategy_dir = Path(strategy_file).parent
|
||||
STRATEGY_RESULT_DIR = strategy_dir / "backtest_results"
|
||||
STRATEGY_RESULT_DIR.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
print("=" * 60)
|
||||
print(f" 开始回测: {strategy_name}")
|
||||
print(f" 结果保存:")
|
||||
print(f" 全局: {GLOBAL_RESULT_DIR}")
|
||||
print(f" 策略: {STRATEGY_RESULT_DIR}")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# 打印配置
|
||||
print(f"📋 回测配置:")
|
||||
print(f" 标的: {vt_symbol}")
|
||||
print(f" 周期: {interval}")
|
||||
print(f" 时间: {start_date} - {end_date}")
|
||||
print(f" 初始资金: {capital:,}")
|
||||
print()
|
||||
|
||||
# 创建回测引擎
|
||||
print("🚀 创建回测引擎...")
|
||||
engine = BacktesterEngine()
|
||||
|
||||
# 添加策略
|
||||
print("🧩 添加策略...")
|
||||
if strategy_config is None:
|
||||
strategy_config = {}
|
||||
engine.add_strategy(strategy_class, strategy_config)
|
||||
|
||||
# 运行回测
|
||||
print("🔄 开始运行回测...")
|
||||
start_time = datetime.now()
|
||||
|
||||
engine.run_backtest(
|
||||
vt_symbol=vt_symbol,
|
||||
interval=interval,
|
||||
start=start_date,
|
||||
end=end_date,
|
||||
rate=rate,
|
||||
slippage=slippage,
|
||||
size=size,
|
||||
pricetick=pricetick,
|
||||
capital=capital
|
||||
)
|
||||
|
||||
end_time = datetime.now()
|
||||
duration = (end_time - start_time).total_seconds()
|
||||
|
||||
print(f"✅ 回测完成!耗时: {duration:.2f} 秒")
|
||||
print()
|
||||
|
||||
# 获取结果文本
|
||||
import io
|
||||
from contextlib import redirect_stdout
|
||||
|
||||
output = io.StringIO()
|
||||
with redirect_stdout(output):
|
||||
engine.show_results()
|
||||
result_text = output.getvalue()
|
||||
|
||||
# 保存结果到全局目录
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
global_result_file = GLOBAL_RESULT_DIR / f"{strategy_name}_{timestamp}.txt"
|
||||
|
||||
with open(global_result_file, 'w', encoding='utf-8') as f:
|
||||
f.write(f"{strategy_name} 回测结果\n")
|
||||
f.write("=" * 50 + "\n")
|
||||
f.write(f"时间: {timestamp}\n")
|
||||
f.write(f"标的: {vt_symbol}\n")
|
||||
f.write(f"周期: {interval}\n")
|
||||
f.write(f"时间范围: {start_date} - {end_date}\n")
|
||||
f.write(f"初始资金: {capital:,}\n")
|
||||
f.write("\n")
|
||||
f.write(result_text)
|
||||
|
||||
print(f"💾 全局结果已保存: {global_result_file}")
|
||||
|
||||
# 保存结果到策略目录
|
||||
strategy_result_file = STRATEGY_RESULT_DIR / f"{strategy_name}_{timestamp}.txt"
|
||||
with open(strategy_result_file, 'w', encoding='utf-8') as f:
|
||||
f.write(f"{strategy_name} 回测结果\n")
|
||||
f.write("=" * 50 + "\n")
|
||||
f.write(f"时间: {timestamp}\n")
|
||||
f.write(f"标的: {vt_symbol}\n")
|
||||
f.write(f"周期: {interval}\n")
|
||||
f.write(f"时间范围: {start_date} - {end_date}\n")
|
||||
f.write(f"初始资金: {capital:,}\n")
|
||||
f.write("\n")
|
||||
f.write(result_text)
|
||||
|
||||
print(f"📂 策略结果已保存: {strategy_result_file}")
|
||||
print()
|
||||
|
||||
# 保存图表
|
||||
print("📈 绘制资金曲线...")
|
||||
plt.figure(figsize=(12, 6))
|
||||
engine.plot_chart()
|
||||
|
||||
# 保存到全局目录
|
||||
global_chart_file = GLOBAL_RESULT_DIR / f"{strategy_name}_{timestamp}.png"
|
||||
plt.savefig(global_chart_file)
|
||||
print(f"📊 全局图表已保存: {global_chart_file}")
|
||||
|
||||
# 保存到策略目录
|
||||
strategy_chart_file = STRATEGY_RESULT_DIR / f"{strategy_name}_{timestamp}.png"
|
||||
plt.savefig(strategy_chart_file)
|
||||
print(f"📊 策略图表已保存: {strategy_chart_file}")
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print(f" 🎉 回测完成!")
|
||||
print(f" 📄 全局结果: {global_result_file}")
|
||||
print(f" 📂 策略结果: {strategy_result_file}")
|
||||
print("=" * 60)
|
||||
|
||||
return global_result_file, strategy_result_file
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
|
||||
parser = argparse.ArgumentParser(description='自动化运行策略回测')
|
||||
parser.add_argument('strategy', help='策略模块名,例如: strategies.guanyu_value_tech_strategy')
|
||||
parser.add_argument('--symbol', default='IF888.CFFEX', help='回测标的')
|
||||
parser.add_argument('--start', default='20200101', help='开始日期')
|
||||
parser.add_argument('--end', default='20251231', help='结束日期')
|
||||
parser.add_argument('--capital', type=int, default=1000000, help='初始资金')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 导入策略模块
|
||||
import importlib
|
||||
module = importlib.import_module(args.strategy)
|
||||
|
||||
# 找到策略类(假设第一个类就是策略类)
|
||||
strategy_class = None
|
||||
for name, obj in module.__dict__.items():
|
||||
if isinstance(obj, type) and 'Strategy' in name:
|
||||
strategy_class = obj
|
||||
break
|
||||
|
||||
if strategy_class is None:
|
||||
print(f"❌ 在模块 {args.strategy} 中没找到策略类")
|
||||
sys.exit(1)
|
||||
|
||||
strategy_name = strategy_class.__name__
|
||||
|
||||
# 获取策略配置(如果有)
|
||||
strategy_config = {}
|
||||
if hasattr(module, 'STRATEGY_CONFIG'):
|
||||
strategy_config = module.STRATEGY_CONFIG
|
||||
|
||||
# 运行回测
|
||||
run_backtest(
|
||||
strategy_class=strategy_class,
|
||||
strategy_name=strategy_name,
|
||||
vt_symbol=args.symbol,
|
||||
start_date=args.start,
|
||||
end_date=args.end,
|
||||
capital=args.capital,
|
||||
strategy_config=strategy_config,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+209
@@ -0,0 +1,209 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# sanguo_vnpy 端到端全自动化 CI/CD 回测
|
||||
# 作者:姜维 伯约
|
||||
# 日期:2026年3月28日
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
|
||||
# 配置信息
|
||||
NAS_IP="192.168.2.154"
|
||||
NAS_USER="admin"
|
||||
SANGUO_PROJECT_DIR="/Users/chufeng/.openclaw/sanguo_projects/sanguo_quant_live"
|
||||
NAS_TARGET_DIR="/volume1/stock/sanguo_vnpy"
|
||||
DOCKER_CONTAINER_NAME="sanguo_vnpy"
|
||||
|
||||
# 说明:
|
||||
# 这个方案会同步整个 sanguo_quant_live 项目,包括:
|
||||
# - strategies/ 所有策略
|
||||
# - sanguo_vnpy/ 框架代码(包括修改后的vnpy)
|
||||
# - jiangwei-platform/ 三国量化实战项目
|
||||
# - pangtong-value/ 价值投资研究
|
||||
# - 所有其他模块
|
||||
|
||||
# 颜色
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 步骤 1: 拉取最新代码
|
||||
pull_latest_code() {
|
||||
log_info "步骤 1/6: 拉取最新代码"
|
||||
|
||||
cd "$SANGUO_PROJECT_DIR"
|
||||
|
||||
if git pull origin main; then
|
||||
log_info "✅ 代码已更新到最新版本"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ 拉取代码失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 步骤 2: 同步所有文件到 NAS
|
||||
sync_to_nas() {
|
||||
log_info "步骤 2/6: 同步所有文件到 NAS"
|
||||
|
||||
# 确保目标目录存在
|
||||
ssh "$NAS_USER@$NAS_IP" "mkdir -p $NAS_TARGET_DIR"
|
||||
|
||||
# 使用 rsync 同步所有文件(包括策略和框架代码)
|
||||
# 会同步整个 sanguo_quant_live 项目,包括:
|
||||
# - strategies/ 所有策略
|
||||
# - sanguo_vnpy/ 框架代码(包括您修改后的 vnpy)
|
||||
# - jiangwei-platform/ 三国量化实战
|
||||
# - pangtong-value/ 价值投资研究
|
||||
# - 所有其他模块
|
||||
rsync -av --delete \
|
||||
"$SANGUO_PROJECT_DIR"/ \
|
||||
"$NAS_USER@$NAS_IP:$NAS_TARGET_DIR"/ \
|
||||
--exclude='.git' \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='*.log' \
|
||||
--exclude='*.pyc' \
|
||||
--exclude='.ipynb_checkpoints' \
|
||||
--exclude='backtest_results/*.png' \
|
||||
--exclude='.DS_Store'
|
||||
|
||||
log_info "✅ 文件同步完成"
|
||||
}
|
||||
|
||||
# 步骤 3: 重建 Docker 容器
|
||||
rebuild_docker() {
|
||||
log_info "步骤 3/6: 重建 Docker 容器"
|
||||
|
||||
ssh "$NAS_USER@$NAS_IP" << 'EOF'
|
||||
export PATH=$PATH:/var/packages/Docker/target/usr/bin
|
||||
cd /volume1/stock/sanguo_vnpy/docker
|
||||
|
||||
# 停止并删除旧容器
|
||||
if docker ps -a | grep -q sanguo_vnpy; then
|
||||
docker stop sanguo_vnpy
|
||||
docker rm sanguo_vnpy
|
||||
fi
|
||||
|
||||
# 构建新镜像
|
||||
docker-compose build --no-cache
|
||||
|
||||
# 启动新容器
|
||||
docker-compose up -d
|
||||
|
||||
# 等待容器启动
|
||||
sleep 10
|
||||
|
||||
# 检查容器状态
|
||||
if docker ps | grep -q sanguo_vnpy; then
|
||||
echo "✅ 容器启动成功"
|
||||
else
|
||||
echo "❌ 容器启动失败"
|
||||
exit 1
|
||||
fi
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "✅ Docker 容器重建完成"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ Docker 容器重建失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 步骤 4: 运行自动化回测
|
||||
run_backtest() {
|
||||
log_info "步骤 4/6: 运行自动化回测"
|
||||
|
||||
ssh "$NAS_USER@$NAS_IP" << 'EOF'
|
||||
cd /volume1/stock/sanguo_vnpy/scripts
|
||||
|
||||
# 运行全自动化回测
|
||||
./run_backtest_auto.sh
|
||||
EOF
|
||||
|
||||
if [ $? -eq -ne 0 ]; then
|
||||
log_warn "回测运行中,请等待完成..."
|
||||
fi
|
||||
|
||||
log_info "✅ 回测已启动"
|
||||
}
|
||||
|
||||
# 步骤 5: 获取结果
|
||||
get_results() {
|
||||
log_info "步骤 5/6: 获取回测结果"
|
||||
|
||||
# 创建本地结果目录
|
||||
mkdir -p "./backtest_results"
|
||||
|
||||
# 下载最新结果
|
||||
rsync -av --delete \
|
||||
"$NAS_USER@$NAS_IP:$NAS_TARGET_DIR/backtest_results/"* \
|
||||
"./backtest_results"/
|
||||
|
||||
log_info "✅ 结果已下载到本地: ./backtest_results/"
|
||||
|
||||
# 显示最新结果
|
||||
LATEST_RESULT=$(ls -t ./backtest_results/*.txt | head -1)
|
||||
if [ -f "$LATEST_RESULT" ]; then
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " 📊 最新回测结果"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
cat "$LATEST_RESULT"
|
||||
echo ""
|
||||
echo "============================================"
|
||||
fi
|
||||
}
|
||||
|
||||
# 步骤 6: 完成通知
|
||||
finish() {
|
||||
log_info "步骤 6/6: 完成!"
|
||||
|
||||
echo ""
|
||||
echo "╔═════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ 🎉 端到端自动化回测完成! ║"
|
||||
echo "╚═════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "📍 结果位置:"
|
||||
echo " NAS: $NAS_TARGET_DIR/backtest_results/"
|
||||
echo " 本地: ./backtest_results/"
|
||||
echo ""
|
||||
echo "🚀 下次更新代码后,直接运行 ./sanguo_nas_ci_cd.sh 就能全自动完成!"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo "============================================"
|
||||
echo " sanguo_vnpy 端到端全自动化回测"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
pull_latest_code && \
|
||||
sync_to_nas && \
|
||||
rebuild_docker && \
|
||||
run_backtest && \
|
||||
get_results && \
|
||||
finish
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 运行
|
||||
main
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# 增量同步代码到 NAS 并重新部署
|
||||
# 当修复 bug 后,运行这个脚本自动同步并重新部署
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
|
||||
# 配置信息
|
||||
NAS_IP="192.168.2.154"
|
||||
NAS_USER="admin"
|
||||
LOCAL_PROJECT="/Users/chufeng/.openclaw/sanguo_projects/sanguo_quant_live"
|
||||
NAS_TARGET="/volume1/stock/sanguo_vnpy"
|
||||
|
||||
# 颜色
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
echo "============================================"
|
||||
echo " 增量同步 + 自动重新部署"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
# 拉取最新代码
|
||||
log_info "1/4: 拉取最新代码"
|
||||
cd "$LOCAL_PROJECT"
|
||||
git pull origin main
|
||||
log_info "✅ 代码已更新"
|
||||
echo ""
|
||||
|
||||
# 增量同步到 NAS
|
||||
log_info "2/4: 增量同步到 NAS"
|
||||
# 同步整个项目,包括策略和框架代码
|
||||
# 任何文件修改(包括 vnpy 框架代码)都会同步到 NAS
|
||||
rsync -av --delete \
|
||||
"$LOCAL_PROJECT"/ \
|
||||
"$NAS_USER@$NAS_IP:$NAS_TARGET"/ \
|
||||
--exclude='.git' \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='*.log' \
|
||||
--exclude='*.pyc' \
|
||||
--exclude='.ipynb_checkpoints' \
|
||||
--exclude='backtest_results/*.png' \
|
||||
--exclude='.DS_Store'
|
||||
|
||||
log_info "✅ 同步完成"
|
||||
echo ""
|
||||
|
||||
# 重启 Docker 容器
|
||||
log_info "3/4: 重启 Docker 容器"
|
||||
ssh "$NAS_USER@$NAS_IP" << 'EOF'
|
||||
cd /volume1/stock/sanguo_vnpy
|
||||
|
||||
# 添加 docker 到 PATH
|
||||
export PATH=$PATH:/var/packages/Docker/target/usr/bin
|
||||
|
||||
# 停止旧容器
|
||||
if docker ps -q -f name=sanguo_vnpy | grep -q .; then
|
||||
docker stop sanguo_vnpy
|
||||
fi
|
||||
|
||||
# 重新构建启动
|
||||
/var/packages/Docker/target/usr/bin/docker-compose up -d --build
|
||||
|
||||
# 等待启动
|
||||
sleep 10
|
||||
|
||||
# 检查状态
|
||||
if docker ps -q -f name=sanguo_vnpy | grep -q .; then
|
||||
echo "✅ 容器重启成功"
|
||||
else
|
||||
echo "❌ 容器启动失败"
|
||||
exit 1
|
||||
fi
|
||||
EOF
|
||||
|
||||
log_info "✅ 容器重启完成"
|
||||
echo ""
|
||||
|
||||
# 运行回测
|
||||
log_info "4/4: 启动自动化回测"
|
||||
ssh "$NAS_USER@$NAS_IP" "cd /volume1/stock/sanguo_vnpy/scripts && ./run_backtest_auto.sh"
|
||||
|
||||
echo ""
|
||||
log_info "🎉 完成!代码已同步,容器已重启,回测已启动!"
|
||||
echo ""
|
||||
Executable
+122
@@ -0,0 +1,122 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# 同步策略 + 全自动回测,不需要重启 Docker!
|
||||
# 使用方法:
|
||||
# ./sync_and_run_backtest.sh --strategy strategies.pangtong-value.strategy_A
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
|
||||
# 配置信息
|
||||
NAS_IP="192.168.2.154"
|
||||
NAS_USER="admin"
|
||||
LOCAL_PROJECT="/Users/chufeng/.openclaw/sanguo_projects/sanguo_quant_live"
|
||||
NAS_TARGET="/volume1/stock/sanguo_vnpy"
|
||||
DOCKER_CONTAINER="sanguo_vnpy"
|
||||
|
||||
# 颜色
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 解析参数
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--strategy)
|
||||
STRATEGY="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 检查参数
|
||||
if [[ -z "$STRATEGY" ]]; then
|
||||
log_error "必须指定 --strategy 参数"
|
||||
echo ""
|
||||
echo "使用方法:"
|
||||
echo " $0 --strategy strategies.pangtong-value.strategy_A"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 提取策略路径(用于同步)
|
||||
# strategies.pangtong-value.strategy_A → strategies/pangtong-value
|
||||
STRATEGY_PATH=$(echo "$STRATEGY" | tr '.' '/')
|
||||
STRATEGY_PATH=$(dirname "$STRATEGY_PATH")
|
||||
|
||||
echo "============================================"
|
||||
echo " 同步策略 + 全自动回测(无需重启 Docker)"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
log_info "策略: $STRATEGY"
|
||||
log_info "策略路径: $STRATEGY_PATH"
|
||||
echo ""
|
||||
|
||||
# 第一步:同步策略代码到 NAS
|
||||
log_info "1/3: 同步策略代码到 NAS"
|
||||
rsync -av --delete \
|
||||
"$LOCAL_PROJECT/$STRATEGY_PATH"/ \
|
||||
"$NAS_USER@$NAS_IP:$NAS_TARGET/$STRATEGY_PATH"/ \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='*.pyc' \
|
||||
--exclude='.ipynb_checkpoints' \
|
||||
--exclude='.DS_Store'
|
||||
|
||||
log_info "✅ 同步完成"
|
||||
echo ""
|
||||
|
||||
# 第二步:在容器中运行回测
|
||||
log_info "2/3: 在运行的容器中执行回测"
|
||||
echo ""
|
||||
|
||||
ssh "$NAS_USER@$NAS_IP" "export PATH=\$PATH:/var/packages/Docker/target/usr/bin && docker exec $DOCKER_CONTAINER python /app/scripts/run_backtest_auto.py $STRATEGY"
|
||||
|
||||
log_info "✅ 回测执行完成"
|
||||
echo ""
|
||||
|
||||
# 第三步:同步回测结果回本地
|
||||
log_info "3/3: 同步回测结果回本地"
|
||||
echo ""
|
||||
|
||||
# 结果目录
|
||||
RESULT_PATH="$STRATEGY_PATH/backtest_results"
|
||||
mkdir -p "$LOCAL_PROJECT/$RESULT_PATH"
|
||||
|
||||
rsync -av \
|
||||
"$NAS_USER@$NAS_IP:$NAS_TARGET/$RESULT_PATH"/ \
|
||||
"$LOCAL_PROJECT/$RESULT_PATH"/ \
|
||||
--exclude='__pycache__'
|
||||
|
||||
log_info "✅ 结果同步完成"
|
||||
echo ""
|
||||
|
||||
echo "============================================"
|
||||
echo " 🎉 回测完成!"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
echo "📄 结果位置:"
|
||||
echo " NAS: $NAS_TARGET/$RESULT_PATH/"
|
||||
echo " 本地: $LOCAL_PROJECT/$RESULT_PATH/"
|
||||
echo ""
|
||||
echo "📊 直接查看结果:"
|
||||
echo " cat $LOCAL_PROJECT/$RESULT_PATH/*.txt"
|
||||
echo " open $LOCAL_PROJECT/$RESULT_PATH/*.png"
|
||||
echo ""
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# 只同步策略,不需要重启 Docker
|
||||
# vnpy 支持动态加载策略,所以同步完成后直接就能在 UI 里加载新策略
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
|
||||
# 配置信息
|
||||
NAS_IP="192.168.2.154"
|
||||
NAS_USER="admin"
|
||||
LOCAL_PROJECT="/Users/chufeng/.openclaw/sanguo_projects/sanguo_quant_live"
|
||||
NAS_TARGET="/volume1/stock/sanguo_vnpy"
|
||||
|
||||
# 颜色
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
echo "============================================"
|
||||
echo " 同步策略到 NAS(不重启 Docker)"
|
||||
echo " vnpy 支持动态加载,同步完直接在 UI 加载新策略"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
# 检查参数
|
||||
if [ $# -eq 0 ]; then
|
||||
log_error "请指定要同步的策略目录"
|
||||
echo ""
|
||||
echo "使用方法:"
|
||||
echo " ./sync_strategy_only.sh strategies/pangtong-value/strategy_A"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STRATEGY_PATH="$1"
|
||||
|
||||
log_info "1/2: 同步策略 $STRATEGY_PATH 到 NAS"
|
||||
rsync -av --delete \
|
||||
"$LOCAL_PROJECT/$STRATEGY_PATH"/ \
|
||||
"$NAS_USER@$NAS_IP:$NAS_TARGET/$STRATEGY_PATH"/ \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='*.pyc' \
|
||||
--exclude='.ipynb_checkpoints' \
|
||||
--exclude='.DS_Store'
|
||||
|
||||
log_info "✅ 同步完成"
|
||||
echo ""
|
||||
log_info "2/2: 完成!"
|
||||
echo ""
|
||||
echo "🎯 下一步:"
|
||||
echo " 1. 在 vnpy UI 里刷新策略"
|
||||
echo " 2. 加载你的策略 $STRATEGY_PATH"
|
||||
echo " 3. 运行回测"
|
||||
echo " 4. 结果自动保存到 $STRATEGY_PATH/backtest_results/"
|
||||
echo ""
|
||||
echo "✅ 完成!不需要重启 Docker!直接用就行!"
|
||||
echo ""
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# 使用 tar 打包方式同步文件到 NAS
|
||||
# 绕过 rsync 权限问题
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
|
||||
# 配置信息
|
||||
NAS_IP="192.168.2.154"
|
||||
NAS_USER="admin"
|
||||
LOCAL_PROJECT="/Users/chufeng/.openclaw/sanguo_projects/sanguo_quant_live"
|
||||
NAS_TARGET="/volume1/stock/sanguo_vnpy"
|
||||
|
||||
# 颜色
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
echo "============================================"
|
||||
echo " 使用 tar 打包方式同步文件到 NAS"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
cd "$LOCAL_PROJECT"
|
||||
|
||||
log_info "1/3: 打包本地项目"
|
||||
# 打包排除一些文件
|
||||
tar -czf /tmp/sanguo_quant_live.tar.gz \
|
||||
--exclude='.git' \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='*.log' \
|
||||
--exclude='*.pyc' \
|
||||
--exclude='.ipynb_checkpoints' \
|
||||
--exclude='backtest_results/*.png' \
|
||||
--exclude='.DS_Store' \
|
||||
.
|
||||
|
||||
log_info "✅ 打包完成"
|
||||
echo ""
|
||||
|
||||
log_info "2/3: 上传并解压到 NAS"
|
||||
# 上传并解压
|
||||
cat /tmp/sanguo_quant_live.tar.gz | ssh "$NAS_USER@$NAS_IP" "cd '$NAS_TARGET' && tar -xzf - && echo '✅ 解压完成'"
|
||||
|
||||
log_info "✅ 同步完成"
|
||||
echo ""
|
||||
|
||||
log_info "3/3: 清理本地临时文件"
|
||||
rm /tmp/sanguo_quant_live.tar.gz
|
||||
log_info "✅ 清理完成"
|
||||
echo ""
|
||||
|
||||
log_info "🎉 文件同步完成!"
|
||||
echo ""
|
||||
@@ -1 +1 @@
|
||||
35771
|
||||
5279
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
vnpy>=4.0.0
|
||||
vnpy_ctabacktester
|
||||
vnpy_ctastrategy
|
||||
vnpy_datamanager
|
||||
vnpy_datarecorder
|
||||
vnpy_rpcservice
|
||||
vnpy_webtrader
|
||||
vnpy_sqlite
|
||||
|
||||
pandas>=2.0.0
|
||||
numpy>=1.24.0
|
||||
scipy>=1.10.0
|
||||
|
||||
matplotlib>=3.7.0
|
||||
seaborn>=0.12.0
|
||||
plotly>=5.14.0
|
||||
|
||||
scikit-learn>=1.3.0
|
||||
lightgbm>=4.0.0
|
||||
xgboost>=2.0.0
|
||||
|
||||
TA-Lib>=0.4.28
|
||||
|
||||
python-dotenv>=1.0.0
|
||||
requests>=2.31.0
|
||||
aiohttp>=3.8.0
|
||||
websockets>=11.0.0
|
||||
pytest>=7.4.0
|
||||
Executable
+218
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
通用自动化回测脚本
|
||||
自动保存结果到:
|
||||
1. 全局目录: /volume1/stock/sanguo_vnpy/backtest_results/
|
||||
2. 策略目录: ./strategies/{your_strategy}/backtest_results/ 便于和策略放在一起查看
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# 添加路径
|
||||
sys.path.insert(0, '/volume1/stock/sanguo_vnpy')
|
||||
sys.path.insert(0, '/volume1/stock/sanguo_vnpy/strategies')
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from vnpy.trader.constant import Interval
|
||||
from vnpy_ctabacktester import BacktesterEngine
|
||||
|
||||
|
||||
def run_backtest(
|
||||
strategy_class,
|
||||
strategy_name: str,
|
||||
vt_symbol: str = "IF888.CFFEX",
|
||||
interval: Interval = Interval.DAILY,
|
||||
start_date: str = "20200101",
|
||||
end_date: str = "20251231",
|
||||
capital: int = 1000000,
|
||||
rate: float = 0.3 / 10000,
|
||||
slippage: float = 0.2,
|
||||
size: int = 300,
|
||||
pricetick: float = 0.2,
|
||||
strategy_config: dict = None,
|
||||
):
|
||||
"""运行单个策略回测"""
|
||||
|
||||
# 创建结果保存目录
|
||||
# 1. 全局目录
|
||||
GLOBAL_RESULT_DIR = Path('/volume1/stock/sanguo_vnpy/backtest_results')
|
||||
GLOBAL_RESULT_DIR.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
# 2. 策略目录(和策略放在一起便于查看)
|
||||
# 找到策略目录
|
||||
strategy_file = sys.modules[strategy_class.__module__].__file__
|
||||
strategy_dir = Path(strategy_file).parent
|
||||
STRATEGY_RESULT_DIR = strategy_dir / "backtest_results"
|
||||
STRATEGY_RESULT_DIR.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
print("=" * 60)
|
||||
print(f" 开始回测: {strategy_name}")
|
||||
print(f" 结果保存:")
|
||||
print(f" 全局: {GLOBAL_RESULT_DIR}")
|
||||
print(f" 策略: {STRATEGY_RESULT_DIR}")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# 打印配置
|
||||
print(f"📋 回测配置:")
|
||||
print(f" 标的: {vt_symbol}")
|
||||
print(f" 周期: {interval}")
|
||||
print(f" 时间: {start_date} - {end_date}")
|
||||
print(f" 初始资金: {capital:,}")
|
||||
print()
|
||||
|
||||
# 创建回测引擎
|
||||
print("🚀 创建回测引擎...")
|
||||
engine = BacktesterEngine()
|
||||
|
||||
# 添加策略
|
||||
print("🧩 添加策略...")
|
||||
if strategy_config is None:
|
||||
strategy_config = {}
|
||||
engine.add_strategy(strategy_class, strategy_config)
|
||||
|
||||
# 运行回测
|
||||
print("🔄 开始运行回测...")
|
||||
start_time = datetime.now()
|
||||
|
||||
engine.run_backtest(
|
||||
vt_symbol=vt_symbol,
|
||||
interval=interval,
|
||||
start=start_date,
|
||||
end=end_date,
|
||||
rate=rate,
|
||||
slippage=slippage,
|
||||
size=size,
|
||||
pricetick=pricetick,
|
||||
capital=capital
|
||||
)
|
||||
|
||||
end_time = datetime.now()
|
||||
duration = (end_time - start_time).total_seconds()
|
||||
|
||||
print(f"✅ 回测完成!耗时: {duration:.2f} 秒")
|
||||
print()
|
||||
|
||||
# 获取结果文本
|
||||
import io
|
||||
from contextlib import redirect_stdout
|
||||
|
||||
output = io.StringIO()
|
||||
with redirect_stdout(output):
|
||||
engine.show_results()
|
||||
result_text = output.getvalue()
|
||||
|
||||
# 保存结果到全局目录
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
global_result_file = GLOBAL_RESULT_DIR / f"{strategy_name}_{timestamp}.txt"
|
||||
|
||||
with open(global_result_file, 'w', encoding='utf-8') as f:
|
||||
f.write(f"{strategy_name} 回测结果\n")
|
||||
f.write("=" * 50 + "\n")
|
||||
f.write(f"时间: {timestamp}\n")
|
||||
f.write(f"标的: {vt_symbol}\n")
|
||||
f.write(f"周期: {interval}\n")
|
||||
f.write(f"时间范围: {start_date} - {end_date}\n")
|
||||
f.write(f"初始资金: {capital:,}\n")
|
||||
f.write("\n")
|
||||
f.write(result_text)
|
||||
|
||||
print(f"💾 全局结果已保存: {global_result_file}")
|
||||
|
||||
# 保存结果到策略目录
|
||||
strategy_result_file = STRATEGY_RESULT_DIR / f"{strategy_name}_{timestamp}.txt"
|
||||
with open(strategy_result_file, 'w', encoding='utf-8') as f:
|
||||
f.write(f"{strategy_name} 回测结果\n")
|
||||
f.write("=" * 50 + "\n")
|
||||
f.write(f"时间: {timestamp}\n")
|
||||
f.write(f"标的: {vt_symbol}\n")
|
||||
f.write(f"周期: {interval}\n")
|
||||
f.write(f"时间范围: {start_date} - {end_date}\n")
|
||||
f.write(f"初始资金: {capital:,}\n")
|
||||
f.write("\n")
|
||||
f.write(result_text)
|
||||
|
||||
print(f"📂 策略结果已保存: {strategy_result_file}")
|
||||
print()
|
||||
|
||||
# 保存图表
|
||||
print("📈 绘制资金曲线...")
|
||||
plt.figure(figsize=(12, 6))
|
||||
engine.plot_chart()
|
||||
|
||||
# 保存到全局目录
|
||||
global_chart_file = GLOBAL_RESULT_DIR / f"{strategy_name}_{timestamp}.png"
|
||||
plt.savefig(global_chart_file)
|
||||
print(f"📊 全局图表已保存: {global_chart_file}")
|
||||
|
||||
# 保存到策略目录
|
||||
strategy_chart_file = STRATEGY_RESULT_DIR / f"{strategy_name}_{timestamp}.png"
|
||||
plt.savefig(strategy_chart_file)
|
||||
print(f"📊 策略图表已保存: {strategy_chart_file}")
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print(f" 🎉 回测完成!")
|
||||
print(f" 📄 全局结果: {global_result_file}")
|
||||
print(f" 📂 策略结果: {strategy_result_file}")
|
||||
print("=" * 60)
|
||||
|
||||
return global_result_file, strategy_result_file
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
|
||||
parser = argparse.ArgumentParser(description='自动化运行策略回测')
|
||||
parser.add_argument('strategy', help='策略模块名,例如: strategies.guanyu_value_tech_strategy')
|
||||
parser.add_argument('--symbol', default='IF888.CFFEX', help='回测标的')
|
||||
parser.add_argument('--start', default='20200101', help='开始日期')
|
||||
parser.add_argument('--end', default='20251231', help='结束日期')
|
||||
parser.add_argument('--capital', type=int, default=1000000, help='初始资金')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 导入策略模块
|
||||
import importlib
|
||||
module = importlib.import_module(args.strategy)
|
||||
|
||||
# 找到策略类(假设第一个类就是策略类)
|
||||
strategy_class = None
|
||||
for name, obj in module.__dict__.items():
|
||||
if isinstance(obj, type) and 'Strategy' in name:
|
||||
strategy_class = obj
|
||||
break
|
||||
|
||||
if strategy_class is None:
|
||||
print(f"❌ 在模块 {args.strategy} 中没找到策略类")
|
||||
sys.exit(1)
|
||||
|
||||
strategy_name = strategy_class.__name__
|
||||
|
||||
# 获取策略配置(如果有)
|
||||
strategy_config = {}
|
||||
if hasattr(module, 'STRATEGY_CONFIG'):
|
||||
strategy_config = module.STRATEGY_CONFIG
|
||||
|
||||
# 运行回测
|
||||
run_backtest(
|
||||
strategy_class=strategy_class,
|
||||
strategy_name=strategy_name,
|
||||
vt_symbol=args.symbol,
|
||||
start_date=args.start,
|
||||
end_date=args.end,
|
||||
capital=args.capital,
|
||||
strategy_config=strategy_config,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
# 创建软链接脚本
|
||||
# 将本地data目录中的文件链接到NAS
|
||||
|
||||
SOURCE_ROOT="/Users/chufeng/.openclaw/sanguo_projects/sanguo_quant_live/zhaoyun-data/data/"
|
||||
TARGET_ROOT="/Users/chufeng/nas/stock-data/sanguo_quant_live/zhaoyun-data/data/"
|
||||
|
||||
echo "🚀 开始创建软链接..."
|
||||
echo " 源目录: $SOURCE_ROOT"
|
||||
echo " 目标目录: $TARGET_ROOT"
|
||||
|
||||
# 创建目录结构
|
||||
find "$SOURCE_ROOT" -type d | while read dir; do
|
||||
target_dir="$TARGET_ROOT$dir"
|
||||
mkdir -p "$target_dir"
|
||||
echo " 创建目录: $target_dir"
|
||||
done
|
||||
|
||||
# 创建文件软链接
|
||||
count=0
|
||||
find "$SOURCE_ROOT" -type f | while read file; do
|
||||
target_file="$TARGET_ROOT$file"
|
||||
target_dir=$(dirname "$target_file")
|
||||
mkdir -p "$target_dir"
|
||||
rm -f "$target_file"
|
||||
ln -s "$file" "$target_file"
|
||||
count=$((count+1))
|
||||
if [ $((count % 100)) -eq 0 ]; then
|
||||
echo " 已创建 $count 个软链接..."
|
||||
fi
|
||||
done
|
||||
|
||||
echo "✅ 软链接创建完成!"
|
||||
echo " 总计: $count 个软链接"
|
||||
echo
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user