Compare commits
21 Commits
b421945b16
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 05d0b7524f | |||
| 8ed87a23e9 | |||
| c9a7dee7ff | |||
| 794ca61358 | |||
| 11aff71415 | |||
| e1dc4595a2 | |||
| fa763bd968 | |||
| 9bce381497 | |||
| 227d8b515f | |||
| c40b4b919c | |||
| 434acb0470 | |||
| 1a70a60dba | |||
| 5cacb62d4c | |||
| d5648e0b2c | |||
| c291f331fc | |||
| 1faeff4ee9 | |||
| eb096d915d | |||
| abc436514a | |||
| b67dde0427 | |||
| 4abec850a3 | |||
| 6c54a8ae95 |
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.11-slim
|
FROM python:3.13.11-slim
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
# FG2609 席位资金分析报告(2026-07-20 → 2026-07-24)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 席位分类
|
||||||
|
|
||||||
|
| 分类 | 席位 |
|
||||||
|
|------|------|
|
||||||
|
| 家人(散户) | 东方财富、徽商期货、方正中期、中信建投、平安期货 |
|
||||||
|
| 大户(产业/套保) | 永安期货、中信期货、国泰君安、海通期货、华泰期货、高盛期货、乾坤期货、摩根大通、中粮期货 |
|
||||||
|
| 噪音(忽略) | 东证期货 |
|
||||||
|
| 其他 | 未归入以上三类的席位 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、家人席位动向(散户情绪)
|
||||||
|
|
||||||
|
| 日期 | 多单增减 | 空单增减 | 当日净多 |
|
||||||
|
|------|---------|---------|----------|
|
||||||
|
| 07-20 | -0.66万 | +0.75万 | -1.41万 |
|
||||||
|
| 07-21 | -3.38万 | -0.04万 | -3.34万 |
|
||||||
|
| 07-22 | -1.60万 | +0.50万 | -2.10万 |
|
||||||
|
| 07-23 | +0.83万 | -0.44万 | +1.27万 |
|
||||||
|
| 07-24 | -1.89万 | -0.30万 | -1.59万 |
|
||||||
|
|
||||||
|
**核心结论:散户持续看空,减多为主。**
|
||||||
|
|
||||||
|
- 5 日累计减多 **-6.69 万**,累计加空 **+0.46 万**,净多变化 **-7.15 万**。
|
||||||
|
- 唯一一天净多(07-23)量级很小(+1.27 万),第二天立刻转回净空,说明散户整体偏空且缺乏持续性信心。
|
||||||
|
- 07-21 单日减多 **-3.38 万**,是当周家人席位减多最大的一天。这一天跳空低开、放量下杀,散户在恐慌中止损多单——典型的追跌杀多行为。
|
||||||
|
- 家人席位里,方正中期、中信建投、徽商期货、东方财富、平安期货五个席位多单在 5 日内全部下降,空单方向仅中信建投在 07-20 和 07-22 有加空动作。
|
||||||
|
|
||||||
|
**定性:散户情绪整体偏空,在下跌中减多出逃,属于典型的情绪化交易。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、大户席位动向(产业/套保资金)
|
||||||
|
|
||||||
|
| 日期 | 多单增减 | 空单增减 | 当日净多 |
|
||||||
|
|------|---------|---------|----------|
|
||||||
|
| 07-20 | +0.13万 | -1.26万 | +1.39万 |
|
||||||
|
| 07-21 | +2.14万 | -1.93万 | +4.07万 |
|
||||||
|
| 07-22 | -2.10万 | -6.67万 | +4.57万 |
|
||||||
|
| 07-23 | -0.52万 | -1.56万 | +1.04万 |
|
||||||
|
| 07-24 | -0.29万 | -1.50万 | +1.21万 |
|
||||||
|
|
||||||
|
**核心结论:大户持续大规模减空,是本周最重要的资金信号。**
|
||||||
|
|
||||||
|
- 5 日累计减空 **-12.93 万**,减多仅 **-0.65 万**,净多变化 **+12.28 万**。
|
||||||
|
- 每天都是净多方向(大户每一天的空单减持量都大于多单减持量),全周无一天例外。
|
||||||
|
- **07-22(周三)是标志性节点**:大户单日减空 **-6.67 万**,是当周最大单日空头撤离。当天高盛减空 -1.49 万、中信减空 -2.20 万、国泰君安减空 -0.93 万,三家头部大户同步砍空单。
|
||||||
|
|
||||||
|
### 关键大户席位拆解
|
||||||
|
|
||||||
|
**空头三巨头(中信、高盛、国泰君安):**
|
||||||
|
|
||||||
|
| 席位 | 07-20 空单 | 07-24 空单 | 5日减空 |
|
||||||
|
|------|-----------|-----------|---------|
|
||||||
|
| 中信期货 | 21.61万 | 18.07万 | **-3.54万** |
|
||||||
|
| 高盛期货 | 21.93万 | 17.81万 | **-4.12万** |
|
||||||
|
| 国泰君安 | 18.81万 | 17.47万 | **-1.34万** |
|
||||||
|
|
||||||
|
三家合计减空 **9.00 万**,占了全部大户减空量的 70%。这不是散户式的小打小闹,是主力资金在系统性地撤退空头头寸。
|
||||||
|
|
||||||
|
**定性:大户资金明确偏多。产业/套保盘的空头大幅回补,说明产业端认为当前价位已不具备继续做空的安全边际,或是交割月临近主动平空离场。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、多空力量对比
|
||||||
|
|
||||||
|
| 分类 | 5日多单累计 | 5日空单累计 | 净多(多-空)| 方向 |
|
||||||
|
|------|------------|------------|-------------|------|
|
||||||
|
| 家人 | -6.69万 | +0.46万 | **-7.15万** | 偏空 |
|
||||||
|
| 大户 | -0.65万 | -12.93万 | **+12.28万** | 强烈偏多 |
|
||||||
|
| 其他 | -4.24万 | -3.63万 | **-0.62万** | 略偏空 |
|
||||||
|
| **合计** | -11.58万 | -16.10万 | **+4.52万** | 偏多 |
|
||||||
|
|
||||||
|
**核心结论:大户的净多力量(+12.28 万)远超家人(-7.15 万)和其他席位(-0.62 万)的净空力量之和。**
|
||||||
|
|
||||||
|
- 全市场合计净多 **+4.52 万**,由大户独力支撑。
|
||||||
|
- 家人与其他席位合计净空 **-7.77 万**,与大户形成明确对手盘——大户在接散户抛出的多单、同时平掉自己的空单,典型的"聪明钱吃傻钱"格局。
|
||||||
|
- 东证期货被定义为噪音席位已排除,不影响结论。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、异常信号
|
||||||
|
|
||||||
|
### 1. 07-22(周三)空头集体溃退
|
||||||
|
|
||||||
|
大户单日减空 **-6.67 万**,是 5 日均值(-2.59 万)的 2.6 倍。中信 -2.20 万、高盛 -1.49 万、国泰 -0.93 万同时大幅减空。当天成交量 Top 1 的国泰君安成交达 38.13 万手(本周最高),放量明显。这一天出现了明确的空头踩踏信号。
|
||||||
|
|
||||||
|
### 2. 07-21(周二)散户与大户严重分歧
|
||||||
|
|
||||||
|
家人减多 **-3.38 万**(全周最大单日减多),大户加多 **+2.14 万**(全周最大单日加多)。一方恐慌出逃,一方逆势加仓。这是本周最典型的"对手盘"交易日。如果当天价格下跌,则大户在低位吸筹;如果价格上涨,则散户在反弹中解套离场——无论哪种,都是大户占优。
|
||||||
|
|
||||||
|
### 3. 冠通期货(其他席位)异动
|
||||||
|
|
||||||
|
07-22 成交 +4.23 万(从名不见经传到跻身 Top 13),07-23 再度 +4.78 万成交并进入 Top 6。这个席位两天成交暴增近 10 万手,背后可能有大资金借道入场,值得后续关注。
|
||||||
|
|
||||||
|
### 4. 永安期货多单逆势增加
|
||||||
|
|
||||||
|
永安 5 日多单从 4.38 万 → 4.53 万 → 4.56 万 → 4.99 万 → 5.19 万,累计加多 **+0.81 万**。同时空单从 6.41 万减至 4.62 万(-1.79 万)。永安的"多增空减"在大户中最为突出,是本周方向最明确的偏多席位。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、行情预判
|
||||||
|
|
||||||
|
### 判断:偏多,短期看反弹
|
||||||
|
|
||||||
|
**理由:**
|
||||||
|
|
||||||
|
1. **大户资金面强烈偏多**:5 日净多 +12.28 万,头部空头(中信、高盛、国泰)合计减空 9 万手,空头回补动能远未耗尽。如果这些席位还有空头敞口,下周可能继续平空,形成持续买盘。
|
||||||
|
|
||||||
|
2. **散户是反向指标**:家人席位 5 日净空 -7.15 万,在下跌中恐慌减多,符合散户在底部区域的行为特征。散户出清往往是阶段性底部的必要条件。
|
||||||
|
|
||||||
|
3. **对手盘格局明显**:大户接散户的盘,且大户是主动方(减空 + 稳多),散户是被动方(减多),这是典型的筑底资金结构。
|
||||||
|
|
||||||
|
4. **永安、国泰君安等头部席位方向一致**:多家大户多单稳定、空单持续下降,资金面没有分歧。
|
||||||
|
|
||||||
|
### 风险提示
|
||||||
|
|
||||||
|
- 全周减多总量(-11.58 万)也很大,说明多头信心整体不足。大户更多是"不空了"而非"大力做多",反弹力度取决于后续是否有新增多单入场。
|
||||||
|
- 冠通期货的异常放量可能代表新的空头力量进场,需要观察其后续持仓方向。
|
||||||
|
- 如果下一交易日价格反弹后大户开始减多,则说明这只是一轮空头回补行情,而非趋势反转。
|
||||||
|
|
||||||
|
**综合判断:偏多(概率 65%),预计 FG2609 下一交易日震荡偏强,关注空头回补持续性和是否有增量多头入场。**
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
# FG2611 席位资金分析报告(2026-07-20 → 2026-07-24)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 席位分类
|
||||||
|
|
||||||
|
| 分类 | 席位 |
|
||||||
|
|------|------|
|
||||||
|
| 家人(散户) | 东方财富、徽商期货、方正中期、中信建投、平安期货 |
|
||||||
|
| 大户(产业/套保) | 永安期货、中信期货、国泰君安、海通期货、华泰期货、高盛期货、乾坤期货、摩根大通、中粮期货 |
|
||||||
|
| 噪音(忽略) | 东证期货 |
|
||||||
|
| 其他 | 未归入以上三类的席位 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、家人席位动向(散户情绪)
|
||||||
|
|
||||||
|
| 日期 | 多单增减 | 空单增减 | 当日净多 |
|
||||||
|
|------|---------|---------|----------|
|
||||||
|
| 07-20 | -0.06万 | -0.15万 | +0.09万 |
|
||||||
|
| 07-21 | -0.00万 | +0.01万 | -0.01万 |
|
||||||
|
| 07-22 | +0.07万 | -0.04万 | +0.11万 |
|
||||||
|
| 07-23 | +0.02万 | -0.06万 | +0.08万 |
|
||||||
|
| 07-24 | -0.13万 | -0.06万 | -0.07万 |
|
||||||
|
|
||||||
|
**核心结论:家人席位参与度极低,无明确方向性。**
|
||||||
|
|
||||||
|
- 5 日累计减多 **-0.10 万**,累计减空 **-0.30 万**,净多变化 **+0.20 万**。
|
||||||
|
- 每日变动量级在数百手级别,与 FG2609 主连合约上万手的波动完全不同。FG2611 是远月合约,散户参与度天然较低。
|
||||||
|
- 无追涨杀跌迹象——单日最大变动仅 -0.13 万(07-24 减多),远不足以构成情绪判断。
|
||||||
|
- 家人持仓绝对量也极小:中信建投是唯一多空持仓均超 2 万的家人席位,东方财富多单仅 0.32 万、空单未进 Top 20。
|
||||||
|
|
||||||
|
**定性:远月合约散户参与度极低,家人席位资金信号不具备参考价值。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、大户席位动向(产业/套保资金)
|
||||||
|
|
||||||
|
| 日期 | 多单增减 | 空单增减 | 当日净多 |
|
||||||
|
|------|---------|---------|----------|
|
||||||
|
| 07-20 | -0.68万 | -0.31万 | -0.37万 |
|
||||||
|
| 07-21 | -0.10万 | +0.14万 | -0.24万 |
|
||||||
|
| 07-22 | -0.28万 | +0.20万 | -0.48万 |
|
||||||
|
| 07-23 | +0.98万 | +0.42万 | +0.56万 |
|
||||||
|
| 07-24 | +0.41万 | +0.30万 | +0.11万 |
|
||||||
|
|
||||||
|
**核心结论:前三天减多偏空,后两天显著反转加多,方向切换明显。**
|
||||||
|
|
||||||
|
- 5 日累计加多 **+0.33 万**,加空 **+0.76 万**,净多变化 **-0.43 万**。总体略偏空,但主要是前三天贡献的。
|
||||||
|
- **07-23(周四)是转折点**:大户加多 +0.98 万,是 5 日最大单日加多。华泰期货加多 +0.74 万(从 0.18 万跳升至 0.92 万,增量惊人)、中粮期货加多 +0.32 万。
|
||||||
|
- 空头方面,高盛期货持续加空:从 07-20 的 1.05 万 → 07-21 的 1.20 万 → 07-22 的 1.76 万 → 07-23 的 1.91 万 → 07-24 的 2.01 万,5 日累计加空 **+0.96 万**,是大户中唯一持续单向加空的席位。
|
||||||
|
- 国泰君安 07-24 多单 +0.26 万、空单 +0.32 万,双边大幅加仓,方向不明确但参与度在提升。
|
||||||
|
|
||||||
|
### 关键大户席位拆解
|
||||||
|
|
||||||
|
**中粮期货——本周最活跃的大户席位:**
|
||||||
|
|
||||||
|
| 指标 | 07-20 | 07-21 | 07-22 | 07-23 | 07-24 |
|
||||||
|
|------|-------|-------|-------|-------|-------|
|
||||||
|
| 成交量 | 5.05万 | 2.81万 | 5.62万 | 7.48万 | 1.65万 |
|
||||||
|
| 多单 | 1.48万 | 1.16万 | 1.43万 | 1.75万 | 1.79万 |
|
||||||
|
| 空单 | 1.46万 | 1.14万 | 1.39万 | 1.73万 | 1.73万 |
|
||||||
|
|
||||||
|
中粮在 FG2611 上多空双向加仓,多单从 1.48 万增至 1.79 万(+0.31 万),空单从 1.46 万增至 1.73 万(+0.27 万)。**这是典型的套利/套保盘口——多空同步增加,而非单纯做方向。**
|
||||||
|
|
||||||
|
**华泰期货 07-23 异动:**
|
||||||
|
|
||||||
|
华泰多单从 0.18 万(07-22)暴增至 0.92 万(07-23),单日加多 **+0.74 万**,增幅超过 400%。同时成交量从 0.42 万放大至 1.22 万。这是一个非常突兀的增仓,值得重点关注后续是否持续。
|
||||||
|
|
||||||
|
**定性:大户整体略偏空(净多 -0.43 万),但后半周出现方向切换迹象。高盛是唯一坚定的空头加仓者,中粮以套利盘为主,华泰异动值得追踪。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、多空力量对比
|
||||||
|
|
||||||
|
| 分类 | 5日多单累计 | 5日空单累计 | 净多(多-空)| 方向 |
|
||||||
|
|------|------------|------------|-------------|------|
|
||||||
|
| 家人 | -0.10万 | -0.30万 | **+0.20万** | 微偏多 |
|
||||||
|
| 大户 | +0.33万 | +0.76万 | **-0.43万** | 略偏空 |
|
||||||
|
| 其他 | -0.72万 | -1.20万 | **+0.48万** | 偏多 |
|
||||||
|
| **合计** | -0.49万 | -0.74万 | **+0.25万** | 微偏多 |
|
||||||
|
|
||||||
|
**核心结论:FG2611 整体资金量级较小,市场方向不明确。**
|
||||||
|
|
||||||
|
- 全市场净多仅 **+0.25 万**,与 FG2609 的 +4.52 万形成鲜明对比。远月合约的资金博弈强度远低于主连。
|
||||||
|
- 三大分类之间无明显对手盘关系——家人偏多(+0.20 万)、大户偏空(-0.43 万)、其他偏多(+0.48 万),但量级都太小,无法形成有效信号。
|
||||||
|
- 其他席位(+0.48 万)贡献了最大的净多力量,但这些席位分散、方向一致性弱。
|
||||||
|
|
||||||
|
**定性:远月合约流动性差,各类资金均未展现出明确的战略方向,不宜据此做出方向判断。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、异常信号
|
||||||
|
|
||||||
|
### 1. 07-23(周四)大户方向逆转
|
||||||
|
|
||||||
|
前半周大户减多为主(三日累计 -1.06 万),07-23 突然加多 +0.98 万。华泰期货一天加多 +0.74 万是主要推手。这种突然的方向切换值得警惕——可能是某个产业户判断价格到位后集中建仓多单。
|
||||||
|
|
||||||
|
### 2. 高盛期货持续单向加空
|
||||||
|
|
||||||
|
高盛空单每日递增:1.05 → 1.20 → 1.76 → 1.91 → 2.01 万,5 日累计 +0.96 万,且多单从不在 Top 20。在所有大户席位中,高盛是唯一一个方向完全一致的席位——**只做空、不加多**。这在 FG2609 高盛大规模减空的背景下显得尤为突兀:主连上高盛在撤退,远月上高盛在加码。需要关注这是跨期套利(空 11 多 09)还是纯粹的远月看空。
|
||||||
|
|
||||||
|
### 3. 07-24(周五)中粮成交量骤降
|
||||||
|
|
||||||
|
中粮成交量从 07-23 的 7.48 万骤降至 07-24 的 1.65 万(-5.82 万),降幅居全席位之首。而当天中粮多空持仓几乎未变(多 +0.04 万、空 +0.00 万)。这说明中粮在周四大幅建仓后,周五选择锁仓不动,符合套保盘的交易特征。
|
||||||
|
|
||||||
|
### 4. 华泰期货多单暴增
|
||||||
|
|
||||||
|
07-23 华泰多单从 0.18 万跳升至 0.92 万(+0.74 万),而 07-22 华泰多单甚至不在 Top 20(意味持仓低于 0.27 万)。也就是说在两天内华泰从几乎零多单变成了近万手多单。这是一个需要持续追踪的信号——如果后续继续加仓,可能预示产业资金在远月布局。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、行情预判
|
||||||
|
|
||||||
|
### 判断:震荡,方向不明
|
||||||
|
|
||||||
|
**理由:**
|
||||||
|
|
||||||
|
1. **资金量级太小**:全周净多仅 +0.25 万,各项资金变动均不足以支撑方向判断。远月合约天然流动性差,席位分析的有效性大幅下降。
|
||||||
|
|
||||||
|
2. **大户内部存在分歧**:高盛持续单向加空(+0.96 万),但华泰(+0.74 万)和中粮(+0.31 万)在加多。同为产业/套保大户,方向截然相反,说明产业端对远月定价存在明显分歧。
|
||||||
|
|
||||||
|
3. **与 FG2609 的方向背离**:FG2609 大户强烈偏多(净多 +12.28 万),而 FG2611 大户略偏空(净多 -0.43 万)。高盛在 2609 上减空 -4.12 万,在 2611 上加空 +0.96 万——这可能是跨期套利,而非单纯看多或看空。
|
||||||
|
|
||||||
|
4. **缺乏家人席位验证**:远月合约散户参与度极低,无法通过"家人—大户对手盘"框架来验证信号。缺少了一个重要的判断维度。
|
||||||
|
|
||||||
|
### 参考建议
|
||||||
|
|
||||||
|
- **以 FG2609 为主、FG2611 为辅**:主连合约资金博弈充分,信号更可靠。FG2611 当前阶段更适合作为跨期价差分析的参考,而非独立的方向判断依据。
|
||||||
|
- **重点关注华泰期货后续动向**:如果华泰在 FG2611 上继续加多,同时 FG2609 方向一致,则强化偏多判断。
|
||||||
|
- **关注高盛的跨期行为**:高盛在 2609 减空、2611 加空,如果这一模式持续,可能是"近强远弱"的跨期结构信号。
|
||||||
|
|
||||||
|
**综合判断:FG2611 自身资金面无法给出有效方向,建议以 FG2609 的分析结论为主(偏多),FG2611 跟随主连波动。远月合约暂以震荡看待。**
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
# FG2701 席位资金分析报告(2026-07-20 → 2026-07-24)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 席位分类
|
||||||
|
|
||||||
|
| 分类 | 席位 |
|
||||||
|
|------|------|
|
||||||
|
| 家人(散户) | 东方财富、徽商期货、方正中期、中信建投、平安期货 |
|
||||||
|
| 大户(产业/套保) | 永安期货、中信期货、国泰君安、海通期货、华泰期货、高盛期货、乾坤期货、摩根大通、中粮期货 |
|
||||||
|
| 噪音(忽略) | 东证期货 |
|
||||||
|
| 其他 | 未归入以上三类的席位 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、家人席位动向(散户情绪)
|
||||||
|
|
||||||
|
| 日期 | 多单增减 | 空单增减 | 当日净多 |
|
||||||
|
|------|---------|---------|----------|
|
||||||
|
| 07-20 | +0.08万 | +0.61万 | -0.53万 |
|
||||||
|
| 07-21 | +0.06万 | 0.00万 | +0.06万 |
|
||||||
|
| 07-22 | +0.20万 | +0.45万 | -0.25万 |
|
||||||
|
| 07-23 | +0.10万 | -0.22万 | +0.32万 |
|
||||||
|
| 07-24 | -0.02万 | -0.18万 | +0.16万 |
|
||||||
|
|
||||||
|
**核心结论:家人席位多空双增,但空单增幅更大,整体略偏空。**
|
||||||
|
|
||||||
|
- 5 日累计加多 **+0.43 万**,加空 **+0.65 万**,净多变化 **-0.23 万**。
|
||||||
|
- 前半周(07-20、07-22)偏空,后半周(07-23、07-24)偏多,方向摇摆不定。
|
||||||
|
- **07-20(周一)是家人加空最多的一天**:+0.61 万,全部来自中信建投单日加空 **+0.56 万**。中信建投空单从 0.80 万跳升至 1.50 万(07-22 最高点),后半周又回落到 1.07 万。这不是散户行为,更像是中信建投自营或大户借道这个席位做空了远月,因为散户不太可能一天加 5600 手空单。
|
||||||
|
- 方正中期是家人中多单最大的席位(1.03→1.03→1.08→1.03→1.06 万),5 日几乎未变,方向中性。
|
||||||
|
- 东方财富和平安期货持仓量极小(0.3 万级别),纯散户参与度极低。
|
||||||
|
|
||||||
|
**定性:FG2701 远月合约家人席位参与度低,且中信建投的"家人"分类在此合约上可能失真——其 07-20 单日加空 0.56 万的行为更像是大户级的操作。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、大户席位动向(产业/套保资金)
|
||||||
|
|
||||||
|
| 日期 | 多单增减 | 空单增减 | 当日净多 |
|
||||||
|
|------|---------|---------|----------|
|
||||||
|
| 07-20 | +0.15万 | +0.27万 | -0.12万 |
|
||||||
|
| 07-21 | -0.76万 | 0.00万 | -0.76万 |
|
||||||
|
| 07-22 | -0.25万 | -0.28万 | +0.03万 |
|
||||||
|
| 07-23 | -0.24万 | +0.68万 | -0.92万 |
|
||||||
|
| 07-24 | +0.12万 | -0.16万 | +0.28万 |
|
||||||
|
|
||||||
|
**核心结论:大户显著偏空,是三个合约中大户方向最明确的空头。**
|
||||||
|
|
||||||
|
- 5 日累计减多 **-0.97 万**,加空 **+0.51 万**,净多变化 **-1.48 万**。
|
||||||
|
- 5 天中有 3 天净空,且 07-21(-0.76 万)和 07-23(-0.92 万)两天的净空量级明显大于另外三天的波动幅度。
|
||||||
|
- 与 FG2609(大户净多 +12.28 万)和 FG2611(大户净多 -0.43 万)形成渐进式对比:**越远的合约,大户越偏空**。
|
||||||
|
|
||||||
|
### 关键大户席位拆解
|
||||||
|
|
||||||
|
**国泰君安——FG2701 的"超级空头":**
|
||||||
|
|
||||||
|
| 指标 | 07-20 | 07-21 | 07-22 | 07-23 | 07-24 |
|
||||||
|
|------|-------|-------|-------|-------|-------|
|
||||||
|
| 多单 | 1.40万 | 1.43万 | 1.50万 | 1.32万 | 1.39万 |
|
||||||
|
| 空单 | **4.42万** | — | 4.15万 | **4.46万** | **4.54万** |
|
||||||
|
|
||||||
|
国泰君安的空单(4.54 万)是它多单(1.39 万)的 **3.3 倍**,占大户空单总量的近 50%,占全市场空单的约 30%。在 FG2701 这个流动性远不如主连的远月合约上,单一席位持有如此庞大的空头敞口,非常不寻常。
|
||||||
|
|
||||||
|
这个席位在三个合约上的行为完全不同:
|
||||||
|
- FG2609:空单 18.81 → 17.47 万(减空 -1.34 万),**偏多**
|
||||||
|
- FG2611:多空双向,方向模糊
|
||||||
|
- FG2701:空单 4.42 → 4.54 万(加空 +0.12 万),**坚定偏空**
|
||||||
|
|
||||||
|
**这很可能不是同一类资金在操作三个合约**。FG2701 的国泰君安空头大概率是产业套保盘——玻璃企业锁定明年 1 月的卖出价格。
|
||||||
|
|
||||||
|
**高盛期货——锁仓不动:**
|
||||||
|
|
||||||
|
高盛空单全周维持在 1.70-1.71 万,几乎零变动。对比 FG2609 上高盛大幅减空 4.12 万、FG2611 上加空 0.96 万,FG2701 上的高盛选择了按兵不动。
|
||||||
|
|
||||||
|
**华泰期货多单持续缩水:**
|
||||||
|
|
||||||
|
华泰多单从 07-20 的 1.63 万 → 07-21 的 0.92 万(-0.71 万)→ 07-22/23/24 维持 0.87-0.91 万。华泰曾是 FG2701 上最大的多头,但 07-21 一天就砍掉了近一半的多单。这是一个明确的减多信号。
|
||||||
|
|
||||||
|
**永安期货多减空增:**
|
||||||
|
|
||||||
|
永安多单从 0.89 万 → 0.85 → 0.41 → 0.42 → 0.46 万(减多 -0.43 万),空单从 0.88 → 无 → 1.02 → 1.32 → 1.06 万(加空 +0.18 万)。方向明确偏空,与国泰君安形成呼应。
|
||||||
|
|
||||||
|
**定性:大户在 FG2701 上呈现一致的偏空布局。国泰君安的 4.54 万天量空单是核心信号,永安、华泰(减多方面)也指向同一方向。高盛虽然锁仓不动,但其在 FG2611 上的加空行为暗示整体偏空倾向。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、多空力量对比
|
||||||
|
|
||||||
|
| 分类 | 5日多单累计 | 5日空单累计 | 净多(多-空)| 方向 |
|
||||||
|
|------|------------|------------|-------------|------|
|
||||||
|
| 家人 | +0.43万 | +0.65万 | **-0.23万** | 略偏空 |
|
||||||
|
| 大户 | -0.97万 | +0.51万 | **-1.48万** | 明确偏空 |
|
||||||
|
| 其他 | +1.01万 | +1.77万 | **-0.75万** | 偏空 |
|
||||||
|
| **合计** | +0.47万 | +2.93万 | **-2.46万** | 明确偏空 |
|
||||||
|
|
||||||
|
**核心结论:FG2701 全市场三大分类一致偏空,是本周三个合约中方向最统一的空头合约。**
|
||||||
|
|
||||||
|
- 全市场净空 **-2.46 万**,与 FG2609 的 +4.52 万净多形成 180 度背离。
|
||||||
|
- 其他席位是最大的空头增量来源(+1.77 万空单),但分散在多个席位上,方向一致性不如大户。
|
||||||
|
- 没有人做多:大户减多(-0.97 万)而非加多,其他席位加多(+1.01 万)但加空更多(+1.77 万),家人加多(+0.43 万)但加空同样更多(+0.65 万)。
|
||||||
|
|
||||||
|
### 三大合约方向对比
|
||||||
|
|
||||||
|
| 合约 | 大户净多 | 总净多 | 方向 |
|
||||||
|
|------|---------|--------|------|
|
||||||
|
| FG2609(近月) | +12.28万 | +4.52万 | **强烈偏多** |
|
||||||
|
| FG2611(次月) | -0.43万 | +0.25万 | 震荡 |
|
||||||
|
| FG2701(远月) | -1.48万 | -2.46万 | **明确偏空** |
|
||||||
|
|
||||||
|
**这构成了一条清晰的期限结构:近强远弱,越远期越偏空。** 这符合商品期货在 contango/backwardation 结构下的常见特征——近月受现货支撑偏强,远月因产业套保盘(玻璃厂卖出远期产量)而承压。
|
||||||
|
|
||||||
|
**定性:FG2701 全市场一致偏空,核心驱动力是大户套保空头(国泰君安 4.54 万)和其他席位的分散加空。这并非市场情绪的"看空",更像是产业资金利用远月合约进行卖出套保的正常操作。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、异常信号
|
||||||
|
|
||||||
|
### 1. 国泰君安的"超级空头"是 FG2701 的定价锚
|
||||||
|
|
||||||
|
一个席位在远月合约上持有 4.54 万空单、多单仅 1.39 万,净空 3.15 万。这个量级在整个 FG2701 市场中占比极大。**如果国泰君安这个空头开始平仓(买回),会对价格形成巨大上推力。** 这是 FG2701 最大的潜在多空催化剂。
|
||||||
|
|
||||||
|
### 2. 07-24(周五)其他席位空单集中暴增
|
||||||
|
|
||||||
|
其他席位空单 07-24 单日 +1.33 万,是 5 日最大单日增幅。主要贡献者:
|
||||||
|
- 国投期货空单 +0.54 万(从 0.39 万跳至 0.93 万)
|
||||||
|
- 广发期货空单 +0.46 万(从 0.38 万跳至 0.84 万)
|
||||||
|
- 瑞达期货空单 +0.23 万(从 0.68 万增至 0.91 万)
|
||||||
|
|
||||||
|
三家合计加空 1.23 万。周五通常是减仓日(规避周末风险),但这三家反而在周五尾盘加空,值得关注。
|
||||||
|
|
||||||
|
### 3. 07-21(周二)华泰多单崩盘式减仓
|
||||||
|
|
||||||
|
华泰期货多单从 1.63 万暴跌至 0.92 万(-0.71 万),单日减幅 44%。当天华泰成交量 1.62 万(较前一日的 0.58 万放大近 3 倍),但多单大减、空单未变——说明是单向平多出逃。作为 FG2701 原本的第一大多头,华泰的突然撤退是重要的偏空信号。
|
||||||
|
|
||||||
|
### 4. 中信建投的异常空单操作(07-20 和 07-22)
|
||||||
|
|
||||||
|
中信建投空单:07-20 为 0.80 万,07-20 当天 +0.56 万 → 07-22 继续增至 1.50 万 → 07-23 降至 1.32 万 → 07-24 再降至 1.07 万。先猛加后缓减,这种操作模式更像是机构行为而非散户行为。将其归入"家人"分类在 FG2701 上可能不准确。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、行情预判
|
||||||
|
|
||||||
|
### 判断:偏空,但不宜追空(空头已高度拥挤)
|
||||||
|
|
||||||
|
**理由:**
|
||||||
|
|
||||||
|
1. **方向一致但过于拥挤**:全市场三大分类全部偏空,总净空 -2.46 万。国泰君安一家就净空 3.15 万。当一个方向上所有人都已入场,反向的边际力量反而更大——任何风吹草动都可能触发空头回补。
|
||||||
|
|
||||||
|
2. **产业套保逻辑清晰**:FG2701 对应明年 1 月交割,是玻璃消费淡季。玻璃厂在远月合约做卖出套保(锁定销售价格)是合理的产业行为。国泰君安的 4.54 万空单大概率是产业客户的套保盘,这类资金通常持有到期,不会频繁交易。所以这 4.54 万更像是"压舱石"而非"投机空头"。
|
||||||
|
|
||||||
|
3. **跨期结构已定价**:近强远弱(FG2609 强多 → FG2701 强空)的期限结构已经非常清晰。如果你已经做空 FG2701,这个结构已经 price in。真正的边际变化要看:
|
||||||
|
- 近月 FG2609 的空头回补能否带动远月跟涨
|
||||||
|
- 国泰君安是否会在 FG2701 上继续加空
|
||||||
|
|
||||||
|
4. **周五空头集中加仓可能是尾部信号**:07-24 其他席位集中加空 +1.33 万,如果这是新空头的"最后一波"入场,那么空头动能可能即将衰竭。
|
||||||
|
|
||||||
|
### 情景推演
|
||||||
|
|
||||||
|
| 情景 | 触发条件 | FG2701 走势 |
|
||||||
|
|------|---------|------------|
|
||||||
|
| 基准(60%) | FG2609 继续反弹,国泰君安维持空单 | 震荡偏弱,跟随主连但涨幅受限 |
|
||||||
|
| 空头回补(25%) | FG2609 大幅反弹,国泰君安开始平 FG2701 空单 | 涨幅可能超过 FG2609(空头踩踏) |
|
||||||
|
| 继续加空(15%) | 市场情绪恶化,产业端加大套保力度 | 独立走弱,跌幅大于 FG2609 |
|
||||||
|
|
||||||
|
**综合判断:偏空(概率 60%),但风险收益比已不适合追空。FG2701 的空头过于拥挤,一旦触发回补行情,反弹速度和幅度可能超预期。建议以观望为主,若持有空单则设好止盈,若空仓则等待更明确的信号。FG2609 的偏多判断和 FG2701 的偏空判断并不矛盾——它们反映的是同一品种不同到期月份的合理价差结构。**
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
# 玻璃期货综合持仓与成交量分析(2026-07-27 盘前)
|
||||||
|
|
||||||
|
基于 FG2609/FG2611/FG2701 三份席位分析报告,对下个交易日(07-27 周一)的行情推演。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、三合约核心数据总览
|
||||||
|
|
||||||
|
| 指标 | FG2609(主连) | FG2611(次月) | FG2701(远月) |
|
||||||
|
|------|:------------:|:------------:|:------------:|
|
||||||
|
| 大户 5 日净多 | **+12.28 万** | -0.43 万 | -1.48 万 |
|
||||||
|
| 家人 5 日净多 | -7.15 万 | +0.20 万 | -0.23 万 |
|
||||||
|
| 全市场净多 | **+4.52 万** | +0.25 万 | **-2.46 万** |
|
||||||
|
| 方向 | **强烈偏多** | 震荡 | 明确偏空 |
|
||||||
|
| 主力信号 | 空头踩踏回补 | 流动性不足 | 产业套保压盘 |
|
||||||
|
|
||||||
|
**期限结构清晰:近强远弱,逐月递减。FG2609 是行情发动机,FG2611 跟随,FG2701 独立承压。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、交易量分析:放量集中在近月,空头回补是主要成交来源
|
||||||
|
|
||||||
|
**FG2609 成交量是核心。** 07-22(周三)国泰君安单日成交 38.13 万手,为全周最高,当天大户减空 -6.67 万手,中信 -2.20 万、高盛 -1.49 万、国泰 -0.93 万同步砍空。这是典型的**空头踩踏式放量**——主要成交不是新增多头在买,而是老空头在抢着平仓。
|
||||||
|
|
||||||
|
FG2611 的成交也佐证了"量在近月":中粮期货 07-23 成交 7.48 万手(周最高),07-24 骤降至 1.65 万手后锁仓不动,套保盘集中建仓后收手。
|
||||||
|
|
||||||
|
**FG2701 周五出现反常放量**——其他席位(国投 +0.54 万、广发 +0.46 万、瑞达 +0.23 万)合计加空 1.23 万手。周五通常是避险减仓日,此时加空说明有空头在押注周一继续下跌,这构成周一潜在的**空头拥挤风险**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、持仓结构分析:三合约构成跨期对手盘矩阵
|
||||||
|
|
||||||
|
**最核心的逻辑不在单个合约内部,而在三个合约之间的资金联动:**
|
||||||
|
|
||||||
|
### 高盛期货——跨期套利布局
|
||||||
|
|
||||||
|
| 合约 | 行为 | 5 日变动 |
|
||||||
|
|------|------|---------|
|
||||||
|
| FG2609 | **大幅减空** | -4.12 万 |
|
||||||
|
| FG2611 | **持续加空** | +0.96 万 |
|
||||||
|
| FG2701 | 锁仓不动 | ~0 |
|
||||||
|
|
||||||
|
高盛在做 **"多近月、空远月"的跨期套利**,不是单边看多或看空。它认为近月(09)被低估、远月(11)相对高估,近强远弱的价差会继续扩大。
|
||||||
|
|
||||||
|
### 国泰君安——三合约三个角色
|
||||||
|
|
||||||
|
| 合约 | 角色 | 持仓特征 |
|
||||||
|
|------|------|---------|
|
||||||
|
| FG2609 | 空头回补者 | 空单 18.81→17.47 万,减空 -1.34 万 |
|
||||||
|
| FG2611 | 双边加仓 | 多空同步增加,方向模糊 |
|
||||||
|
| FG2701 | **超级空头** | 空单 4.54 万,是多单的 3.3 倍 |
|
||||||
|
|
||||||
|
国泰君安在 FG2701 上 4.54 万空单占全市场约 30%,这大概率是产业套保盘(玻璃厂锁定明年 1 月卖出价格),持有到期、不频繁交易,是 FG2701 的长期压舱石。
|
||||||
|
|
||||||
|
### 永安期货——唯一的多空同向偏多席位
|
||||||
|
|
||||||
|
永安在 FG2609 上:多单 5 日递增(4.38→4.53→4.56→4.99→5.19 万,+0.81 万),空单递减(6.41→4.62 万,-1.79 万)。**多增空减,在所有大户中方向最干净、最统一。**
|
||||||
|
|
||||||
|
### 空头三巨头剩余敞口
|
||||||
|
|
||||||
|
| 席位 | 07-24 空单 | 5 日减空 | 剩余空头敞口 |
|
||||||
|
|------|-----------|---------|------------|
|
||||||
|
| 中信期货 | 18.07 万 | -3.54 万 | 仍很大 |
|
||||||
|
| 高盛期货 | 17.81 万 | -4.12 万 | 仍很大 |
|
||||||
|
| 国泰君安 | 17.47 万 | -1.34 万 | 仍很大 |
|
||||||
|
| **合计** | **53.35 万** | **-9.00 万** | — |
|
||||||
|
|
||||||
|
三家合计仍持有 53.35 万空单,上周只减了 9 万。**如果反弹延续,空头回补的买盘还远未耗尽。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、下个交易日(07-27)行情推演
|
||||||
|
|
||||||
|
### FG2609(主连):偏多(概率 65%),看反弹延续
|
||||||
|
|
||||||
|
**做多力量来源:**
|
||||||
|
|
||||||
|
1. **空头回补动能远未耗尽**:三大空头仍有 53.35 万敞口。反弹延续 → 空头继续砍仓 → 形成持续买盘的逻辑链条仍在。
|
||||||
|
2. **永安在持续加多**:唯一在价格低位连续 5 日递增多单的大户,方向最明确。
|
||||||
|
3. **散户是反向指标**:家人 5 日净空 -7.15 万,在下跌中止损多单。散户出清 + 大户接盘 = 典型的筑底资金结构。
|
||||||
|
|
||||||
|
**抑制上涨的因素:**
|
||||||
|
|
||||||
|
- 大户更多是"不空了"而非"大力做多"(减空 -12.93 万 vs 减多仅 -0.65 万)。空头回补驱动的反弹,一旦空头平完、没有新增多头接力,反弹就会熄火。
|
||||||
|
- 冠通期货连续两天放量(+9 万手),持仓方向未知,可能是新的空头力量入场。
|
||||||
|
|
||||||
|
**情景判断:**
|
||||||
|
|
||||||
|
| 情景 | 概率 | 条件 | FG2609 走势 |
|
||||||
|
|------|:---:|------|------------|
|
||||||
|
| 基准 | 65% | 空头回补延续,成交量维持 | 震荡偏强,小幅上行 |
|
||||||
|
| 回调 | 25% | 反弹到位,空头停止回补 | 冲高回落,转震荡 |
|
||||||
|
| 加速上涨 | 10% | 增量多头入场 + 空头踩踏 | 放量大涨 |
|
||||||
|
|
||||||
|
### FG2611(次月):跟随主连震荡,无独立行情
|
||||||
|
|
||||||
|
流动性太差,全周净多仅 +0.25 万。唯一关注点:**华泰期货 07-23 多单暴增 +0.74 万**——如果周一华泰继续加多,是偏多信号;如果减仓,说明只是一日游。
|
||||||
|
|
||||||
|
### FG2701(远月):偏空但有回补风险,不建议追空
|
||||||
|
|
||||||
|
- 三类资金一致偏空,总净空 -2.46 万,方向最统一。
|
||||||
|
- **但空头已高度拥挤**:国泰一家净空 3.15 万 + 周五新空头集中入场(+1.23 万)。当一个方向所有人都已上船,反向的边际冲击最大。
|
||||||
|
- **核心变量是 FG2609**:如果主连大幅反弹触发远月空头回补,FG2701 涨幅可能超 FG2609(空头踩踏)。国泰的 4.54 万套保盘不会动,但投机空头会跑得很快。
|
||||||
|
|
||||||
|
| 情景 | 概率 | 触发条件 | FG2701 走势 |
|
||||||
|
|------|:---:|---------|------------|
|
||||||
|
| 基准 | 60% | FG2609 小幅反弹,国泰维持空单 | 震荡偏弱,涨幅受限 |
|
||||||
|
| 空头回补 | 25% | FG2609 大幅反弹,投机空头平仓 | 涨幅可能超 FG2609 |
|
||||||
|
| 继续加空 | 15% | 市场情绪恶化,产业加大套保 | 独立走弱 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、综合结论
|
||||||
|
|
||||||
|
> **下个交易日整体判断:偏多震荡。FG2609 是核心战场,空头回补是主要驱动。FG2701 的空头拥挤是最大的潜在爆发点——如果主连大幅上涨触发远月空头回补,可能出现近远月同步拉升的极端行情。反之,如果主连反弹乏力、成交量萎缩,则空头回补行情接近尾声,谨慎追多。**
|
||||||
|
|
||||||
|
### 周一盘中关键观察指标
|
||||||
|
|
||||||
|
1. **FG2609 成交量**:放量 = 空头继续砍仓(偏多),缩量 = 回补动能衰竭(转震荡)
|
||||||
|
2. **中信/高盛/国泰空单变化**(盘后数据):继续下降则反弹延续
|
||||||
|
3. **FG2701 是否跟涨**:跟涨说明空头回补扩散;不跟涨是正常期限结构
|
||||||
|
4. **华泰 FG2611 多单**:继续增加则强化远月翻多信号
|
||||||
|
5. **冠通期货持仓方向**:如果其方向为多,则强化偏多;如果为空,则是新的压力来源
|
||||||
@@ -28,6 +28,6 @@ def check_lock(
|
|||||||
return (current_price - open_price) >= lock_threshold
|
return (current_price - open_price) >= lock_threshold
|
||||||
|
|
||||||
|
|
||||||
def should_meltdown(lock_count: int) -> bool:
|
def should_meltdown(lock_count: int, max_locks: int = 3) -> bool:
|
||||||
"""3锁熔断"""
|
"""锁仓次数达到上限时熔断"""
|
||||||
return lock_count >= 3
|
return lock_count >= max_locks
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ class Round(Base):
|
|||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
contract_code: Mapped[str] = mapped_column(String(10), index=True)
|
contract_code: Mapped[str] = mapped_column(String(10), index=True)
|
||||||
daily_hands: Mapped[int] = mapped_column(Integer, default=1)
|
daily_hands: Mapped[int] = mapped_column(Integer, default=1)
|
||||||
|
max_locks: Mapped[int] = mapped_column(Integer, default=3)
|
||||||
status: Mapped[str] = mapped_column(String(20), default="active")
|
status: Mapped[str] = mapped_column(String(20), default="active")
|
||||||
started_at: Mapped[date] = mapped_column(Date)
|
started_at: Mapped[date] = mapped_column(Date)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,229 @@
|
|||||||
|
"""持仓分析 prompt 生成器。
|
||||||
|
|
||||||
|
根据最近 5 个交易日的主力持仓排名生成结构化分析 prompt,
|
||||||
|
按席位属性(家人/大户/其他/噪音)分类汇总,聚焦资金风向变化。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
from datetime import date, timedelta
|
||||||
|
|
||||||
|
from app.database import SessionLocal
|
||||||
|
from app.models import PositionRanking, Contract
|
||||||
|
|
||||||
|
|
||||||
|
# ── 席位分类 ──
|
||||||
|
FAMILY_NAMES = [
|
||||||
|
"东方财富", "徽商期货", "方正中期", "中信建投", "平安期货",
|
||||||
|
]
|
||||||
|
WHALE_NAMES = [
|
||||||
|
"永安期货", "中信期货", "国泰君安", "海通期货", "华泰期货",
|
||||||
|
"高盛期货", "乾坤期货", "摩根大通", "中粮期货",
|
||||||
|
]
|
||||||
|
NOISE_NAMES = ["东证期货"]
|
||||||
|
|
||||||
|
TYPE_LABEL = {"volume": "成交量", "long": "多单持仓", "short": "空单持仓"}
|
||||||
|
|
||||||
|
|
||||||
|
def _classify(inst: str) -> str:
|
||||||
|
"""匹配席位分类,返回 家人/大户/噪音/其他。"""
|
||||||
|
for kw in FAMILY_NAMES:
|
||||||
|
if kw in inst:
|
||||||
|
return "家人"
|
||||||
|
for kw in WHALE_NAMES:
|
||||||
|
if kw in inst:
|
||||||
|
return "大户"
|
||||||
|
for kw in NOISE_NAMES:
|
||||||
|
if kw in inst:
|
||||||
|
return "噪音"
|
||||||
|
return "其他"
|
||||||
|
|
||||||
|
|
||||||
|
def _build_legend() -> str:
|
||||||
|
"""生成席位分类图例。"""
|
||||||
|
lines = ["## 席位分类", ""]
|
||||||
|
lines.append(f"- **家人席位(散户)**: {'、'.join(FAMILY_NAMES)}")
|
||||||
|
lines.append(f"- **大户席位(产业/套保)**: {'、'.join(WHALE_NAMES)}")
|
||||||
|
lines.append(f"- **噪音席位(忽略)**: {'、'.join(NOISE_NAMES)}")
|
||||||
|
lines.append("- **其他席位**: 未归入以上三类的席位")
|
||||||
|
lines.append("")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_number(n: int) -> str:
|
||||||
|
"""数字格式化为万手。"""
|
||||||
|
v = n / 10000
|
||||||
|
sign = "+" if n > 0 else ""
|
||||||
|
return f"{sign}{v:.2f}万"
|
||||||
|
|
||||||
|
|
||||||
|
def build_prompt(contract_code: str | None = None) -> str:
|
||||||
|
"""生成单个或全部活跃合约的持仓分析 prompt。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
contract_code: 合约代码(如 FG2609),为 None 时分析所有活跃合约。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
可直接发送给 AI 进行分析的 prompt 文本。
|
||||||
|
"""
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
if contract_code:
|
||||||
|
codes = [contract_code.upper()]
|
||||||
|
else:
|
||||||
|
codes = [
|
||||||
|
r[0] for r in
|
||||||
|
db.query(Contract.code)
|
||||||
|
.filter(Contract.is_active == True)
|
||||||
|
.order_by(Contract.code)
|
||||||
|
.all()
|
||||||
|
]
|
||||||
|
|
||||||
|
# 取最近 5 个有持仓数据的交易日
|
||||||
|
all_dates = sorted({
|
||||||
|
r[0] for r in
|
||||||
|
db.query(PositionRanking.date)
|
||||||
|
.filter(PositionRanking.contract_code.in_(codes))
|
||||||
|
.distinct()
|
||||||
|
.all()
|
||||||
|
}, reverse=True)[:5]
|
||||||
|
all_dates.reverse() # 从旧到新
|
||||||
|
|
||||||
|
if not all_dates:
|
||||||
|
return "无持仓数据"
|
||||||
|
|
||||||
|
# 拉取全部数据
|
||||||
|
rankings = (
|
||||||
|
db.query(PositionRanking)
|
||||||
|
.filter(
|
||||||
|
PositionRanking.contract_code.in_(codes),
|
||||||
|
PositionRanking.date.in_(all_dates),
|
||||||
|
)
|
||||||
|
.order_by(PositionRanking.contract_code, PositionRanking.date, PositionRanking.data_type, PositionRanking.rank)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
# 按 contract → date → data_type 组织
|
||||||
|
data: dict[str, dict] = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
|
||||||
|
for r in rankings:
|
||||||
|
data[r.contract_code][r.date][r.data_type].append(r)
|
||||||
|
|
||||||
|
parts = [_build_legend()]
|
||||||
|
|
||||||
|
for code in codes:
|
||||||
|
if code not in data:
|
||||||
|
continue
|
||||||
|
cd = data[code]
|
||||||
|
parts.append(f"## {code}")
|
||||||
|
parts.append("")
|
||||||
|
parts.append(f"分析区间: {min(all_dates)} → {max(all_dates)}(共 {len(all_dates)} 个交易日)")
|
||||||
|
parts.append("")
|
||||||
|
|
||||||
|
# ── 每日明细 ──
|
||||||
|
for d in all_dates:
|
||||||
|
if d not in cd:
|
||||||
|
continue
|
||||||
|
parts.append(f"### {d} {_weekday_zh(d)}")
|
||||||
|
parts.append("")
|
||||||
|
|
||||||
|
for dtype in ["volume", "long", "short"]:
|
||||||
|
rows = cd[d].get(dtype, [])
|
||||||
|
if not rows:
|
||||||
|
continue
|
||||||
|
parts.append(f"**{TYPE_LABEL[dtype]} Top 20**")
|
||||||
|
parts.append("")
|
||||||
|
parts.append("| 排名 | 席位 | 分类 | 持仓量 | 增减 |")
|
||||||
|
parts.append("|------|------|------|--------|------|")
|
||||||
|
for r in rows[:20]:
|
||||||
|
tag = _classify(r.institution)
|
||||||
|
if tag == "噪音":
|
||||||
|
continue # 跳过东证
|
||||||
|
parts.append(
|
||||||
|
f"| {r.rank} | {r.institution} | {tag} | "
|
||||||
|
f"{_format_number(r.value)} | {_format_number(r.change)} |"
|
||||||
|
)
|
||||||
|
parts.append("")
|
||||||
|
|
||||||
|
# ── 每日家人 vs 大户汇总 ──
|
||||||
|
parts.append(f"### {code} 分类资金变化汇总")
|
||||||
|
parts.append("")
|
||||||
|
parts.append("| 日期 | 家人多单增减 | 家人空单增减 | 大户多单增减 | 大户空单增减 | 其他多单增减 | 其他空单增减 |")
|
||||||
|
parts.append("|------|-------------|-------------|-------------|-------------|-------------|-------------|")
|
||||||
|
|
||||||
|
for d in all_dates:
|
||||||
|
if d not in cd:
|
||||||
|
continue
|
||||||
|
cat_changes = defaultdict(lambda: defaultdict(int))
|
||||||
|
for dtype in ["long", "short"]:
|
||||||
|
for r in cd[d].get(dtype, []):
|
||||||
|
tag = _classify(r.institution)
|
||||||
|
if tag == "噪音":
|
||||||
|
continue
|
||||||
|
cat_changes[tag][dtype] += r.change
|
||||||
|
|
||||||
|
parts.append(
|
||||||
|
f"| {d} "
|
||||||
|
f"| {_format_number(cat_changes['家人']['long'])} "
|
||||||
|
f"| {_format_number(cat_changes['家人']['short'])} "
|
||||||
|
f"| {_format_number(cat_changes['大户']['long'])} "
|
||||||
|
f"| {_format_number(cat_changes['大户']['short'])} "
|
||||||
|
f"| {_format_number(cat_changes['其他']['long'])} "
|
||||||
|
f"| {_format_number(cat_changes['其他']['short'])} |"
|
||||||
|
)
|
||||||
|
parts.append("")
|
||||||
|
|
||||||
|
# ── 5日累计 ──
|
||||||
|
parts.append(f"### {code} 5日累计资金变化")
|
||||||
|
parts.append("")
|
||||||
|
total_cat = defaultdict(lambda: defaultdict(int))
|
||||||
|
for d in all_dates:
|
||||||
|
if d not in cd:
|
||||||
|
continue
|
||||||
|
for dtype in ["long", "short"]:
|
||||||
|
for r in cd[d].get(dtype, []):
|
||||||
|
tag = _classify(r.institution)
|
||||||
|
if tag == "噪音":
|
||||||
|
continue
|
||||||
|
total_cat[tag][dtype] += r.change
|
||||||
|
|
||||||
|
parts.append("| 分类 | 5日多单累计 | 5日空单累计 | 净多(多-空) |")
|
||||||
|
parts.append("|------|------------|------------|--------------|")
|
||||||
|
for tag in ["家人", "大户", "其他"]:
|
||||||
|
net = total_cat[tag]["long"] - total_cat[tag]["short"]
|
||||||
|
net_str = _format_number(net)
|
||||||
|
parts.append(
|
||||||
|
f"| {tag} | {_format_number(total_cat[tag]['long'])} "
|
||||||
|
f"| {_format_number(total_cat[tag]['short'])} | {net_str} |"
|
||||||
|
)
|
||||||
|
parts.append("")
|
||||||
|
|
||||||
|
# ── 分析引导 ──
|
||||||
|
parts.append("---")
|
||||||
|
parts.append("")
|
||||||
|
parts.append("请基于以上数据进行分析,重点关注:")
|
||||||
|
parts.append("")
|
||||||
|
parts.append("1. **家人席位动向**: 5日内多空增减趋势,散户情绪偏多还是偏空,"
|
||||||
|
"是否有明显的追涨杀跌行为。")
|
||||||
|
parts.append("2. **大户席位动向**: 产业资金和套保资金的布局方向,"
|
||||||
|
"多空增减是否与家人席位形成对手盘。")
|
||||||
|
parts.append("3. **多空力量对比**: 各分类的净多/净空方向,"
|
||||||
|
"整体市场情绪偏向。")
|
||||||
|
parts.append("4. **异常信号**: 单日大幅增减的席位,"
|
||||||
|
"分类资金出现明显分歧的交易日。")
|
||||||
|
parts.append("5. **行情预判**: 结合席位资金流向,"
|
||||||
|
"对下一交易日走势给出偏多/偏空/震荡的判断及理由。")
|
||||||
|
|
||||||
|
return "\n".join(parts)
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def _weekday_zh(d: date) -> str:
|
||||||
|
return ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][d.weekday()]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, "/app")
|
||||||
|
from app.prompt_builder import build_prompt
|
||||||
|
prompt = build_prompt()
|
||||||
|
print(prompt)
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import math
|
import math
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from fastapi import APIRouter, Depends, Request, Query
|
from fastapi import APIRouter, Depends, Request, Query
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse, PlainTextResponse
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from app.database import get_db
|
from app.database import get_db
|
||||||
from app.models import DailyBar, Contract, PositionRanking
|
from app.models import DailyBar, Contract, PositionRanking
|
||||||
|
from app.prompt_builder import build_prompt
|
||||||
|
|
||||||
router = APIRouter(prefix="/contracts", tags=["contracts"])
|
router = APIRouter(prefix="/contracts", tags=["contracts"])
|
||||||
|
|
||||||
@@ -164,6 +165,13 @@ def contract_detail(
|
|||||||
"change": r.change,
|
"change": r.change,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
pos_totals = {
|
||||||
|
"long": sum(r.change for r in rankings if r.data_type == "long"),
|
||||||
|
"short": sum(r.change for r in rankings if r.data_type == "short"),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
pos_totals = {"long": 0, "short": 0}
|
||||||
|
|
||||||
# Predict next trading day amplitude: mean of latest 5 diffs
|
# Predict next trading day amplitude: mean of latest 5 diffs
|
||||||
# Compute next trading date
|
# Compute next trading date
|
||||||
next_date = latest.date + timedelta(days=1) if latest else None
|
next_date = latest.date + timedelta(days=1) if latest else None
|
||||||
@@ -186,6 +194,7 @@ def contract_detail(
|
|||||||
pos_dates=pos_dates,
|
pos_dates=pos_dates,
|
||||||
selected_pos_date=selected_date,
|
selected_pos_date=selected_date,
|
||||||
pos_data=pos_data,
|
pos_data=pos_data,
|
||||||
|
pos_totals=pos_totals,
|
||||||
next_amp=next_amp,
|
next_amp=next_amp,
|
||||||
next_date=next_date.strftime("%Y/%-m/%-d") if next_date else None,
|
next_date=next_date.strftime("%Y/%-m/%-d") if next_date else None,
|
||||||
next_weekday=WEEKDAY_ZH.get(next_date.weekday(), "") if next_date else "",
|
next_weekday=WEEKDAY_ZH.get(next_date.weekday(), "") if next_date else "",
|
||||||
@@ -197,3 +206,9 @@ def contract_detail(
|
|||||||
WEEKDAY_ZH=WEEKDAY_ZH,
|
WEEKDAY_ZH=WEEKDAY_ZH,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{contract}/analysis-prompt", response_class=PlainTextResponse)
|
||||||
|
def contract_analysis_prompt(contract: str):
|
||||||
|
"""返回合约的持仓分析 prompt 文本。"""
|
||||||
|
return build_prompt(contract.upper())
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ def edit_trade(
|
|||||||
open_price: float = Form(...),
|
open_price: float = Form(...),
|
||||||
open_fee: float = Form(0.0),
|
open_fee: float = Form(0.0),
|
||||||
close_date: str = Form(""),
|
close_date: str = Form(""),
|
||||||
close_price: float = Form(None),
|
close_price: str = Form(""),
|
||||||
close_fee: float = Form(None),
|
close_fee: str = Form(""),
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
):
|
):
|
||||||
t = db.query(OptionTrade).filter(OptionTrade.id == trade_id).first()
|
t = db.query(OptionTrade).filter(OptionTrade.id == trade_id).first()
|
||||||
@@ -132,10 +132,10 @@ def edit_trade(
|
|||||||
t.open_date = date.fromisoformat(open_date)
|
t.open_date = date.fromisoformat(open_date)
|
||||||
t.open_price = open_price
|
t.open_price = open_price
|
||||||
t.open_fee = open_fee
|
t.open_fee = open_fee
|
||||||
if close_date and close_price is not None:
|
if close_date and close_price:
|
||||||
t.close_date = date.fromisoformat(close_date)
|
t.close_date = date.fromisoformat(close_date)
|
||||||
t.close_price = close_price
|
t.close_price = float(close_price)
|
||||||
t.close_fee = close_fee or 0
|
t.close_fee = float(close_fee) if close_fee else 0.0
|
||||||
db.commit()
|
db.commit()
|
||||||
return RedirectResponse("/options/", status_code=303)
|
return RedirectResponse("/options/", status_code=303)
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ def add_position(
|
|||||||
round_id: int,
|
round_id: int,
|
||||||
open_price: int = Form(...),
|
open_price: int = Form(...),
|
||||||
amp_threshold: int = Form(...),
|
amp_threshold: int = Form(...),
|
||||||
|
lock_price: str = Form(""),
|
||||||
hands: int = Form(1),
|
hands: int = Form(1),
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
):
|
):
|
||||||
@@ -87,12 +88,12 @@ def add_position(
|
|||||||
if not r or r.status != "active":
|
if not r or r.status != "active":
|
||||||
return RedirectResponse("/positions/?error=轮次不存在或已结束", status_code=303)
|
return RedirectResponse("/positions/?error=轮次不存在或已结束", status_code=303)
|
||||||
|
|
||||||
lock_price = open_price + amp_threshold
|
final_lock = int(lock_price) if lock_price else open_price + amp_threshold
|
||||||
p = Position(
|
p = Position(
|
||||||
round_id=r.id,
|
round_id=r.id,
|
||||||
open_price=open_price,
|
open_price=open_price,
|
||||||
amp_threshold=amp_threshold,
|
amp_threshold=amp_threshold,
|
||||||
lock_price=lock_price,
|
lock_price=final_lock,
|
||||||
hands=hands,
|
hands=hands,
|
||||||
opened_at=date.today(),
|
opened_at=date.today(),
|
||||||
)
|
)
|
||||||
@@ -102,9 +103,15 @@ def add_position(
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/{position_id}/lock")
|
@router.post("/{position_id}/lock")
|
||||||
def lock_position(position_id: int, db: Session = Depends(get_db)):
|
def lock_position(
|
||||||
|
position_id: int,
|
||||||
|
lock_price: str = Form(""),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
p = db.query(Position).filter(Position.id == position_id).first()
|
p = db.query(Position).filter(Position.id == position_id).first()
|
||||||
if p and p.locked_count < p.hands:
|
if p and p.locked_count < p.hands:
|
||||||
|
if lock_price:
|
||||||
|
p.lock_price = int(lock_price)
|
||||||
p.locked_count += 1
|
p.locked_count += 1
|
||||||
db.commit()
|
db.commit()
|
||||||
return RedirectResponse("/positions/", status_code=303)
|
return RedirectResponse("/positions/", status_code=303)
|
||||||
@@ -119,6 +126,32 @@ def unlock_position(position_id: int, db: Session = Depends(get_db)):
|
|||||||
return RedirectResponse("/positions/", status_code=303)
|
return RedirectResponse("/positions/", status_code=303)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{position_id}/update-lock-price")
|
||||||
|
def update_lock_price(
|
||||||
|
position_id: int,
|
||||||
|
lock_price: int = Form(...),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
p = db.query(Position).filter(Position.id == position_id).first()
|
||||||
|
if p and p.locked_count == 0:
|
||||||
|
p.lock_price = lock_price
|
||||||
|
db.commit()
|
||||||
|
return RedirectResponse("/positions/", status_code=303)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{round_id}/max-locks")
|
||||||
|
def update_max_locks(
|
||||||
|
round_id: int,
|
||||||
|
max_locks: int = Form(...),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
r = db.query(Round).filter(Round.id == round_id).first()
|
||||||
|
if r and r.status == "active" and 1 <= max_locks <= 10:
|
||||||
|
r.max_locks = max_locks
|
||||||
|
db.commit()
|
||||||
|
return RedirectResponse("/positions/", status_code=303)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{round_id}/close")
|
@router.post("/{round_id}/close")
|
||||||
def close_round(
|
def close_round(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|||||||
@@ -119,8 +119,8 @@ def edit_trade(
|
|||||||
open_price: float = Form(...),
|
open_price: float = Form(...),
|
||||||
open_fee: float = Form(0.0),
|
open_fee: float = Form(0.0),
|
||||||
close_date: str = Form(""),
|
close_date: str = Form(""),
|
||||||
close_price: float = Form(None),
|
close_price: str = Form(""),
|
||||||
close_fee: float = Form(None),
|
close_fee: str = Form(""),
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
):
|
):
|
||||||
t = db.query(Trade).filter(Trade.id == trade_id).first()
|
t = db.query(Trade).filter(Trade.id == trade_id).first()
|
||||||
@@ -133,10 +133,10 @@ def edit_trade(
|
|||||||
t.open_date = date.fromisoformat(open_date)
|
t.open_date = date.fromisoformat(open_date)
|
||||||
t.open_price = open_price
|
t.open_price = open_price
|
||||||
t.open_fee = open_fee
|
t.open_fee = open_fee
|
||||||
if close_date and close_price is not None:
|
if close_date and close_price:
|
||||||
t.close_date = date.fromisoformat(close_date)
|
t.close_date = date.fromisoformat(close_date)
|
||||||
t.close_price = close_price
|
t.close_price = float(close_price)
|
||||||
t.close_fee = close_fee or 0
|
t.close_fee = float(close_fee) if close_fee else 0.0
|
||||||
db.commit()
|
db.commit()
|
||||||
return RedirectResponse("/trades/", status_code=303)
|
return RedirectResponse("/trades/", status_code=303)
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,24 @@ SEED_BARS: list[dict] = [
|
|||||||
{"date": "2026-07-24", "open": 900, "close": 908, "high": 912, "low": 891},
|
{"date": "2026-07-24", "open": 900, "close": 908, "high": 912, "low": 891},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def _migrate(engine):
|
||||||
|
"""Add missing columns to existing tables (SQLite doesn't auto-migrate)."""
|
||||||
|
import sqlite3
|
||||||
|
conn = engine.raw_connection()
|
||||||
|
try:
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("PRAGMA table_info(rounds)")
|
||||||
|
cols = {row[1] for row in cur.fetchall()}
|
||||||
|
if "max_locks" not in cols:
|
||||||
|
cur.execute("ALTER TABLE rounds ADD COLUMN max_locks INTEGER DEFAULT 3")
|
||||||
|
conn.commit()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
def seed():
|
def seed():
|
||||||
Base.metadata.create_all(bind=engine)
|
Base.metadata.create_all(bind=engine)
|
||||||
|
_migrate(engine)
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
<div class="section-title" style="margin-top:28px;display:flex;align-items:center;gap:12px;">
|
<div class="section-title" style="margin-top:28px;display:flex;align-items:center;gap:12px;">
|
||||||
<span>持仓排名 · 前20</span>
|
<span>持仓排名 · 前20</span>
|
||||||
<input type="date" id="pos-date-select" value="{{ selected_pos_date }}" min="{{ pos_dates[-1] }}" max="{{ pos_dates[0] }}" onchange="changePosDate(this.value)" style="font-size:0.82rem;padding:4px 10px;border:1px solid var(--border);border-radius:5px;background:var(--surface);color:var(--fg);cursor:pointer;">
|
<input type="date" id="pos-date-select" value="{{ selected_pos_date }}" min="{{ pos_dates[-1] }}" max="{{ pos_dates[0] }}" onchange="changePosDate(this.value)" style="font-size:0.82rem;padding:4px 10px;border:1px solid var(--border);border-radius:5px;background:var(--surface);color:var(--fg);cursor:pointer;">
|
||||||
|
<button onclick="openAnalysisDrawer()" style="margin-left:auto;padding:6px 16px;border:1px solid var(--accent);border-radius:5px;background:var(--accent-light);color:var(--accent);cursor:pointer;font-size:0.82rem;font-weight:600;white-space:nowrap;">AI 分析</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:24px;">
|
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:24px;">
|
||||||
@@ -92,6 +93,15 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% if dtype != 'volume' %}
|
||||||
|
<tr style="background:var(--th-bg);font-weight:600;">
|
||||||
|
<td></td><td style="text-align:right;">增减合计</td>
|
||||||
|
<td></td>
|
||||||
|
<td style="color:{% if pos_totals[dtype] > 0 %}var(--danger-fg){% elif pos_totals[dtype] < 0 %}var(--success-fg){% else %}var(--sub){% endif %};">
|
||||||
|
{% if pos_totals[dtype] > 0 %}+{% endif %}{{ pos_totals[dtype] }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -99,6 +109,18 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<div id="analysisOverlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.35);z-index:99;" onclick="closeAnalysisDrawer()"></div>
|
||||||
|
<div id="analysisDrawer" style="display:none;position:fixed;top:0;right:0;width:700px;max-width:95vw;height:100%;background:var(--surface);border-left:1px solid var(--border);z-index:100;box-shadow:-4px 0 24px rgba(0,0,0,.12);transform:translateX(100%);transition:transform .25s ease;display:flex;flex-direction:column;">
|
||||||
|
<div style="display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);">
|
||||||
|
<h3 style="margin:0;font-size:1rem;">持仓分析 {{ contract }}</h3>
|
||||||
|
<div style="display:flex;gap:8px;">
|
||||||
|
<button onclick="copyPrompt()" style="padding:4px 12px;border:1px solid var(--border);border-radius:4px;background:var(--surface);color:var(--fg);cursor:pointer;font-size:0.78rem;">复制</button>
|
||||||
|
<button onclick="closeAnalysisDrawer()" style="background:none;border:none;font-size:1.3rem;cursor:pointer;color:var(--sub);line-height:1;">×</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<pre id="analysisContent" style="flex:1;margin:0;padding:16px 20px;overflow-y:auto;font-size:0.78rem;line-height:1.6;white-space:pre-wrap;word-break:break-all;color:var(--fg);background:var(--bg);">加载中...</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function changePosDate(d) {
|
function changePosDate(d) {
|
||||||
var url = new URL(window.location);
|
var url = new URL(window.location);
|
||||||
@@ -154,5 +176,64 @@ function showDrawer(cell, product, calcRows, isNext) {
|
|||||||
activeRow = row;
|
activeRow = row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var promptText = '';
|
||||||
|
function openAnalysisDrawer() {
|
||||||
|
var overlay = document.getElementById('analysisOverlay');
|
||||||
|
var drawer = document.getElementById('analysisDrawer');
|
||||||
|
drawer.style.display = 'flex';
|
||||||
|
overlay.style.display = 'block';
|
||||||
|
requestAnimationFrame(function() {
|
||||||
|
drawer.style.transform = 'translateX(0)';
|
||||||
|
});
|
||||||
|
var content = document.getElementById('analysisContent');
|
||||||
|
if (promptText) {
|
||||||
|
content.textContent = promptText;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
content.textContent = '加载中...';
|
||||||
|
fetch('/contracts/{{ contract }}/analysis-prompt')
|
||||||
|
.then(function(r) { return r.text(); })
|
||||||
|
.then(function(t) {
|
||||||
|
promptText = t;
|
||||||
|
content.textContent = t;
|
||||||
|
})
|
||||||
|
.catch(function() { content.textContent = '加载失败'; });
|
||||||
|
}
|
||||||
|
function closeAnalysisDrawer() {
|
||||||
|
document.getElementById('analysisOverlay').style.display = 'none';
|
||||||
|
document.getElementById('analysisDrawer').style.transform = 'translateX(100%)';
|
||||||
|
}
|
||||||
|
function copyPrompt() {
|
||||||
|
navigator.clipboard.writeText(promptText).then(function() {
|
||||||
|
showToast('已复制到剪贴板');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function showToast(msg) {
|
||||||
|
var m = document.getElementById('toastMask');
|
||||||
|
if (!m) {
|
||||||
|
m = document.createElement('div');
|
||||||
|
m.id = 'toastMask';
|
||||||
|
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.3);z-index:999;display:flex;align-items:center;justify-content:center;transition:opacity .25s;opacity:0;';
|
||||||
|
m.onclick = function() { hideToast(); };
|
||||||
|
var b = document.createElement('div');
|
||||||
|
b.id = 'toastBox';
|
||||||
|
b.style.cssText = 'background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:32px 40px;text-align:center;box-shadow:0 8px 32px rgba(0,0,0,.18);';
|
||||||
|
m.appendChild(b);
|
||||||
|
document.body.appendChild(m);
|
||||||
|
}
|
||||||
|
var box = document.getElementById('toastBox');
|
||||||
|
box.innerHTML = '<div style="font-size:2rem;margin-bottom:12px;">✅</div><p style="font-size:1rem;font-weight:600;color:var(--fg);">' + msg + '</p>';
|
||||||
|
m.style.display = 'flex';
|
||||||
|
requestAnimationFrame(function() { m.style.opacity = '1'; });
|
||||||
|
clearTimeout(m._tid);
|
||||||
|
m._tid = setTimeout(hideToast, 1800);
|
||||||
|
}
|
||||||
|
function hideToast() {
|
||||||
|
var m = document.getElementById('toastMask');
|
||||||
|
if (!m) return;
|
||||||
|
m.style.opacity = '0';
|
||||||
|
setTimeout(function() { m.style.display = 'none'; }, 250);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -56,13 +56,13 @@
|
|||||||
<div style="display:flex;align-items:center;gap:16px;padding:14px 20px;background:var(--th-bg);flex-wrap:wrap;">
|
<div style="display:flex;align-items:center;gap:16px;padding:14px 20px;background:var(--th-bg);flex-wrap:wrap;">
|
||||||
<strong style="font-size:1rem;">{{ round.contract_code }}</strong>
|
<strong style="font-size:1rem;">{{ round.contract_code }}</strong>
|
||||||
<span class="badge badge-up">{{ round.daily_hands }} 手/天</span>
|
<span class="badge badge-up">{{ round.daily_hands }} 手/天</span>
|
||||||
<span style="font-size:0.82rem;color:var(--sub);">已开 {{ round.positions|length }} 天</span>
|
<span style="font-size:0.82rem;font-weight:600;{% if locks >= round.max_locks %}color:var(--danger-fg);{% endif %}">
|
||||||
<span style="font-size:0.82rem;color:var(--sub);">开始 {{ round.started_at }}</span>
|
锁仓 {{ locks }}/{{ round.max_locks }}
|
||||||
<span style="font-size:0.82rem;font-weight:600;{% if locks >= 3 %}color:var(--danger-fg);{% endif %}">
|
{% if locks >= round.max_locks %} ⚠ 熔断{% endif %}
|
||||||
锁仓 {{ locks }}/3
|
|
||||||
{% if locks >= 3 %} ⚠ 熔断{% endif %}
|
|
||||||
</span>
|
</span>
|
||||||
<span style="margin-left:auto;display:flex;gap:8px;">
|
<span style="margin-left:auto;display:flex;gap:8px;">
|
||||||
|
<button style="background:var(--surface);color:var(--fg);border:1px solid var(--border);padding:3px 12px;border-radius:4px;cursor:pointer;font-size:0.78rem;"
|
||||||
|
onclick="showMaxLocksModal({{ round.id }}, {{ round.max_locks }})">修改锁仓数</button>
|
||||||
<form method="post" action="/positions/{{ round.id }}/close" style="display:inline;">
|
<form method="post" action="/positions/{{ round.id }}/close" style="display:inline;">
|
||||||
<input type="hidden" name="result" value="profit_taken">
|
<input type="hidden" name="result" value="profit_taken">
|
||||||
<button class="btn" style="background:var(--success);color:#fff;font-size:0.78rem;padding:4px 14px;" onclick="confirmDelete(event, '确认 {{ round.contract_code }} 止盈清仓?', this.closest('form').action)">止盈</button>
|
<button class="btn" style="background:var(--success);color:#fff;font-size:0.78rem;padding:4px 14px;" onclick="confirmDelete(event, '确认 {{ round.contract_code }} 止盈清仓?', this.closest('form').action)">止盈</button>
|
||||||
@@ -118,11 +118,10 @@
|
|||||||
<div class="table-wrap" style="margin-bottom:0;">
|
<div class="table-wrap" style="margin-bottom:0;">
|
||||||
<table style="font-size:0.84rem;">
|
<table style="font-size:0.84rem;">
|
||||||
<tr>
|
<tr>
|
||||||
<th>日期</th><th>开仓价</th><th>A</th><th>锁仓价</th><th>手数</th><th>已锁</th><th>状态</th><th>操作</th>
|
<th>开仓价</th><th>A</th><th>锁仓价</th><th>手数</th><th>已锁</th><th>状态</th><th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for p in round.positions|reverse %}
|
{% for p in round.positions|reverse %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>{{ p.opened_at }}</strong></td>
|
|
||||||
<td>{{ p.open_price }}</td>
|
<td>{{ p.open_price }}</td>
|
||||||
<td>{{ p.amp_threshold }}</td>
|
<td>{{ p.amp_threshold }}</td>
|
||||||
<td style="color:var(--danger-fg);font-weight:600;">{{ p.lock_price }}</td>
|
<td style="color:var(--danger-fg);font-weight:600;">{{ p.lock_price }}</td>
|
||||||
@@ -145,10 +144,9 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div style="display:flex;gap:6px;justify-content:center;">
|
<div style="display:flex;gap:6px;justify-content:center;">
|
||||||
<form method="post" action="/positions/{{ p.id }}/lock" style="display:inline;">
|
|
||||||
<button style="background:var(--warn-bg);color:var(--warn-fg);border:1px solid var(--warn);padding:2px 8px;border-radius:4px;cursor:pointer;font-size:0.75rem;font-weight:600;"
|
<button style="background:var(--warn-bg);color:var(--warn-fg);border:1px solid var(--warn);padding:2px 8px;border-radius:4px;cursor:pointer;font-size:0.75rem;font-weight:600;"
|
||||||
{% if p.locked_count >= p.hands %}disabled style="opacity:0.4;cursor:default;"{% endif %}>锁</button>
|
{% if p.locked_count >= p.hands %}disabled style="opacity:0.4;cursor:default;"{% endif %}
|
||||||
</form>
|
onclick="showLockConfirm({{ p.id }}, {{ p.lock_price }})">锁</button>
|
||||||
<form method="post" action="/positions/{{ p.id }}/unlock" style="display:inline;">
|
<form method="post" action="/positions/{{ p.id }}/unlock" style="display:inline;">
|
||||||
<button style="background:var(--surface);color:var(--sub);border:1px solid var(--border);padding:2px 8px;border-radius:4px;cursor:pointer;font-size:0.75rem;"
|
<button style="background:var(--surface);color:var(--sub);border:1px solid var(--border);padding:2px 8px;border-radius:4px;cursor:pointer;font-size:0.75rem;"
|
||||||
{% if p.locked_count == 0 %}disabled style="opacity:0.4;cursor:default;"{% endif %}>撤</button>
|
{% if p.locked_count == 0 %}disabled style="opacity:0.4;cursor:default;"{% endif %}>撤</button>
|
||||||
@@ -181,8 +179,72 @@ function filterContracts() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function showMaxLocksModal(rid, currentMax) {
|
||||||
|
document.getElementById('maxLocksRid').value = rid;
|
||||||
|
document.getElementById('maxLocksInput').value = currentMax;
|
||||||
|
document.getElementById('maxLocksModal').style.display = 'block';
|
||||||
|
document.getElementById('maxLocksModalOverlay').style.display = 'block';
|
||||||
|
document.getElementById('maxLocksInput').focus();
|
||||||
|
}
|
||||||
|
function hideMaxLocksModal() {
|
||||||
|
document.getElementById('maxLocksModal').style.display = 'none';
|
||||||
|
document.getElementById('maxLocksModalOverlay').style.display = 'none';
|
||||||
|
}
|
||||||
|
function doUpdateMaxLocks() {
|
||||||
|
var rid = document.getElementById('maxLocksRid').value;
|
||||||
|
var fd = new FormData();
|
||||||
|
fd.append('max_locks', document.getElementById('maxLocksInput').value);
|
||||||
|
fetch('/positions/' + rid + '/max-locks', {method:'POST', body:fd})
|
||||||
|
.then(function() { location.reload(); });
|
||||||
|
}
|
||||||
|
function showLockConfirm(pid, lockPrice) {
|
||||||
|
document.getElementById('lockPid').value = pid;
|
||||||
|
document.getElementById('lockPriceInput').value = lockPrice;
|
||||||
|
document.getElementById('lockModal').style.display = 'block';
|
||||||
|
document.getElementById('lockModalOverlay').style.display = 'block';
|
||||||
|
document.getElementById('lockPriceInput').focus();
|
||||||
|
}
|
||||||
|
function hideLockModal() {
|
||||||
|
document.getElementById('lockModal').style.display = 'none';
|
||||||
|
document.getElementById('lockModalOverlay').style.display = 'none';
|
||||||
|
}
|
||||||
|
function doLock() {
|
||||||
|
var pid = document.getElementById('lockPid').value;
|
||||||
|
var fd = new FormData();
|
||||||
|
fd.append('lock_price', document.getElementById('lockPriceInput').value);
|
||||||
|
fetch('/positions/' + pid + '/lock', {method:'POST', body:fd})
|
||||||
|
.then(function() { location.reload(); });
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div id="maxLocksModalOverlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.35);z-index:159;" onclick="hideMaxLocksModal()"></div>
|
||||||
|
<div id="maxLocksModal" style="display:none;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:24px;z-index:160;width:300px;max-width:90vw;">
|
||||||
|
<h3 style="margin-bottom:16px;">修改锁仓上限</h3>
|
||||||
|
<input type="hidden" id="maxLocksRid">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>锁仓次数上限(熔断阈值)</label>
|
||||||
|
<input type="number" id="maxLocksInput" min="1" max="10" required style="font-size:1rem;text-align:center;font-weight:600;">
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;gap:10px;justify-content:flex-end;margin-top:8px;">
|
||||||
|
<button class="btn" style="background:var(--th-bg);color:var(--fg);" onclick="hideMaxLocksModal()">取消</button>
|
||||||
|
<button class="btn" style="background:var(--accent);color:#fff;" onclick="doUpdateMaxLocks()">确认修改</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="lockModalOverlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.35);z-index:149;" onclick="hideLockModal()"></div>
|
||||||
|
<div id="lockModal" style="display:none;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:24px;z-index:150;width:300px;max-width:90vw;">
|
||||||
|
<h3 style="margin-bottom:16px;">确认锁仓</h3>
|
||||||
|
<input type="hidden" id="lockPid">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>锁仓价位</label>
|
||||||
|
<input type="number" id="lockPriceInput" required style="font-size:1rem;text-align:center;font-weight:600;color:var(--danger-fg);">
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;gap:10px;justify-content:flex-end;margin-top:8px;">
|
||||||
|
<button class="btn" style="background:var(--th-bg);color:var(--fg);" onclick="hideLockModal()">取消</button>
|
||||||
|
<button class="btn" style="background:var(--warn);color:#fff;" onclick="doLock()">确认锁仓</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="confirmOverlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.35);z-index:199;" onclick="hideConfirm()"></div>
|
<div id="confirmOverlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.35);z-index:199;" onclick="hideConfirm()"></div>
|
||||||
<div id="confirmBox" style="display:none;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:28px 32px;z-index:200;text-align:center;max-width:90vw;">
|
<div id="confirmBox" style="display:none;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:28px 32px;z-index:200;text-align:center;max-width:90vw;">
|
||||||
<p id="confirmMsg" style="font-size:0.95rem;margin-bottom:20px;"></p>
|
<p id="confirmMsg" style="font-size:0.95rem;margin-bottom:20px;"></p>
|
||||||
|
|||||||
@@ -5,6 +5,12 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% set view = request.query_params.get('view', '') %}
|
{% set view = request.query_params.get('view', '') %}
|
||||||
|
{% set product_tab = request.query_params.get('tab', '全部') %}
|
||||||
|
{% set product_codes = product_contracts.keys()|list %}
|
||||||
|
{% set grouped_open = {} %}
|
||||||
|
{% for t in open_trades %}
|
||||||
|
{% set _ = grouped_open.setdefault(t.product_code, []).append(t) %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.tabs { display: flex; gap: 0; margin-bottom: 24px; border-bottom: 2px solid var(--border); }
|
.tabs { display: flex; gap: 0; margin-bottom: 24px; border-bottom: 2px solid var(--border); }
|
||||||
@@ -18,6 +24,14 @@
|
|||||||
.tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
|
.tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
|
||||||
.tab-panel { display: none; }
|
.tab-panel { display: none; }
|
||||||
.tab-panel.active { display: block; }
|
.tab-panel.active { display: block; }
|
||||||
|
.sub-tabs { display: flex; gap: 6px; margin-bottom: 16px; flex-wrap: wrap; }
|
||||||
|
.sub-tab-btn {
|
||||||
|
padding: 4px 14px; border: 1px solid var(--border); background: var(--surface);
|
||||||
|
font-size: 0.78rem; color: var(--sub); cursor: pointer; text-decoration: none;
|
||||||
|
border-radius: 4px; transition: color .12s, border-color .12s;
|
||||||
|
}
|
||||||
|
.sub-tab-btn:hover { color: var(--fg); border-color: var(--accent); }
|
||||||
|
.sub-tab-btn.active { color: var(--accent); border-color: var(--accent); font-weight: 600; background: var(--accent-light); }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
@@ -76,13 +90,27 @@
|
|||||||
{# ── 持仓列表 ── #}
|
{# ── 持仓列表 ── #}
|
||||||
<div class="section-title">持仓中 · {{ open_trades|length }} 笔</div>
|
<div class="section-title">持仓中 · {{ open_trades|length }} 笔</div>
|
||||||
|
|
||||||
|
<div class="sub-tabs">
|
||||||
|
<a href="/trades/?tab=全部" class="sub-tab-btn{% if product_tab == '全部' %} active{% endif %}">全部</a>
|
||||||
|
{% for pc in product_codes %}
|
||||||
|
{% set count = grouped_open.get(pc, [])|length %}
|
||||||
|
<a href="/trades/?tab={{ pc }}" class="sub-tab-btn{% if product_tab == pc %} active{% endif %}">{{ pc }}{% if count %} · {{ count }}{% endif %}</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if open_trades %}
|
{% if open_trades %}
|
||||||
<div class="table-wrap">
|
{% for pc in product_codes %}
|
||||||
|
{% if product_tab == '全部' or product_tab == pc %}
|
||||||
|
{% set trades = grouped_open.get(pc, []) %}
|
||||||
|
{% if trades %}
|
||||||
|
<div class="table-wrap" style="margin-bottom:24px;">
|
||||||
|
{% if product_tab == '全部' %}
|
||||||
|
<div class="section-title" style="font-size:0.88rem;margin-bottom:0;padding:6px 12px;">{{ pc }} · {{ trades|length }} 笔</div>
|
||||||
|
{% endif %}
|
||||||
<table>
|
<table>
|
||||||
<tr><th>品种</th><th>合约</th><th>方向</th><th>开仓日期</th><th>开仓价</th><th>手续费</th><th>操作</th></tr>
|
<tr><th>合约</th><th>方向</th><th>开仓日期</th><th>开仓价</th><th>手续费</th><th>操作</th></tr>
|
||||||
{% for t in open_trades %}
|
{% for t in trades %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ t.product_code }}</td>
|
|
||||||
<td><strong>{{ t.contract_code.replace(t.product_code, '', 1) }}</strong></td>
|
<td><strong>{{ t.contract_code.replace(t.product_code, '', 1) }}</strong></td>
|
||||||
<td>
|
<td>
|
||||||
{% if t.direction == 'short' %}
|
{% if t.direction == 'short' %}
|
||||||
@@ -107,6 +135,9 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div style="text-align:center;padding:40px;color:var(--sub);">暂无持仓</div>
|
<div style="text-align:center;padding:40px;color:var(--sub);">暂无持仓</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -166,7 +197,7 @@
|
|||||||
<td>
|
<td>
|
||||||
{% set p = t.pnl %}
|
{% set p = t.pnl %}
|
||||||
{% if p is not none %}
|
{% if p is not none %}
|
||||||
<span style="font-weight:700;{% if p > 0 %}color:var(--success-fg);{% elif p < 0 %}color:var(--danger-fg);{% else %}color:var(--sub);{% endif %}">
|
<span style="font-weight:700;{% if p > 0 %}color:var(--danger-fg);{% elif p < 0 %}color:var(--success-fg);{% else %}color:var(--sub);{% endif %}">
|
||||||
{% if p > 0 %}+{% endif %}{{ p }}
|
{% if p > 0 %}+{% endif %}{{ p }}
|
||||||
</span>
|
</span>
|
||||||
<span style="color:var(--sub);cursor:pointer;font-size:0.75rem;margin-left:4px;"
|
<span style="color:var(--sub);cursor:pointer;font-size:0.75rem;margin-left:4px;"
|
||||||
@@ -192,7 +223,7 @@
|
|||||||
{% set ns.total_close_fee = ns.total_close_fee + (t.close_fee or 0) %}
|
{% set ns.total_close_fee = ns.total_close_fee + (t.close_fee or 0) %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div style="display:flex;gap:24px;margin-bottom:24px;padding:12px 18px;background:var(--surface);border:1px solid var(--border);border-radius:8px;font-size:0.88rem;flex-wrap:wrap;">
|
<div style="display:flex;gap:24px;margin-bottom:24px;padding:12px 18px;background:var(--surface);border:1px solid var(--border);border-radius:8px;font-size:0.88rem;flex-wrap:wrap;">
|
||||||
<span>盈亏合计 <b style="{% if ns.total_pnl > 0 %}color:var(--success-fg);{% elif ns.total_pnl < 0 %}color:var(--danger-fg);{% endif %}">{% if ns.total_pnl > 0 %}+{% endif %}{{ '%.2f'|format(ns.total_pnl) }}</b></span>
|
<span>盈亏合计 <b style="{% if ns.total_pnl > 0 %}color:var(--danger-fg);{% elif ns.total_pnl < 0 %}color:var(--success-fg);{% endif %}">{% if ns.total_pnl > 0 %}+{% endif %}{{ '%.2f'|format(ns.total_pnl) }}</b></span>
|
||||||
<span style="color:var(--sub);">|</span>
|
<span style="color:var(--sub);">|</span>
|
||||||
<span>开仓手续费 <b>{{ '%.2f'|format(ns.total_open_fee) }}</b></span>
|
<span>开仓手续费 <b>{{ '%.2f'|format(ns.total_open_fee) }}</b></span>
|
||||||
<span style="color:var(--sub);">|</span>
|
<span style="color:var(--sub);">|</span>
|
||||||
@@ -306,7 +337,7 @@ function showPnlDrawer(title, direction, openPrice, closePrice, openFee, closeFe
|
|||||||
html += '<tr><td>开仓手续费</td><td></td><td style="text-align:right;color:var(--danger-fg);">-' + openFee.toFixed(2) + '</td></tr>';
|
html += '<tr><td>开仓手续费</td><td></td><td style="text-align:right;color:var(--danger-fg);">-' + openFee.toFixed(2) + '</td></tr>';
|
||||||
html += '<tr><td>平仓手续费</td><td></td><td style="text-align:right;color:var(--danger-fg);">-' + closeFee.toFixed(2) + '</td></tr>';
|
html += '<tr><td>平仓手续费</td><td></td><td style="text-align:right;color:var(--danger-fg);">-' + closeFee.toFixed(2) + '</td></tr>';
|
||||||
document.getElementById('pnlTable').innerHTML = html;
|
document.getElementById('pnlTable').innerHTML = html;
|
||||||
document.getElementById('pnlResult').innerHTML = '<b>盈亏 = ' + gross.toFixed(2) + ' - ' + openFee.toFixed(2) + ' - ' + closeFee.toFixed(2) + ' = <span style="color:' + (result > 0 ? 'var(--success-fg)' : result < 0 ? 'var(--danger-fg)' : 'var(--sub)') + ';">' + (result > 0 ? '+' : '') + result.toFixed(2) + '</span></b>';
|
document.getElementById('pnlResult').innerHTML = '<b>盈亏 = ' + gross.toFixed(2) + ' - ' + openFee.toFixed(2) + ' - ' + closeFee.toFixed(2) + ' = <span style="color:' + (result > 0 ? 'var(--danger-fg)' : result < 0 ? 'var(--success-fg)' : 'var(--sub)') + ';">' + (result > 0 ? '+' : '') + result.toFixed(2) + '</span></b>';
|
||||||
document.getElementById('pnlOverlay').style.display = 'block';
|
document.getElementById('pnlOverlay').style.display = 'block';
|
||||||
document.getElementById('pnlBox').style.display = 'block';
|
document.getElementById('pnlBox').style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -1,145 +0,0 @@
|
|||||||
# 玻璃期货行情分析 Prompt
|
|
||||||
|
|
||||||
## 分析步骤
|
|
||||||
|
|
||||||
按以下步骤分析玻璃期货当前行情,每一步输出关键数据和判断,最后汇总结论。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第一步:行情概览
|
|
||||||
|
|
||||||
查询 FG2609、FG2611、FG2701 最近 10 个交易日的日线数据(开盘、收盘、最高、最低、波幅、5 日均振幅)。
|
|
||||||
|
|
||||||
输出格式:
|
|
||||||
```
|
|
||||||
合约 最新价 10日涨跌幅 5日均振幅 相对2609升水
|
|
||||||
FG2609 xxx -xx% xx —
|
|
||||||
FG2611 xxx -xx% xx +xx
|
|
||||||
FG2701 xxx -xx% xx +xx
|
|
||||||
```
|
|
||||||
|
|
||||||
判断:趋势方向(下跌/反弹/横盘)、三个合约的波动率差异、期限结构是否正常。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第二步:全市场主力持仓
|
|
||||||
|
|
||||||
查询三个合约最近 5 个交易日的前 20 名持仓汇总:
|
|
||||||
|
|
||||||
```
|
|
||||||
合约 多单合计 空单合计 净多 多空比 5日趋势
|
|
||||||
FG2609 xxx xxx -xx,xxx 1:x.xx 净空收敛/扩大/持平
|
|
||||||
FG2611 xxx xxx -xx,xxx 1:x.xx 净空收敛/扩大/持平
|
|
||||||
FG2701 xxx xxx -xx,xxx 1:x.xx 净空收敛/扩大/持平
|
|
||||||
```
|
|
||||||
|
|
||||||
关键判断:空头是在撤退(三个合约净空都在降)还是在展期(近月降、远月升)?
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第三步:核心机构逐日持仓(仅分析信号价值高的机构)
|
|
||||||
|
|
||||||
#### 国泰君安(空头之锚)
|
|
||||||
|
|
||||||
查询三个合约的逐日多单/空单/净空变化。
|
|
||||||
|
|
||||||
判断标准:
|
|
||||||
- 净空稳定 → 趋势未反转
|
|
||||||
- 净空连续下降超过 3 天 → 空头可能在撤退,警惕反弹
|
|
||||||
- 净空扩大 → 空头在加码,趋势强化
|
|
||||||
|
|
||||||
#### 高盛期货(纯空头,最干净的信号)
|
|
||||||
|
|
||||||
查询三个合约的逐日空单变化。
|
|
||||||
|
|
||||||
判断标准:
|
|
||||||
- 只出现在空单前 5,几乎不在多单前 5 → 裸空确认
|
|
||||||
- 空单连续减少 → 纯投机盘在撤退,短期强烈偏多信号
|
|
||||||
- 空单稳定或增加 → 空头信心充足
|
|
||||||
- 哪个合约有高盛的仓位 → 那个合约才是真正的空头主战场
|
|
||||||
|
|
||||||
#### 中信期货(稳定型参考)
|
|
||||||
|
|
||||||
查询三个合约的逐日空单变化。
|
|
||||||
|
|
||||||
判断标准:
|
|
||||||
- 方向一致、每日变化不大 → 可靠的趋势信号
|
|
||||||
- 方向反复、单日波动大 → 降低参考权重
|
|
||||||
|
|
||||||
#### ⚠️ 东证期货 → 直接忽略
|
|
||||||
|
|
||||||
东证是高频做市商(日均成交量 20-30 万手、长期排名第一),持仓每日大幅摆动(±3-5 万手/天)属于日内交易的残差,不具方向性信号价值。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第四步:家人们情绪(散户反向指标)
|
|
||||||
|
|
||||||
查询五大家人席位(东方财富、徽商期货、方正中期、中信建投、平安期货)在三个合约的持仓:
|
|
||||||
|
|
||||||
```
|
|
||||||
席位 合约 多单 5日变化 空单 净头寸 特征
|
|
||||||
东方财富 FG2609 xx,xxx -xx% 无 纯多 散户代表
|
|
||||||
徽商期货 FG2609 xx,xxx -xx% 无 纯多 散户大本营
|
|
||||||
方正中期 FG2609 xx,xxx -xx% xx,xxx 净多/空 多空双开
|
|
||||||
中信建投 FG2609 xx,xxx -xx% xx,xxx 净多/空 偏机构型
|
|
||||||
平安期货 FG2609 xx,xxx -xx% xx,xxx 净多/空 偏机构型
|
|
||||||
──────────────────────────────────────────────────
|
|
||||||
合计 xxx,xxx -xx% xx,xxx 净多/空 +xx,xxx
|
|
||||||
```
|
|
||||||
|
|
||||||
注意区分两类:
|
|
||||||
- **纯散户型**(东方财富、徽商):只持单边,不做对冲,追涨杀跌
|
|
||||||
- **偏机构型**(方正中期、中信建投、平安):多空双开,但散户占比仍高
|
|
||||||
|
|
||||||
判断标准:
|
|
||||||
- 家人们与主流机构同向 → 注意反转风险,机构往往比散户跑得快
|
|
||||||
- 家人们与主流机构对向 → 经典散户接盘/逆势抄底,趋势大概率延续
|
|
||||||
- 家人们持仓持续大幅缩减(5 日 -20% 以上)→ 散户在割肉,可能是短期底部的先兆
|
|
||||||
- 家人们持仓企稳或开始加仓 → 散户情绪见底或过热
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第五步:跨合约资金流向
|
|
||||||
|
|
||||||
对比三个合约的前 5 名机构重叠情况和净空趋势:
|
|
||||||
|
|
||||||
```
|
|
||||||
关键问题:
|
|
||||||
1. 空头在哪个合约集中?→ 那个合约是主战场
|
|
||||||
2. 近月减空的同时远月是否在加空?→ 展期 or 撤退
|
|
||||||
3. 是否有机构同时在多空前 5 出现?→ 套保/套利盘,方向信号弱
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第六步:综合判断
|
|
||||||
|
|
||||||
汇总以上数据,给出:
|
|
||||||
|
|
||||||
1. **当前趋势状态**:下跌中继 / 底部震荡 / 反弹中 / 反转确认
|
|
||||||
2. **主力意图**:加空 / 减空 / 展期 / 观望
|
|
||||||
3. **关键观察信号**:接下来最该盯的 1-2 个指标
|
|
||||||
4. **风险提示**:当前最大的不确定性是什么
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 机构信号权重表
|
|
||||||
|
|
||||||
| 机构 | 信号权重 | 特点 | 看什么 |
|
|
||||||
|------|---------|------|--------|
|
|
||||||
| 国泰君安 | ★★★★★ | 最大持仓,多空双开 | 净空变化趋势 |
|
|
||||||
| 高盛期货 | ★★★★★ | 纯裸空,不持多单 | 空单增减 + 出现在哪个合约 |
|
|
||||||
| 中信期货 | ★★★★☆ | 稳定型,方向一致 | 空单变化方向 |
|
|
||||||
| 其他机构 | ★★★☆☆ | 视情况 | 多空集中度 |
|
|
||||||
| 东证期货 | ☆☆☆☆☆ | 做市商噪音 | **别看** |
|
|
||||||
| 东方财富 | ★★★☆☆ | 纯散户型,只做单边 | 散户情绪(反向指标) |
|
|
||||||
| 徽商期货 | ★★★☆☆ | 散户大本营,只做单边 | 同上 |
|
|
||||||
| 方正中期 | ★★★☆☆ | 散户+机构混合 | 多空对比变化 |
|
|
||||||
| 中信建投 | ★★★☆☆ | 偏机构型,线上线下混合 | 参考意义弱于纯散户席位 |
|
|
||||||
| 平安期货 | ★★★☆☆ | 偏机构型 | 参考意义弱于纯散户席位 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 使用方式
|
|
||||||
|
|
||||||
将此文件内容作为 prompt 输入,配合 ft-app 数据库查询(`data/ft.db`,相对于 ft-app 目录),即可获得标准化的玻璃期货行情分析报告。
|
|
||||||
Reference in New Issue
Block a user