Compare commits
2 Commits
76feec6824
...
fb2f814111
| Author | SHA1 | Date | |
|---|---|---|---|
| fb2f814111 | |||
| 1b8eba8aff |
@@ -74,7 +74,7 @@ docker-compose*.yml
|
|||||||
.dockerignore
|
.dockerignore
|
||||||
|
|
||||||
# 其他
|
# 其他
|
||||||
.env
|
# .env # 允许打包进镜像(容器化部署需要)
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|
||||||
|
|||||||
16
Dockerfile
16
Dockerfile
@@ -3,6 +3,11 @@ FROM index-base:latest
|
|||||||
# 设置工作目录
|
# 设置工作目录
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 安装中文字体(使用清华源加速)
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
fonts-wqy-microhei \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# 复制依赖文件
|
# 复制依赖文件
|
||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
|
|
||||||
@@ -11,9 +16,14 @@ RUN uv pip install --system -r requirements.txt
|
|||||||
# 仅复制除 data 目录外的应用代码, data 在 dockerignore 中已经被排除
|
# 仅复制除 data 目录外的应用代码, data 在 dockerignore 中已经被排除
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# 创建日志目录
|
||||||
|
RUN mkdir -p /app/logs
|
||||||
|
|
||||||
# 暴露端口
|
# 设置时区为上海
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
|
||||||
|
# 暴露端口(如需Web服务)
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
# 运行应用
|
# 运行定时任务调度器(默认daemon模式)
|
||||||
# CMD ["python", "update_data.py"]
|
CMD ["python", "scripts/daily_scheduler.py", "--time", "09:00"]
|
||||||
@@ -64,6 +64,13 @@ RUN apt-get update && apt-get install -y \
|
|||||||
libopengl0 \
|
libopengl0 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 安装中文字体
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
fonts-noto-cjk \
|
||||||
|
fonts-wqy-zenhei \
|
||||||
|
fonts-wqy-microhei \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# 安装Playwright浏览器
|
# 安装Playwright浏览器
|
||||||
RUN playwright install chromium
|
RUN playwright install chromium
|
||||||
RUN playwright install-deps
|
RUN playwright install-deps
|
||||||
|
|||||||
@@ -1,20 +1,35 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Value Investing App - 构建和推送脚本
|
# ETF策略 - 构建和推送脚本
|
||||||
# 使用 docker 构建镜像并推送到私有仓库
|
# 使用 docker 构建镜像并推送到私有仓库
|
||||||
|
|
||||||
set -e # 遇到错误立即退出
|
set -e # 遇到错误立即退出
|
||||||
|
|
||||||
# 配置变量
|
# 配置变量
|
||||||
IMAGE_NAME="index-app"
|
IMAGE_NAME="${1:-etf-scheduler}"
|
||||||
REGISTRY="192.168.0.115:5000"
|
REGISTRY="192.168.0.115:5000"
|
||||||
TAG="latest"
|
TAG="latest"
|
||||||
FULL_IMAGE_NAME="${REGISTRY}/${IMAGE_NAME}:${TAG}"
|
FULL_IMAGE_NAME="${REGISTRY}/${IMAGE_NAME}:${TAG}"
|
||||||
|
|
||||||
echo "========================================="
|
echo "========================================="
|
||||||
echo "Value Investing App 构建和推送脚本"
|
echo "ETF策略 构建和推送脚本"
|
||||||
echo "========================================="
|
echo "========================================="
|
||||||
|
echo "镜像名称: ${IMAGE_NAME}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 检查并构建基础镜像(如果不存在)
|
||||||
|
echo "0. 检查基础镜像..."
|
||||||
|
if ! docker images | grep -q "index-base"; then
|
||||||
|
echo " 基础镜像不存在,开始构建..."
|
||||||
|
if [ -f "Dockerfile_base" ]; then
|
||||||
|
docker build --platform linux/arm64 -f Dockerfile_base -t index-base:latest .
|
||||||
|
echo " ✅ 基础镜像构建成功"
|
||||||
|
else
|
||||||
|
echo " ⚠️ 未找到 Dockerfile_base,跳过基础镜像构建"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ✅ 基础镜像已存在"
|
||||||
|
fi
|
||||||
|
|
||||||
# 构建镜像
|
# 构建镜像
|
||||||
echo "1. 构建 Docker 镜像..."
|
echo "1. 构建 Docker 镜像..."
|
||||||
@@ -64,8 +79,12 @@ echo "========================================="
|
|||||||
echo "镜像地址: ${FULL_IMAGE_NAME}"
|
echo "镜像地址: ${FULL_IMAGE_NAME}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "可以使用以下命令运行容器:"
|
echo "可以使用以下命令运行容器:"
|
||||||
echo "docker run -d -p 5000:5000 --name value-investing-container ${FULL_IMAGE_NAME}"
|
echo "docker run -d --name etf-scheduler-container \\"
|
||||||
|
echo " -v /path/to/.env:/app/.env \\"
|
||||||
|
echo " -v /path/to/hk_ecs.pem:/app/hk_ecs.pem \\"
|
||||||
|
echo " -v /path/to/data:/app/data \\"
|
||||||
|
echo " ${FULL_IMAGE_NAME}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "或者在其他机器上拉取镜像:"
|
echo "或者在其他机器上拉取镜像:"
|
||||||
echo "docker pull ${FULL_IMAGE_NAME}"
|
echo "docker pull k3d-quant-registry:5000/${IMAGE_NAME}:${TAG}"
|
||||||
echo "========================================="
|
echo "========================================="
|
||||||
@@ -1,32 +1,26 @@
|
|||||||
version: '3.8'
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
etf-scheduler:
|
||||||
image: postgres:15-alpine
|
image: 192.168.0.115:5000/etf-scheduler:latest
|
||||||
container_name: etf_postgres
|
container_name: etf-scheduler
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: etf_db
|
- TZ=Asia/Shanghai
|
||||||
POSTGRES_USER: admin
|
|
||||||
POSTGRES_PASSWORD: admin
|
|
||||||
POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
|
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
# 挂载环境变量文件(必需)
|
||||||
networks:
|
- ./.env:/app/.env:ro
|
||||||
- etf_network
|
# 挂载 SSH 私钥(必需,用于 yfinance 数据下载)
|
||||||
healthcheck:
|
- ./hk_ecs.pem:/app/hk_ecs.pem:ro
|
||||||
test: ["CMD-SHELL", "pg_isready -U admin -d etf_db"]
|
# 挂载数据目录(持久化)
|
||||||
interval: 30s
|
- ./data:/app/data
|
||||||
timeout: 10s
|
# 挂载日志目录
|
||||||
retries: 3
|
- ./logs:/app/logs
|
||||||
start_period: 30s
|
# 挂载 results 目录(保存报告)
|
||||||
|
- ./results:/app/results
|
||||||
volumes:
|
# 默认daemon模式运行,只需简单命令即可
|
||||||
postgres_data:
|
# command: ["python", "scripts/daily_scheduler.py"]
|
||||||
driver: local
|
# 如需立即执行一次并退出:
|
||||||
|
# command: ["python", "scripts/daily_scheduler.py", "--run-now"]
|
||||||
networks:
|
# 如需执行一次后进入定时循环:
|
||||||
etf_network:
|
# command: ["python", "scripts/daily_scheduler.py", "--no-daemon"]
|
||||||
driver: bridge
|
|
||||||
|
|||||||
@@ -1,29 +1,53 @@
|
|||||||
# 核心依赖
|
# ==================== 核心依赖 ====================
|
||||||
pandas>=1.5.0
|
pandas>=1.5.0
|
||||||
numpy>=1.24.0
|
numpy>=1.24.0
|
||||||
|
|
||||||
# 数据库连接
|
# ==================== 数据库连接 ====================
|
||||||
psycopg2-binary>=2.9.0
|
psycopg2-binary>=2.9.0
|
||||||
sqlalchemy>=2.0.0
|
sqlalchemy>=2.0.0
|
||||||
|
|
||||||
# 日志
|
# ==================== 日志 ====================
|
||||||
loguru>=0.7.0
|
loguru>=0.7.0
|
||||||
|
|
||||||
|
# ==================== HTTP请求 ====================
|
||||||
# HTTP请求
|
|
||||||
requests>=2.28.0
|
requests>=2.28.0
|
||||||
urllib3>=1.26.0
|
urllib3>=1.26.0
|
||||||
|
|
||||||
# 进度条
|
# ==================== 进度条 ====================
|
||||||
tqdm>=4.65.0
|
tqdm>=4.65.0
|
||||||
|
|
||||||
|
# ==================== 时间处理 ====================
|
||||||
# 时间处理
|
|
||||||
python-dateutil>=2.8.0
|
python-dateutil>=2.8.0
|
||||||
schedule
|
schedule>=1.2.0
|
||||||
akshare
|
|
||||||
TA-Lib
|
|
||||||
tabulate
|
|
||||||
|
|
||||||
|
# ==================== 金融数据 ====================
|
||||||
|
tushare>=1.2.0
|
||||||
|
yfinance>=0.2.0
|
||||||
|
akshare>=1.10.0
|
||||||
|
|
||||||
|
# ==================== 技术分析 ====================
|
||||||
|
TA-Lib>=0.4.0
|
||||||
|
|
||||||
|
# ==================== 表格输出 ====================
|
||||||
|
tabulate>=0.9.0
|
||||||
|
|
||||||
|
# ==================== 自动化测试 ====================
|
||||||
playwright>=1.45.1
|
playwright>=1.45.1
|
||||||
retry>=0.9.2
|
|
||||||
|
# ==================== 重试机制 ====================
|
||||||
|
retry>=0.9.2
|
||||||
|
|
||||||
|
# ==================== 环境变量 ====================
|
||||||
|
python-dotenv>=1.0.0
|
||||||
|
|
||||||
|
# ==================== 阿里云OSS ====================
|
||||||
|
oss2>=2.18.0
|
||||||
|
|
||||||
|
# ==================== YAML配置 ====================
|
||||||
|
PyYAML>=6.0
|
||||||
|
|
||||||
|
# ==================== 绘图 ====================
|
||||||
|
matplotlib>=3.7.0
|
||||||
|
|
||||||
|
# ==================== 机器学习 ====================
|
||||||
|
scikit-learn>=1.3.0
|
||||||
@@ -258,7 +258,7 @@ def main():
|
|||||||
"--time",
|
"--time",
|
||||||
type=str,
|
type=str,
|
||||||
default="09:00",
|
default="09:00",
|
||||||
help="执行时间 (HH:MM),默认15:30",
|
help="执行时间 (HH:MM),默认09:00",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--config",
|
"--config",
|
||||||
@@ -269,12 +269,12 @@ def main():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--run-now",
|
"--run-now",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="立即执行一次(不启动定时任务)",
|
help="立即执行一次并退出(不启动定时任务)",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--daemon",
|
"--no-daemon",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="后台运行(持续执行定时任务)",
|
help="非后台模式:执行一次后进入定时循环",
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -282,19 +282,19 @@ def main():
|
|||||||
(project_root / "logs").mkdir(exist_ok=True)
|
(project_root / "logs").mkdir(exist_ok=True)
|
||||||
|
|
||||||
if args.run_now:
|
if args.run_now:
|
||||||
# 立即执行一次
|
# 立即执行一次并退出
|
||||||
daily_task(args.config)
|
daily_task(args.config)
|
||||||
elif args.daemon:
|
elif args.no_daemon:
|
||||||
# 后台运行模式
|
# 非后台模式:执行一次后进入定时循环
|
||||||
setup_schedule(args.time, args.config)
|
|
||||||
run_scheduler_loop()
|
|
||||||
else:
|
|
||||||
# 默认:设置定时任务并执行一次(用于测试)
|
|
||||||
setup_schedule(args.time, args.config)
|
setup_schedule(args.time, args.config)
|
||||||
logger.info("执行一次任务用于测试...")
|
logger.info("执行一次任务用于测试...")
|
||||||
daily_task(args.config)
|
daily_task(args.config)
|
||||||
logger.info("测试完成,启动定时任务循环(按 Ctrl+C 停止)...")
|
logger.info("测试完成,启动定时任务循环(按 Ctrl+C 停止)...")
|
||||||
run_scheduler_loop()
|
run_scheduler_loop()
|
||||||
|
else:
|
||||||
|
# 默认:后台daemon模式
|
||||||
|
setup_schedule(args.time, args.config)
|
||||||
|
run_scheduler_loop()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -292,7 +292,14 @@ def _plot_report_chart(
|
|||||||
metrics: dict = None,
|
metrics: dict = None,
|
||||||
):
|
):
|
||||||
"""绘制报告图表"""
|
"""绘制报告图表"""
|
||||||
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS", "SimHei", "DejaVu Sans"]
|
# 设置中文字体(优先使用基础镜像中已存在的字体)
|
||||||
|
plt.rcParams["font.sans-serif"] = [
|
||||||
|
"WenQuanYi Zen Hei", # 基础镜像已安装
|
||||||
|
"WenQuanYi Micro Hei", # 将要安装
|
||||||
|
"DejaVu Sans",
|
||||||
|
"SimHei",
|
||||||
|
"Arial Unicode MS"
|
||||||
|
]
|
||||||
plt.rcParams["axes.unicode_minus"] = False
|
plt.rcParams["axes.unicode_minus"] = False
|
||||||
|
|
||||||
strategy_nav = backtest_result["轮动策略净值"]
|
strategy_nav = backtest_result["轮动策略净值"]
|
||||||
|
|||||||
Reference in New Issue
Block a user