Initial: MoFin 持仓分析与策略管理系统

核心模块:
- 策略生命周期管理 (strategy_lifecycle.py)
- 技术分析引擎 (technical_analysis.py)
- 双维度策略评估 (strategy_evaluator.py)
- 实时行情获取 (get_realtime_prices.py)
- Web Dashboard (server.py, :8899)

提示词版本管理:
- prompt_manager 模块 — 统一管理所有知微提示词
- 8个提示词共24个版本已录入
- 策略→提示词版本关联追踪
- Dashboard「提示词」Tab

数据源增强:
- 服务端 POST /api/update/realtime 端点已就绪
- clients/tdx-relay/ — 小小莫在Windows上开发的通达信中继
- 解决港股15分钟延迟问题
This commit is contained in:
2026-06-12 22:54:51 +08:00
commit 9b9c37002a
65 changed files with 8659 additions and 0 deletions
+191
View File
@@ -0,0 +1,191 @@
# 通达信行情中继 — 开发指南
> 写给小小莫。在 Windows 上用 Python 直连招商证券的通达信行情主站,获取实时港股行情,推送到知微的 MoFin Dashboard。
---
## 一、背景
知微的 MoFin 系统目前用腾讯免费 API 获取行情。A 股是实时的,但港股有 **15 分钟延迟**
你在 Windows 上的招商证券客户端(通达信内核)是买了港股实时数据的。我们可以通过 Python 直连招商证券的行情主站,拿到**真正实时的港股行情**,然后推送到知微的 Dashboard。
## 二、技术方案
```
招商证券客户端 (通信设置里的IP:7709)
opentdx (Python库)
tdx_relay.py (你写)
│ POST /api/update/realtime
MoFin Dashboard (192.168.1.246:8899)
```
推荐用 **opentdx**pytdx 的升级版):
```bash
pip install opentdx requests
```
## 三、第一步:拿行情主站IP
打开招商证券 PC 客户端:
1. **菜单 → 选项 → 通信设置**
2. 你会看到一列行情主站,类似:`招商证券深圳主站 113.105.73.88:7709`
3. 记下来,填到 `src/relay/config.py`
或者在命令行找:
```cmd
tasklist | findstr "zhsh" # 找招商证券进程PID
netstat -ano | findstr "7709" # 看连接的IP
```
## 四、CLI 验证连通性
装好 opentdx 后,先用命令行测试:
```bash
# 测试 A 股
opentdx quote "SZ 000001"
# 测试港股(用你的服务器IP替换)
opentdx g-quote "HK_MAIN_BOARD 00700" --server 113.105.73.88:7709
```
> opentdx 的完整命令列表:`opentdx doc`(交互式文档)
## 五、Python 代码示例
### 连接 + 获取港股行情
```python
from opentdx.tdxClient import TdxClient
from opentdx.const import EX_MARKET
# 方式一:自动选最快服务器
with TdxClient() as client:
# A股
a_quotes = client.stock_quotes([(0, '000001')])
# 港股 ⭐
hk_quotes = client.goods_quotes([
(EX_MARKET.HK_MAIN_BOARD, '00700'), # 腾讯
(EX_MARKET.HK_MAIN_BOARD, '09988'), # 阿里
])
for q in hk_quotes:
print(f"{q['code']}: {q['price']} {q['change_pct']}%")
```
> ⚠️ `opentdx` 的具体 API 以 `opentdx doc` 为准。
> GitHub: https://github.com/acb6104/opentdx
### 推送到 MoFin
用现成的工具类:
```python
from relay.pusher import MoFinPusher
pusher = MoFinPusher("http://192.168.1.246:8899")
result = pusher.push([
{
"code": "00700",
"name": "腾讯控股",
"price": 463.6,
"change_pct": 1.55,
"high": 468.0,
"low": 460.2,
"open": 462.0,
"volume": 25000000,
"timestamp": "2026-06-12 14:30:00"
}
])
print(result) # {"status": "ok", "updated": 1}
```
## 六、你的任务清单
### 阶段一:环境 + 连通性
- [ ] 装 Python 3.10+(如果没有的话)
- [ ] `pip install opentdx requests`
- [ ] 运行 `opentdx doc` 看看接口
- [ ] 从招商证券通信设置拿到行情主站IP
- [ ] 用 CLI 测试港股:`opentdx g-quote "HK_MAIN_BOARD 00700" --server <IP>:7709`
### 阶段二:港股行情获取
- [ ] 实现 `tdx_client.py` 的连接和港股查询
- [ ] 验证单只港股(腾讯00700)数据正确
- [ ] 验证批量港股(持仓列表)
- [ ] ⚠️ **对比招商证券客户端价格,确认数据准确**(红线)
### 阶段三:数据推送
- [ ] 实现 `run_relay.py` 主循环
- [ ] 测试推送一条数据到 MoFin Dashboard
- [ ] 全量推送所有持仓港股
### 阶段四:自动化
- [ ] 设置 Windows 定时任务(每15~30秒运行一次)
## 七、注意事项
### 数据准确性(⚠️红线)
```python
# 从通达信拿到的价格 和 招商证券客户端显示的现价
# 两者必须一致!
tdx_price = 463.6
assert abs(tdx_price - 招商证券_显示价格) < 0.01
```
### A股不要动
A 股继续走腾讯 API,已经是实时的。**本项目只解决港股延迟。**
### 字段映射
| 含义 | 腾讯API索引 | 通达信字段 |
|------|------------|-----------|
| 当前价 | fields[3] | price |
| 昨收 | fields[4] | last_close |
| 今开 | fields[5] | open |
| 最高 | fields[33] | high |
| 最低 | fields[34] | low |
| 涨跌幅 | fields[32] | change_pct |
### 回退方案
通达信连不上时自动回退腾讯 API(当前方案),不中断行情更新。
```python
try:
data = tdx_client.get_quotes(codes)
except Exception:
data = tencent_api.get_quotes(codes) # 回退
```
### 连接稳定性
opentdx 内置心跳,但网络不稳时需要重连:
```python
def safe_get(client, codes, retries=3):
for i in range(retries):
try:
return client.goods_quotes(codes)
except (ConnectionError, TimeoutError):
client.disconnect()
time.sleep(2)
client.connect(ip, port)
return None # 回退腾讯API
```
## 八、参考
| 资源 | 地址 |
|------|------|
| opentdx GitHub | https://github.com/acb6104/opentdx |
| opentdx PyPI | `pip install opentdx` |
| MoFin Dashboard | http://192.168.1.246:8899 |
| 架构文档 | 见本目录上级 `docs/` |
+2
View File
@@ -0,0 +1,2 @@
opentdx>=0.1.0
requests>=2.25.0
@@ -0,0 +1,13 @@
@echo off
echo MoFin TDX Relay - 依赖安装
pip install opentdx
if %ERRORLEVEL% NEQ 0 (
echo ❌ opentdx 安装失败
pause
exit /b 1
)
pip install requests
echo ✅ 安装完成
echo 下一步:编辑 src/relay/config.py 填入招商证券IP
echo 然后运行:python tests/test_tdx_connect.py
pause
+62
View File
@@ -0,0 +1,62 @@
"""运行入口 — 循环获取通达信行情并推送 MoFin
小小莫:完成 tdx_client.py 后运行此脚本。
"""
import time
import logging
from relay.tdx_client import TDXClient
from relay.pusher import MoFinPusher
from relay.config import MARKET_SERVERS, MOFIN_URL, PUSH_INTERVAL, HK_STOCKS
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
log = logging.getLogger(__name__)
def find_working_server() -> tuple:
"""自动检测可用的通达信服务器"""
for name, ip, port in MARKET_SERVERS:
try:
client = TDXClient()
client.connect(ip, port)
data = client.get_hk_quote(31, "00700")
client.close()
if data and data.get("price"):
log.info(f"{name} ({ip}:{port}) 可用")
return (ip, port)
except Exception as e:
log.warning(f"{name} ({ip}:{port}): {e}")
return (None, None)
def main():
log.info("🚀 TDX Relay 启动")
log.info(f"目标: {MOFIN_URL} | 港股: {len(HK_STOCKS)}只 | 间隔: {PUSH_INTERVAL}s")
ip, port = find_working_server()
if not ip:
log.error("没有可用服务器")
return
pusher = MoFinPusher(MOFIN_URL)
client = TDXClient()
try:
client.connect(ip, port)
while True:
try:
hk_data = client.get_hk_quotes()
if hk_data:
r = pusher.push(hk_data)
log.info(f"推送 {len(hk_data)} 只: {r.get('status')}")
time.sleep(PUSH_INTERVAL)
except (ConnectionError, TimeoutError):
log.warning("断连,重试...")
client.close()
time.sleep(3)
client.connect(ip, port)
finally:
client.close()
if __name__ == "__main__":
main()
+1
View File
@@ -0,0 +1 @@
"""tdx-relay package — 通达信行情中继"""
+39
View File
@@ -0,0 +1,39 @@
"""配置管理 — 请在 Windows 上填好招商证券行情主站IP后运行"""
# 招商证券行情主站列表
# 打开招商证券PC客户端 → 通信设置 查看
# 格式: (名称, IP, 端口)
MARKET_SERVERS = [
# 示例(替换为你的实际IP):
# ("招商证券深圳主站", "113.105.73.88", 7709),
# ("招商证券上海主站", "211.154.53.106", 7709),
# TODO: 小小莫替换为实际IP
]
# MoFin Dashboard 地址
MOFIN_URL = "http://192.168.1.246:8899"
# 推送频率(秒)
PUSH_INTERVAL = 15
# 港股列表(市场代码, 股票代码)
# 港股主板 market_code = 31
HK_STOCKS = [
(31, "00700"), # 腾讯控股
(31, "09988"), # 阿里巴巴-W
(31, "00981"), # 中芯国际
(31, "01211"), # 比亚迪股份
(31, "01888"), # 建滔积层板
(31, "02318"), # 中国平安
(31, "02359"), # 药明康德
(31, "02388"), # 中银香港
(31, "02628"), # 中国人寿
(31, "06160"), # 百济神州
(31, "06869"), # 长飞光纤
(31, "09868"), # 小鹏汽车-W
(31, "01070"), # TCL电子
(31, "01088"), # 中国神华
(31, "00968"), # 信义光能
(31, "02202"), # 万科企业
(31, "01478"), # 丘钛科技
]
+40
View File
@@ -0,0 +1,40 @@
"""数据推送器 — 推送实时行情到 MoFin Dashboard"""
import json
import time
import urllib.request
import urllib.error
class MoFinPusher:
"""推送实时行情到 MoFin Dashboard API"""
def __init__(self, base_url: str = "http://192.168.1.246:8899"):
self.base_url = base_url.rstrip("/")
self.endpoint = f"{self.base_url}/api/update/realtime"
def push(self, stocks: list) -> dict:
"""推送一批实时行情
Args:
stocks: [{"code", "price", "change_pct", ...}, ...]
Returns:
{"status": "ok", "updated": N, "timestamp": "..."}
"""
payload = json.dumps({
"stocks": stocks,
"source": "tdx_relay",
}).encode("utf-8")
req = urllib.request.Request(
self.endpoint,
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read().decode("utf-8"))
except (urllib.error.URLError, urllib.error.HTTPError, OSError) as e:
return {"status": "error", "message": str(e)}
+62
View File
@@ -0,0 +1,62 @@
"""TDX 行情客户端 — 连接通达信行情服务器获取实时数据
小小莫:
1. pip install opentdx
2. 在 config.py 中填入招商证券行情主站IP
3. 实现下面的方法
4. 运行 test_tdx_connect.py 验证
"""
# ═══════════════════════════════════════════
# 配置区 — 请替换为你的招商证券行情主站IP
# ═══════════════════════════════════════════
from .config import MARKET_SERVERS, HK_STOCKS
class TDXClient:
"""通达信行情客户端封装
用法:
client = TDXClient()
client.connect("113.105.73.88", 7709)
data = client.get_hk_quotes()
client.close()
"""
def __init__(self):
self.client = None
def connect(self, ip: str, port: int = 7709):
"""连接到通达信行情服务器"""
# TODO: 小小莫实现
# from opentdx.client.macExtendedClient import MacExtendedClient
# self.client = MacExtendedClient()
# self.client.connect(ip, port)
raise NotImplementedError("由小小莫实现")
def get_hk_quote(self, market_code: int, code: str) -> dict:
"""获取单只港股实时报价
Args:
market_code: 港股主板=31
code: 如 "00700"
Returns:
{"code": "00700", "name": "腾讯控股", "price": 463.6,
"change_pct": 1.55, "high": 468.0, "low": 460.2, ...}
"""
raise NotImplementedError("由小小莫实现")
def get_hk_quotes(self, codes: list = None) -> list:
"""批量获取港股实时报价"""
if codes is None:
codes = [(31, code) for _, code in HK_STOCKS]
raise NotImplementedError("由小小莫实现")
def close(self):
if self.client:
try:
self.client.disconnect()
except Exception:
pass
self.client = None
+23
View File
@@ -0,0 +1,23 @@
"""MoFin API 推送测试"""
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
from relay.pusher import MoFinPusher
pusher = MoFinPusher()
result = pusher.push([{
"code": "00700",
"name": "腾讯控股",
"price": 463.6,
"change_pct": 1.55,
"high": 468.0,
"low": 460.2,
"open": 462.0,
"volume": 25000000,
"timestamp": "2026-06-12 14:30:00",
}])
print(f"推送结果: {result}")
if result.get("status") == "ok":
print("✅ 推送成功")
else:
print(f"❌ 失败: {result.get('message')}")
@@ -0,0 +1,49 @@
"""通达信连接测试 — 验证能否连上招商证券服务器
用法:
python tests/test_tdx_connect.py
"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
try:
import opentdx
print(f"✅ opentdx {opentdx.__version__}")
except ImportError:
print("❌ 请先 pip install opentdx")
sys.exit(1)
def test_server(ip, port, name="未知"):
print(f"\n🔄 {name}: {ip}:{port}")
try:
from opentdx.tdxClient import TdxClient
from opentdx.const import EX_MARKET
with TdxClient() as client:
# A股
aq = client.stock_quotes([(0, '000001')])
if aq:
print(f" ✅ A股 平安银行: {aq[0].get('price', '?')}")
# 港股
hk = client.goods_quotes([(EX_MARKET.HK_MAIN_BOARD, '00700')])
if hk:
q = hk[0]
print(f" ✅ 港股 腾讯: {q.get('price', '?')} ({q.get('change_pct', '?')}%)")
else:
print(" ❌ 港股无数据")
except Exception as e:
print(f"{e}")
if __name__ == "__main__":
from relay.config import MARKET_SERVERS
if MARKET_SERVERS:
for name, ip, port in MARKET_SERVERS:
test_server(ip, port, name)
else:
print("⚠️ config.py 中 MARKET_SERVERS 为空")
print("请在招商证券客户端 → 通信设置 查看IP后填入")