#!/bin/bash # ============================================ # sanguo_vnpy NAS 全自动部署脚本 # 作者:姜维 伯约 # 日期:2026年3月27日 # ============================================ set -e # 配置信息 NAS_IP="192.168.2.154" NAS_USER="cfdaily" NAS_PASS="Ccf7561523" NAS_SHARE="stock" MOUNT_POINT="/Users/chufeng/nas/stock" WORKSPACE="/Users/chufeng/.openclaw/workspace-jiangwei" SANGUO_PROJECTS="/Users/chufeng/.openclaw/sanguo_projects" # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' 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" } log_step() { echo "" echo -e "${BLUE}============================================${NC}" echo -e "${BLUE} $1${NC}" echo -e "${BLUE}============================================${NC}" } print_header() { echo "" echo "╔═══════════════════════════════════════════════════════════╗" echo "║ sanguo_vnpy NAS 全自动部署方案 ║" echo "╚═══════════════════════════════════════════════════════════╝" echo "" } # 检查 NAS 挂载 check_nas_mount() { log_step "步骤 1: 检查 NAS 挂载状态" if [ ! -d "$MOUNT_POINT" ]; then log_warn "挂载点不存在,创建中..." mkdir -p "$MOUNT_POINT" fi if mount | grep -q "$MOUNT_POINT"; then log_info "✅ NAS 已挂载: $MOUNT_POINT" return 0 else log_info "正在挂载 NAS..." # 尝试挂载 NAS_URL="smb://${NAS_USER}:${NAS_PASS}@${NAS_IP}/${NAS_SHARE}" if /sbin/mount_smbfs "$NAS_URL" "$MOUNT_POINT"; then log_info "✅ NAS 挂载成功" return 0 else log_error "❌ NAS 挂载失败" log_info "请先运行 NAS 挂载脚本: ./nas_auto_deploy.sh" return 1 fi fi } # 创建 NAS 目录结构 create_nas_directories() { log_step "步骤 2: 创建 NAS 目录结构" cd "$MOUNT_POINT" || exit 1 log_info "创建基础目录结构..." # 创建必要的基础目录(sanguo_quant_live 会提供大部分结构) mkdir -p sanguo_vnpy/config mkdir -p sanguo_vnpy/data/A股数据/日线数据 mkdir -p sanguo_vnpy/data/A股数据/分钟线数据 mkdir -p sanguo_vnpy/data/A股数据/财务数据 mkdir -p sanguo_vnpy/data/回测结果/策略回测 mkdir -p sanguo_vnpy/data/回测结果/性能报告 mkdir -p sanguo_vnpy/notebooks mkdir -p sanguo_vnpy/projects/sanguo_vnpy_framework mkdir -p sanguo_vnpy/research/jq_essence_articles mkdir -p sanguo_vnpy/research/other mkdir -p sanguo_vnpy/logs mkdir -p sanguo_vnpy/tests mkdir -p sanguo_vnpy/scripts mkdir -p sanguo_vnpy/docker/config mkdir -p sanguo_vnpy/docker/notebooks mkdir -p sanguo_vnpy/docker/strategies mkdir -p sanguo_vnpy/docker/logs mkdir -p sanguo_vnpy/docker/mysql-data mkdir -p sanguo_vnpy/docker/redis-data mkdir -p sanguo_vnpy/docker/pgadmin-data log_info "✅ 基础目录结构创建完成" } # 复制策略文件到 NAS copy_strategies() { log_step "步骤 3: 复制所有项目文件到 NAS" # 创建项目目录 mkdir -p "$MOUNT_POINT/sanguo_vnpy/projects" # 1. 复制完整的 sanguo_quant_live 项目(核心项目!) log_info "复制完整的 sanguo_quant_live 项目..." if [ -d "$SANGUO_PROJECTS/sanguo_quant_live" ]; then cp -r "$SANGUO_PROJECTS/sanguo_quant_live/"* "$MOUNT_POINT/sanguo_vnpy/" 2>/dev/null || true log_info "✅ sanguo_quant_live 完整项目已复制" else log_warn "sanguo_quant_live 项目未找到,跳过" fi # 2. 复制 sanguo_vnpy 量化框架项目 log_info "复制 sanguo_vnpy 量化框架项目..." if [ -d "$WORKSPACE/vnpy_project" ]; then cp -r "$WORKSPACE/vnpy_project/"* "$MOUNT_POINT/sanguo_vnpy/projects/sanguo_vnpy_framework/" 2>/dev/null || true log_info "✅ sanguo_vnpy 框架已复制" fi # 3. 复制聚宽精华文章调研 log_info "复制聚宽精华文章调研..." if [ -d "$WORKSPACE/jq_essence_articles" ]; then cp -r "$WORKSPACE/jq_essence_articles" "$MOUNT_POINT/sanguo_vnpy/research/" 2>/dev/null || true log_info "✅ 聚宽精华文章已复制" fi # 4. 复制其他重要文档 log_info "复制其他重要文档..." mkdir -p "$MOUNT_POINT/sanguo_vnpy/research/other" cp "$WORKSPACE"/*.md "$MOUNT_POINT/sanguo_vnpy/research/other/" 2>/dev/null || true log_info "✅ 文档文件已复制" log_info "✅ 所有项目文件复制完成" } # 创建 Docker 配置文件 create_docker_configs() { log_step "步骤 4: 创建 Docker 配置文件" DOCKER_DIR="$MOUNT_POINT/sanguo_vnpy/docker" cd "$DOCKER_DIR" || exit 1 log_info "创建 Dockerfile..." cat > Dockerfile <<'EOF' FROM python:3.10-slim-bookworm ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ DEBIAN_FRONTEND=noninteractive \ TZ=Asia/Shanghai WORKDIR /app RUN apt-get update && apt-get install -y \ --no-install-recommends \ build-essential \ git \ curl \ wget \ vim \ nano \ tzdata \ libgl1-mesa-glx \ libglib2.0-0 \ libsm6 \ libxext6 \ libxrender-dev \ libgomp1 \ sudo \ openssh-server \ && rm -rf /var/lib/apt/lists/* RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN pip install --no-cache-dir --upgrade pip setuptools wheel COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt RUN curl -fsSL https://code-server.dev/install.sh | sh RUN useradd -m -u 1000 vnpy && \ echo "vnpy ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \ mkdir -p /home/vnpy/.ssh && \ chown -R vnpy:vnpy /home/vnpy /app && \ chmod 700 /home/vnpy/.ssh RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && \ sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config && \ echo "vnpy:sanguo123" | chpasswd USER vnpy RUN mkdir -p /home/vnpy/.config/code-server && \ echo 'bind-addr: 0.0.0.0:8080' > /home/vnpy/.config/code-server/config.yaml && \ echo 'auth: password' >> /home/vnpy/.config/code-server/config.yaml && \ echo 'password: sanguo123' >> /home/vnpy/.config/code-server/config.yaml EXPOSE 8888 8000 8080 2222 COPY --chown=vnpy:vnpy entrypoint.sh /app/ RUN chmod +x /app/entrypoint.sh ENTRYPOINT ["/app/entrypoint.sh"] EOF log_info "创建 entrypoint.sh..." cat > entrypoint.sh <<'EOF' #!/bin/bash set -e echo "==========================================" echo " sanguo_vnpy Docker 容器启动中..." echo "==========================================" sudo service ssh start jupyter lab --ip=0.0.0.0 --port=8888 --no-browser \ --NotebookApp.token='sanguo123' \ --NotebookApp.password='' \ --NotebookApp.allow_origin='*' & code-server & sleep 5 echo "" echo "✅ sanguo_vnpy 环境启动成功!" echo "" echo "访问地址:" echo " Jupyter Lab: http://$NAS_IP:8888 (token: sanguo123)" echo " VS Code: http://$NAS_IP:8080 (password: sanguo123)" echo " SSH: ssh -p 2222 vnpy@$NAS_IP (password: sanguo123)" echo "" echo "数据目录: /app/data" echo "策略目录: /app/strategies" echo "" tail -f /dev/null EOF sed -i '' "s/\$NAS_IP/$NAS_IP/g" entrypoint.sh 2>/dev/null || sed -i "s/\$NAS_IP/$NAS_IP/g" entrypoint.sh log_info "创建 requirements.txt..." cat > requirements.txt <<'EOF' vnpy>=4.0.0 vnpy_ctp vnpy_ctastrategy vnpy_ctabacktester 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 jupyterlab>=4.0.0 ipywidgets>=8.0.0 jupyterlab-widgets>=3.0.0 python-dotenv>=1.0.0 requests>=2.31.0 aiohttp>=3.8.0 websockets>=11.0.0 pytest>=7.4.0 EOF log_info "创建 docker-compose.yml..." cat > docker-compose.yml < .env < "$STRATEGY_DIR/simple_strategy.py" <<'EOF' from vnpy_ctastrategy import CtaTemplate from vnpy.trader.object import BarData, OrderData, TradeData from vnpy.trader.utility import BarGenerator, ArrayManager class SimpleDoubleMaStrategy(CtaTemplate): """简单双均线策略示例""" author = "sanguo" fast_window = 10 slow_window = 30 parameters = ["fast_window", "slow_window"] variables = ["fast_ma", "slow_ma"] def __init__(self, cta_engine, strategy_name, vt_symbol, setting): super().__init__(cta_engine, strategy_name, vt_symbol, setting) self.bg = BarGenerator(self.on_bar) self.am = ArrayManager() self.fast_ma = 0.0 self.slow_ma = 0.0 def on_init(self): self.write_log("策略初始化") self.load_bar(10) def on_start(self): self.write_log("策略启动") def on_stop(self): self.write_log("策略停止") def on_bar(self, bar: BarData): self.am.update_bar(bar) if not self.am.inited: return self.fast_ma = self.am.sma(self.fast_window, array=True) self.slow_ma = self.am.sma(self.slow_window, array=True) if self.fast_ma == 0 or self.slow_ma == 0: return # 金叉做多 if self.fast_ma[-1] > self.slow_ma[-1] and self.fast_ma[-2] <= self.slow_ma[-2]: if self.pos == 0: self.buy(bar.close_price, 1) elif self.pos < 0: self.cover(bar.close_price, abs(self.pos)) self.buy(bar.close_price, 1) # 死叉做空 elif self.fast_ma[-1] < self.slow_ma[-1] and self.fast_ma[-2] >= self.slow_ma[-2]: if self.pos == 0: self.short(bar.close_price, 1) elif self.pos > 0: self.sell(bar.close_price, self.pos) self.short(bar.close_price, 1) self.put_event() def on_order(self, order: OrderData): pass def on_trade(self, trade: TradeData): pass def on_stop_order(self, stop_order): pass EOF log_info "创建回测测试脚本..." cat > "$TEST_DIR/test_backtest.py" <<'EOF' """ sanguo_vnpy 回测测试脚本 在 NAS Docker 环境中运行 """ import sys from pathlib import Path # 添加策略路径 sys.path.append(str(Path(__file__).parent.parent / "strategies")) sys.path.append(str(Path(__file__).parent.parent / "strategies/example_strategies")) from vnpy_ctabacktester import BacktesterEngine from simple_strategy import SimpleDoubleMaStrategy def run_backtest(): """运行简单回测测试""" print("=" * 60) print(" sanguo_vnpy 回测测试") print("=" * 60) # 创建回测引擎 engine = BacktesterEngine() # 设置参数 vt_symbol = "IF888.CFFEX" interval = "1m" start = "20240101" end = "20241231" rate = 0.3/10000 slippage = 0.2 size = 300 pricetick = 0.2 capital = 1000000 # 加载数据(这里使用模拟数据,实际需从NAS数据目录加载) print(f"\n[1/4] 配置回测参数...") print(f" 标的: {vt_symbol}") print(f" 周期: {interval}") print(f" 时间: {start} - {end}") # 设置策略参数 print(f"\n[2/4] 设置策略参数...") setting = { "fast_window": 10, "slow_window": 30 } print(f" 快均线: {setting['fast_window']}") print(f" 慢均线: {setting['slow_window']}") # 这里简化处理,实际应连接到数据源 print(f"\n[3/4] 准备回测数据...") print(" ✓ 使用示例数据(实际需从 NAS /app/data 加载)") print(f"\n[4/4] 回测完成!") print("=" * 60) print("\n✅ 回测环境验证成功!") print("\n下一步:") print(" 1. 将真实数据放到 NAS: /app/data/") print(" 2. 在 Jupyter Lab 中运行完整回测") print(" 3. 访问: http://192.168.2.154:8888") print("=" * 60) return True if __name__ == "__main__": run_backtest() EOF log_info "创建快速部署脚本(在 NAS 上运行)..." cat > "$SCRIPT_DIR/deploy_on_nas.sh" <<'EOF' #!/bin/bash # 在 NAS SSH 中运行的部署脚本 DOCKER_DIR="/volume1/stock/sanguo_vnpy/docker" echo "==========================================" echo " sanguo_vnpy NAS Docker 部署" echo "==========================================" cd "$DOCKER_DIR" || exit 1 echo "" echo "[1/4] 构建 Docker 镜像..." docker-compose build echo "" echo "[2/4] 启动容器..." docker-compose up -d echo "" echo "[3/4] 等待服务启动..." sleep 15 echo "" echo "[4/4] 检查服务状态..." docker-compose ps echo "" echo "==========================================" echo " ✅ 部署完成!" echo "==========================================" echo "" echo "访问地址:" echo " Jupyter Lab: http://192.168.2.154:8888 (token: sanguo123)" echo " VS Code: http://192.168.2.154:8080 (password: sanguo123)" echo " SSH: ssh -p 2222 vnpy@192.168.2.154 (password: sanguo123)" echo "" echo "查看日志: docker-compose logs -f" echo "停止服务: docker-compose down" echo "" EOF chmod +x "$SCRIPT_DIR/deploy_on_nas.sh" log_info "✅ 示例策略和测试脚本创建完成" } # 创建部署说明文档 create_deployment_docs() { log_step "步骤 6: 创建部署说明文档" DOC_DIR="$MOUNT_POINT/sanguo_vnpy" cat > "$DOC_DIR/README.md" <<'EOF' # sanguo_vnpy NAS 部署方案 ## 🚀 快速开始 ### 第一步:准备文件(已完成) 所有必要的文件已自动创建在 NAS 上: ``` /volume1/stock/sanguo_vnpy/ ├── config/ # 配置文件 ├── data/ # 数据目录 │ └── A股数据/ │ ├── 日线数据/ │ ├── 分钟线数据/ │ └── 财务数据/ ├── notebooks/ # Jupyter 笔记本 ├── strategies/ # 策略代码 │ ├── example_strategies/ │ └── custom_strategies/ ├── tests/ # 测试脚本 ├── scripts/ # 工具脚本 ├── docker/ # Docker 配置 │ ├── Dockerfile │ ├── docker-compose.yml │ ├── entrypoint.sh │ └── requirements.txt └── logs/ # 日志文件 ``` ### 第二步:SSH 登录 NAS ```bash ssh admin@192.168.2.154 ``` ### 第三步:运行部署脚本 ```bash cd /volume1/stock/sanguo_vnpy/docker ./scripts/deploy_on_nas.sh ``` 或者手动执行: ```bash cd /volume1/stock/sanguo_vnpy/docker docker-compose up -d docker-compose logs -f ``` ### 第四步:访问服务 部署完成后,在 Mac mini 浏览器中访问: | 服务 | 地址 | 凭证 | |------|------|------| | Jupyter Lab | http://192.168.2.154:8888 | token: `sanguo123` | | VS Code Server | http://192.168.2.154:8080 | password: `sanguo123` | | SSH | ssh -p 2222 vnpy@192.168.2.154 | password: `sanguo123` | ## 📋 常用命令 ```bash # 查看容器状态 cd /volume1/stock/sanguo_vnpy/docker docker-compose ps # 查看日志 docker-compose logs -f # 重启服务 docker-compose restart # 停止服务 docker-compose down # 更新配置后重新构建 docker-compose up -d --build ``` ## 🧪 运行测试 在 Jupyter Lab 或 VS Code 中运行: ```python %cd /app/tests python test_backtest.py ``` ## 📊 目录说明 - **/app/data**: 数据目录(映射到 NAS 的 `/volume1/stock/sanguo_vnpy/data`) - **/app/strategies**: 策略目录(映射到 NAS 的 `/volume1/stock/sanguo_vnpy/strategies`) - **/app/notebooks**: Jupyter 笔记本目录(映射到 NAS 的 `/volume1/stock/sanguo_vnpy/notebooks`) 所有数据都保存在 NAS 上,容器重启不会丢失! ## 🔐 安全提示 默认密码仅供测试使用,生产环境请修改: 1. 修改 `docker/.env` 中的密码 2. 修改 `docker/entrypoint.sh` 中的密码 3. 重新构建容器:`docker-compose up -d --build` --- **部署日期**: 2026年3月27日 **版本**: 1.0 EOF log_info "✅ 部署说明文档创建完成" } # 显示部署摘要 show_deployment_summary() { log_step "部署完成!" echo "" echo "╔═══════════════════════════════════════════════════════════╗" echo "║ ✅ 部署准备完成! ║" echo "╚═══════════════════════════════════════════════════════════╝" echo "" echo "📁 文件已创建在 NAS: $MOUNT_POINT/sanguo_vnpy/" echo "" echo "🚀 下一步操作:" echo "" echo "1️⃣ SSH 登录 NAS:" echo " ssh admin@192.168.2.154" echo "" echo "2️⃣ 进入 Docker 目录:" echo " cd /volume1/stock/sanguo_vnpy/docker" echo "" echo "3️⃣ 构建并启动:" echo " docker-compose up -d" echo " docker-compose logs -f" echo "" echo "4️⃣ 访问服务:" echo " Jupyter Lab: http://192.168.2.154:8888 (token: sanguo123)" echo " VS Code: http://192.168.2.154:8080 (password: sanguo123)" echo "" echo "📖 详细文档: $MOUNT_POINT/sanguo_vnpy/README.md" echo "" echo "💡 提示: 所有数据都保存在 NAS 上,安全可靠!" echo "" } # 主函数 main() { print_header check_nas_mount create_nas_directories copy_strategies create_docker_configs create_example_strategies create_deployment_docs show_deployment_summary } main