diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e726169 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.git +.idea +__pycache__ +*.pyc +.env +logs +.DS_Store diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9d5c701 --- /dev/null +++ b/.env.example @@ -0,0 +1,25 @@ +# tradingSystem (PMS) 环境变量模板 +# 服务器部署: cp .env.example .env 后填入真实值。.env 已在 .gitignore, 绝不入库。 +# 键名与 bionic_trader 对齐, 同一台服务器两系统可参照同一套连接信息。 +# 业务参数 (PMS_TOTAL_SCALE 等) 不必写在这里 —— settings.py 有初值, +# 上线后经管理页面调整并持久化到 pms_runtime_param 表。 + +# ---- 153 代理 (pms_* 全部表 + trading_* + strategy_daily_results, 严格单表访问) ---- +PROXY_DB_URL=mysql+pymysql://用户:密码@192.168.16.153:3307/my_quant_db + +# ---- 因子分表库 (gp_stock_factor_pro_*, 自算参考位用) ---- +SOURCE_DB_EXT_DSN=mysql+pymysql://用户:密码@192.168.16.150:3306/factordb_mysql + +# ---- 大盘指数库 (zs_day_data, 页面区制提示用) ---- +DB_MYSQL_URL=mysql+pymysql://用户:密码@192.168.18.199:3306/db_gp_cj + +# ---- 信号/行情 Redis (208: 决策系统信号流 db2/db3 订阅 + 实时行情 db13) ---- +SIGNAL_REDIS_HOST=192.168.18.208 +SIGNAL_REDIS_PORT=6379 +SIGNAL_REDIS_PASSWORD=密码 + +# ---- PMS 自己的 Celery 总线 (150 Redis, db8; 决策系统用 db7, 逻辑隔离) ---- +PMS_REDIS_URL=redis://:密码@192.168.16.150:6379/8 + +# ---- 管理页面 ---- +PMS_WEB_PORT=38100 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2555c47 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# 机密: 数据库/Redis 连接串与密码, 绝不入库 (服务器上手工维护, 参照 .env.example) +.env + +__pycache__/ +*.pyc +logs/ +.DS_Store + +# PyCharm 工程文件目前已跟踪; 如决定不入库, 取消下行注释并执行: +# git rm -r --cached .idea && git commit -m "untrack .idea" +#.idea/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..284ed37 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +# tradingSystem (PMS) 运行镜像 +# 三个服务 (pms-web / pms-beat / pms-worker) 共用本镜像, 由 docker-compose 以不同 command 启动 +FROM python:3.11-slim + +ENV TZ=Asia/Shanghai \ + PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 + +# 国内构建默认走清华 PyPI 镜像; 网络环境不同可覆盖: +# docker compose build --build-arg PIP_INDEX_URL=https://pypi.org/simple +ARG PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple + +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone + +WORKDIR /app + +# 依赖层单独 COPY, 代码改动不触发重装依赖 +COPY requirements.txt . +RUN pip install --no-cache-dir -i ${PIP_INDEX_URL} -r requirements.txt + +COPY . . + +RUN mkdir -p /app/logs + +# 默认命令 = 管理页面; beat/worker 由 compose 覆盖 command +CMD ["uvicorn", "app.web.main:app", "--host", "0.0.0.0", "--port", "38100"] diff --git a/README.md b/README.md index ae63d1a..5dfd48c 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,31 @@ 2. **分工不越权**:持仓系统管「做什么、多少」,决策系统管「该不该、何时」,下游只管执行;研判不可用时降级为保守规则 + 人工确认,不自建第二套研判。 3. **先记账后动作 + 故障即守成**:指令先落表再下发;故障不产生新指令;账本与下游定期对账,以下游为实际持仓事实源。 +## Docker 部署(项目统一以容器方式构建运行) + +服务共用一个镜像:`pms-web`(管理页面,端口 38100)+ `pms-beat` / `pms-worker`(Celery 调度与执行,挂在 `sched` profile 下,调度器代码就绪后启用)。 + +```bash +# 服务器首次部署 +git clone <仓库地址> && cd tradingSystem +cp .env.example .env && vim .env # 填入真实连接串 (.env 不入库) +docker compose build # 默认走清华 PyPI 镜像; 可 --build-arg PIP_INDEX_URL=... 覆盖 +docker compose run --rm pms-web python scripts/test_core_units.py # 构建验证: 应输出 ALL PASS (14 cases) +docker compose up -d # 当前仅启动 pms-web +curl http://127.0.0.1:38100/health # 健康检查 + 配置装载自证 + +# 调度器代码就绪后 +docker compose --profile sched up -d + +# 日常更新 +git pull && docker compose build && docker compose up -d +``` + +基础镜像 `python:3.11-slim` 拉取慢时,先给服务器 Docker 配置 registry 镜像加速。日志落 `./logs`(已挂载卷);容器时区 Asia/Shanghai。 + ## 开发约定 -- 开发机与服务器经 git 同步代码;测试脚本由用户在实机运行,结果回传后迭代。 +- 开发机与服务器经 git 同步代码;**构建与运行统一走 Docker**(`docker compose build` → 容器内跑测试脚本 → `up -d`),测试结果回传后迭代。 - 153 代理侧数据库严格单表访问;持仓系统内部代码统一 Tushare 点式。 -- 配置全部在 `config/settings.py` 显式声明字段;参数可经管理页面在线修改并持久化。 +- 配置分两层:基础设施连接串在 `.env`(服务器手工维护,不入库,模板见 `.env.example`);业务参数在 `config/settings.py` 只是初值,上线后经管理页面修改并持久化到 `pms_runtime_param` 表。 - 里程碑(设计定稿、建表、各期上线)及时 git 提交。 diff --git a/app/web/main.py b/app/web/main.py new file mode 100644 index 0000000..0651812 --- /dev/null +++ b/app/web/main.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" +PMS 管理页面 · Web 入口 (当前为最小骨架) +========================================== +现阶段职责: 让 pms-web 容器可启动、可健康检查, 验证 Docker 构建与配置装载链路。 +后续在此基础上扩展四块页面 (POSITION_MGMT_DESIGN.md §3.3): + 参数设置 / 命令台 / 持仓与账本 / 提议确认。 +""" +from fastapi import FastAPI + +from config.settings import settings + +VERSION = "0.1.0-dev" + +app = FastAPI(title="PMS 持仓管理系统", version=VERSION) + + +@app.get("/health") +def health(): + """容器健康检查 + 配置装载自证 (不连库, 只回显关键初值)。""" + return { + "status": "ok", + "version": VERSION, + "service": "pms-web", + "config_loaded": { + "total_scale": settings.PMS_TOTAL_SCALE, + "portfolio_cap": settings.PMS_PORTFOLIO_CAP, + "stock_cap": settings.PMS_STOCK_CAP, + "max_names": settings.PMS_MAX_NAMES, + "autonomy": settings.PMS_AUTONOMY, + "web_port": settings.PMS_WEB_PORT, + }, + } + + +@app.get("/") +def index(): + return {"hint": "PMS 管理页面骨架。健康检查: /health ; 页面四块随后续开发接入。"} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f4e2d39 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,45 @@ +# tradingSystem (PMS) 容器编排 +# 用法见 README「Docker 部署」。.env 放在本文件同目录 (服务器上手工维护, 不入库)。 +# 当前默认只启动 pms-web; pms-beat / pms-worker 挂在 sched profile 下, +# 调度器代码 (app/scheduler.py) 就绪后用 --profile sched 启用。 + +x-pms-base: &pms-base + build: . + image: pms:latest + env_file: .env + environment: + - TZ=Asia/Shanghai + volumes: + - ./logs:/app/logs + restart: unless-stopped + logging: + driver: json-file + options: + max-size: "50m" + max-file: "5" + +services: + pms-web: + <<: *pms-base + container_name: pms-web + command: uvicorn app.web.main:app --host 0.0.0.0 --port 38100 + ports: + - "38100:38100" + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:38100/health', timeout=3)"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + + pms-beat: + <<: *pms-base + container_name: pms-beat + profiles: ["sched"] + command: celery -A app.scheduler.celery_app beat -l info + + pms-worker: + <<: *pms-base + container_name: pms-worker + profiles: ["sched"] + command: celery -A app.scheduler.celery_app worker -l info -c 2