Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
"""
|
| 3 |
港股智能分析平台 - Hugging Face Spaces版本
|
| 4 |
集成数学原理的XPINNs训练与LLM推理系统
|
|
|
|
| 5 |
"""
|
| 6 |
|
| 7 |
import os
|
|
@@ -64,7 +65,7 @@ class FiberBundleTheory:
|
|
| 64 |
def __init__(self, base_dim=2, fiber_dim=16):
|
| 65 |
self.base_dim = base_dim # VIX^2, RV
|
| 66 |
self.fiber_dim = fiber_dim # 隐藏状态维度
|
| 67 |
-
self.whitney_factor = 2 * fiber_dim # Whitney嵌入
|
| 68 |
|
| 69 |
def project_to_base(self, high_dim_state):
|
| 70 |
"""投影到基空间"""
|
|
@@ -72,7 +73,7 @@ class FiberBundleTheory:
|
|
| 72 |
high_dim_state = np.pad(high_dim_state, (0, self.fiber_dim - len(high_dim_state)))
|
| 73 |
|
| 74 |
# 计算VIX^2代理
|
| 75 |
-
vix_squared = np.sum(high_dim_state[:self.fiber_dim//2]**2) / (self.fiber_dim//2)
|
| 76 |
# 计算实现波动率
|
| 77 |
rv = np.std(high_dim_state[self.fiber_dim//2:])
|
| 78 |
|
|
@@ -83,6 +84,182 @@ class FiberBundleTheory:
|
|
| 83 |
vix_squared, rv = base_point
|
| 84 |
return vix_squared - rv
|
| 85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
class EquivariantLayer(nn.Module):
|
| 87 |
"""SO(3)等变层"""
|
| 88 |
|
|
@@ -230,12 +407,17 @@ class CausalVAR:
|
|
| 230 |
class MathematicalTrainer:
|
| 231 |
"""数学原理驱动的训练器"""
|
| 232 |
|
| 233 |
-
def __init__(self):
|
| 234 |
-
self.
|
| 235 |
-
self.
|
|
|
|
| 236 |
self.model = XPINNsGenerator()
|
| 237 |
self.optimizer = Adam(self.model.parameters(), lr=0.001)
|
| 238 |
self.scaler = StandardScaler()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
|
| 240 |
def prepare_data(self, df):
|
| 241 |
"""准备训练数据"""
|
|
@@ -324,6 +506,35 @@ class MathematicalTrainer:
|
|
| 324 |
logger.info(f"Epoch {epoch}: Loss = {avg_loss:.6f}")
|
| 325 |
|
| 326 |
return losses
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 327 |
|
| 328 |
class LLMInterface:
|
| 329 |
"""LLM接口"""
|
|
@@ -340,7 +551,7 @@ class LLMInterface:
|
|
| 340 |
"inputs": prompt,
|
| 341 |
"parameters": {
|
| 342 |
"max_new_tokens": max_length,
|
| 343 |
-
"temperature": 0.
|
| 344 |
"top_p": 0.9,
|
| 345 |
"do_sample": True
|
| 346 |
}
|
|
@@ -358,34 +569,77 @@ class LLMInterface:
|
|
| 358 |
except Exception as e:
|
| 359 |
return f"错误: {str(e)}"
|
| 360 |
|
| 361 |
-
def
|
| 362 |
-
"""
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 370 |
|
| 371 |
市场数据摘要:
|
| 372 |
{market_data}
|
| 373 |
|
| 374 |
-
请
|
| 375 |
-
1
|
| 376 |
-
2
|
| 377 |
-
3
|
| 378 |
-
4
|
| 379 |
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
|
|
|
| 383 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
class TradingPlatform:
|
| 385 |
"""主交易平台"""
|
| 386 |
|
| 387 |
-
def __init__(self):
|
| 388 |
-
self.trainer = MathematicalTrainer()
|
| 389 |
self.llm = LLMInterface()
|
| 390 |
self.current_data = None
|
| 391 |
self.analysis_results = {}
|
|
@@ -473,7 +727,43 @@ class TradingPlatform:
|
|
| 473 |
except Exception as e:
|
| 474 |
return f"训练失败: {str(e)}", None
|
| 475 |
|
| 476 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
"""获取交易策略"""
|
| 478 |
if not self.analysis_results:
|
| 479 |
return "请先上传并分析数据"
|
|
@@ -484,14 +774,18 @@ class TradingPlatform:
|
|
| 484 |
if self.current_data is not None:
|
| 485 |
numeric_cols = self.current_data.select_dtypes(include=[np.number]).columns
|
| 486 |
if len(numeric_cols) > 0:
|
| 487 |
-
latest_data = self.current_data[numeric_cols].tail(
|
| 488 |
market_summary = latest_data.to_string()
|
| 489 |
|
| 490 |
# 合并用户问题
|
| 491 |
full_prompt = f"{user_question}\n\n当前分析结果:{json.dumps(self.analysis_results, indent=2)}\n\n市场数据:{market_summary}"
|
| 492 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 493 |
# 获取LLM回复
|
| 494 |
-
response = self.llm.analyze_trading(self.analysis_results, full_prompt)
|
| 495 |
|
| 496 |
return response
|
| 497 |
|
|
@@ -504,14 +798,13 @@ def create_interface():
|
|
| 504 |
|
| 505 |
with gr.Blocks(title="港股智能分析平台", theme=gr.themes.Soft()) as interface:
|
| 506 |
gr.Markdown("""
|
| 507 |
-
# 🚀 港股智能分析平台 - 数学原理驱动
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
-
|
| 511 |
-
-
|
| 512 |
-
-
|
| 513 |
-
-
|
| 514 |
-
- 🤖 **LLM策略生成**:基于数学分析的交易建议
|
| 515 |
""")
|
| 516 |
|
| 517 |
with gr.Tabs():
|
|
@@ -547,6 +840,23 @@ def create_interface():
|
|
| 547 |
outputs=[data_summary, analysis_result, status_text]
|
| 548 |
)
|
| 549 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 550 |
# 模型训练标签
|
| 551 |
with gr.TabItem("🧮 XPINNs模型训练"):
|
| 552 |
with gr.Row():
|
|
@@ -577,15 +887,13 @@ def create_interface():
|
|
| 577 |
# 交易策略标签
|
| 578 |
with gr.TabItem("💹 智能交易策略"):
|
| 579 |
gr.Markdown("""
|
| 580 |
-
### 基于数学原理的交易策略生成
|
| 581 |
|
| 582 |
系统将结合:
|
| 583 |
- 纤维丛投影的市场状态
|
| 584 |
- 因果VAR的动态关系
|
| 585 |
-
-
|
| 586 |
-
- LLM智能推理
|
| 587 |
-
|
| 588 |
-
为您生成专业的日内交易策略。
|
| 589 |
""")
|
| 590 |
|
| 591 |
with gr.Row():
|
|
@@ -599,16 +907,18 @@ def create_interface():
|
|
| 599 |
|
| 600 |
user_input = gr.Textbox(
|
| 601 |
label="交易问题",
|
| 602 |
-
placeholder="例如:基于当前分析,
|
| 603 |
lines=3
|
| 604 |
)
|
| 605 |
|
|
|
|
|
|
|
| 606 |
strategy_btn = gr.Button("生成策略", variant="primary")
|
| 607 |
|
| 608 |
with gr.Column():
|
| 609 |
strategy_output = gr.Textbox(
|
| 610 |
label="AI交易策略建议",
|
| 611 |
-
lines=
|
| 612 |
interactive=False
|
| 613 |
)
|
| 614 |
|
|
@@ -624,54 +934,46 @@ def create_interface():
|
|
| 624 |
|
| 625 |
strategy_btn.click(
|
| 626 |
platform.get_trading_strategy,
|
| 627 |
-
inputs=[user_input],
|
| 628 |
outputs=[strategy_output]
|
| 629 |
)
|
| 630 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 631 |
# 数学原理说明
|
| 632 |
with gr.TabItem("📚 数学原理"):
|
| 633 |
-
gr.Markdown("""
|
| 634 |
-
## 核心数学原理
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
-
|
| 638 |
-
-
|
| 639 |
-
- **总空间 E**: 高维市场状态
|
| 640 |
-
- **投影 π**: E → B 的降维映射
|
| 641 |
-
- **VRP截面**: s_VRP(b) = VIX² - RV
|
| 642 |
-
|
| 643 |
-
### 2. Whitney嵌入定理
|
| 644 |
-
- 保证2n维嵌入保留n维流形的所有微分结构
|
| 645 |
-
- 提供因子设计的理论下限
|
| 646 |
-
|
| 647 |
-
### 3. 梯度动力学
|
| 648 |
-
- **势函数**: U(b) = (VRP)²/2
|
| 649 |
-
- **演化方程**: db/dt = -η∇U(b) + σdW
|
| 650 |
-
- **李雅普诺夫稳定性**: V(t) ≤ V(0)e^(-αt)
|
| 651 |
-
|
| 652 |
-
### 4. SO(3)等变网络
|
| 653 |
-
- 保持旋转对称性: f(g·x) = g·f(x)
|
| 654 |
-
- 正交权重初始化
|
| 655 |
-
- 群作用下的不变性
|
| 656 |
-
|
| 657 |
-
### 5. XPINNs域分解
|
| 658 |
-
- 并行子域处理
|
| 659 |
-
- 界面条件约束
|
| 660 |
-
- 零曲率保证一致性
|
| 661 |
-
|
| 662 |
-
### 6. 因果VAR模型
|
| 663 |
-
- 动态因果发现
|
| 664 |
-
- Granger因果检验
|
| 665 |
-
- 脉冲响应分析
|
| 666 |
""")
|
| 667 |
|
| 668 |
gr.Markdown("""
|
| 669 |
---
|
| 670 |
### 使用说明:
|
| 671 |
1. 上传您的金融数据(CSV或Excel格式)
|
| 672 |
-
2.
|
| 673 |
-
3. 训练XPINNs模
|
| 674 |
-
4. 使用LLM生成
|
| 675 |
|
| 676 |
**注意**: 本系统仅供研究参考,不构成投资建议。
|
| 677 |
""")
|
|
@@ -680,10 +982,10 @@ def create_interface():
|
|
| 680 |
|
| 681 |
# 主函数
|
| 682 |
if __name__ == "__main__":
|
| 683 |
-
logger.info("启动港股智能分析平台...")
|
| 684 |
interface = create_interface()
|
| 685 |
interface.launch(
|
| 686 |
server_name="0.0.0.0",
|
| 687 |
server_port=7860,
|
| 688 |
share=False
|
| 689 |
-
)
|
|
|
|
| 2 |
"""
|
| 3 |
港股智能分析平台 - Hugging Face Spaces版本
|
| 4 |
集成数学原理的XPINNs训练与LLM推理系统
|
| 5 |
+
(已扩展:噪声分离、Whitney嵌入截面学习、梯度动力学模拟、LLM伪代码/日内策略)
|
| 6 |
"""
|
| 7 |
|
| 8 |
import os
|
|
|
|
| 65 |
def __init__(self, base_dim=2, fiber_dim=16):
|
| 66 |
self.base_dim = base_dim # VIX^2, RV
|
| 67 |
self.fiber_dim = fiber_dim # 隐藏状态维度
|
| 68 |
+
self.whitney_factor = 2 * fiber_dim # Whitney嵌入因子(理论上)
|
| 69 |
|
| 70 |
def project_to_base(self, high_dim_state):
|
| 71 |
"""投影到基空间"""
|
|
|
|
| 73 |
high_dim_state = np.pad(high_dim_state, (0, self.fiber_dim - len(high_dim_state)))
|
| 74 |
|
| 75 |
# 计算VIX^2代理
|
| 76 |
+
vix_squared = np.sum(high_dim_state[:self.fiber_dim//2]**2) / (self.fiber_dim//2 + 1e-12)
|
| 77 |
# 计算实现波动率
|
| 78 |
rv = np.std(high_dim_state[self.fiber_dim//2:])
|
| 79 |
|
|
|
|
| 84 |
vix_squared, rv = base_point
|
| 85 |
return vix_squared - rv
|
| 86 |
|
| 87 |
+
class NoiseExplorer:
|
| 88 |
+
"""
|
| 89 |
+
噪声分离与验证:基于 VIX^2 与 RV 的关系,尝试把观测上的噪声(纤维方向)与基空间信息分离。
|
| 90 |
+
简单实现:对 VIX^2 与 RV 做线性回归,分析残差的统计特性(自相关、能量谱),并给出 VRP 统计摘要。
|
| 91 |
+
以后可扩展为更严格的协整检验(Johansen / ADF)。
|
| 92 |
+
"""
|
| 93 |
+
def __init__(self):
|
| 94 |
+
pass
|
| 95 |
+
|
| 96 |
+
def regress_vix2_vs_rv(self, vix2_series, rv_series):
|
| 97 |
+
# 简单线性回归:vix2 = a * rv + b
|
| 98 |
+
X = np.vstack([rv_series, np.ones_like(rv_series)]).T
|
| 99 |
+
try:
|
| 100 |
+
coef, _, _, _ = np.linalg.lstsq(X, vix2_series, rcond=None)
|
| 101 |
+
a, b = coef[0], coef[1]
|
| 102 |
+
preds = a * rv_series + b
|
| 103 |
+
resid = vix2_series - preds
|
| 104 |
+
return {
|
| 105 |
+
'a': float(a), 'b': float(b),
|
| 106 |
+
'preds': preds, 'resid': resid
|
| 107 |
+
}
|
| 108 |
+
except Exception as e:
|
| 109 |
+
return None
|
| 110 |
+
|
| 111 |
+
def resid_stats(self, resid):
|
| 112 |
+
# 基本残差统计:均值、方差、自相关(lag1)、能量谱(简单FFT)
|
| 113 |
+
mean = float(np.mean(resid))
|
| 114 |
+
var = float(np.var(resid))
|
| 115 |
+
if len(resid) > 2:
|
| 116 |
+
ac1 = float(np.corrcoef(resid[:-1], resid[1:])[0,1])
|
| 117 |
+
else:
|
| 118 |
+
ac1 = 0.0
|
| 119 |
+
# FFT能量谱主频
|
| 120 |
+
try:
|
| 121 |
+
fft = np.fft.rfft(resid - mean)
|
| 122 |
+
freqs = np.fft.rfftfreq(len(resid))
|
| 123 |
+
power = np.abs(fft)**2
|
| 124 |
+
dominant_idx = int(np.argmax(power[1:]) + 1) if len(power) > 1 else 0
|
| 125 |
+
dominant_freq = float(freqs[dominant_idx]) if len(freqs) > dominant_idx else 0.0
|
| 126 |
+
except:
|
| 127 |
+
dominant_freq = 0.0
|
| 128 |
+
return {'mean':mean, 'var':var, 'ac1':ac1, 'dominant_freq':dominant_freq}
|
| 129 |
+
|
| 130 |
+
def explore(self, df, vix2_col=None, rv_col=None):
|
| 131 |
+
# 自动寻找列名
|
| 132 |
+
numeric = df.select_dtypes(include=[np.number]).columns.tolist()
|
| 133 |
+
if vix2_col is None or rv_col is None:
|
| 134 |
+
# 尝试匹配
|
| 135 |
+
candidates = [c.lower() for c in numeric]
|
| 136 |
+
vix_col = None
|
| 137 |
+
rv_col_local = None
|
| 138 |
+
for c in numeric:
|
| 139 |
+
if 'vix' in c.lower():
|
| 140 |
+
vix_col = c
|
| 141 |
+
if 'rv' in c.lower() or 'realized' in c.lower():
|
| 142 |
+
rv_col_local = c
|
| 143 |
+
if vix_col is None or rv_col_local is None:
|
| 144 |
+
# 退回到前两列
|
| 145 |
+
if len(numeric) >= 2:
|
| 146 |
+
vix_col, rv_col_local = numeric[0], numeric[1]
|
| 147 |
+
else:
|
| 148 |
+
return None
|
| 149 |
+
vix2_col, rv_col = vix_col, rv_col_local
|
| 150 |
+
|
| 151 |
+
vix2 = df[vix2_col].fillna(method='ffill').values
|
| 152 |
+
rv = df[rv_col].fillna(method='ffill').values
|
| 153 |
+
|
| 154 |
+
reg = self.regress_vix2_vs_rv(vix2, rv)
|
| 155 |
+
if reg is None:
|
| 156 |
+
return None
|
| 157 |
+
stats = self.resid_stats(reg['resid'])
|
| 158 |
+
vrp_series = vix2 - reg['preds']
|
| 159 |
+
# 返回摘要
|
| 160 |
+
return {
|
| 161 |
+
'vix2_col': vix2_col,
|
| 162 |
+
'rv_col': rv_col,
|
| 163 |
+
'reg_coeff': {'a':reg['a'], 'b':reg['b']},
|
| 164 |
+
'resid_stats': stats,
|
| 165 |
+
'vrp_mean': float(np.mean(vrp_series)),
|
| 166 |
+
'vrp_std': float(np.std(vrp_series)),
|
| 167 |
+
'vrp_series': vrp_series,
|
| 168 |
+
'reg_pred': reg['preds'],
|
| 169 |
+
'residuals': reg['resid']
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
class WhitneyEmbedder(nn.Module):
|
| 173 |
+
"""
|
| 174 |
+
基于 Whitney 嵌入思想的简单 autoencoder 与截面学习网络:
|
| 175 |
+
- autoencoder 用于学习从高维观测到低维(whitney_factor)的光滑嵌入
|
| 176 |
+
- section_net 将 base (VIX^2, RV) 映射回 fiber(截面估计),用于自演化/再构造
|
| 177 |
+
"""
|
| 178 |
+
def __init__(self, input_dim=64, fiber_dim=16, device='cpu'):
|
| 179 |
+
super().__init__()
|
| 180 |
+
self.device = device
|
| 181 |
+
self.fiber_dim = fiber_dim
|
| 182 |
+
self.whitney_dim = 2 * fiber_dim # 推荐维度
|
| 183 |
+
# encoder / decoder
|
| 184 |
+
self.encoder = nn.Sequential(
|
| 185 |
+
nn.Linear(input_dim, 128),
|
| 186 |
+
nn.ReLU(),
|
| 187 |
+
nn.Linear(128, self.whitney_dim)
|
| 188 |
+
)
|
| 189 |
+
self.decoder = nn.Sequential(
|
| 190 |
+
nn.Linear(self.whitney_dim, 128),
|
| 191 |
+
nn.ReLU(),
|
| 192 |
+
nn.Linear(128, input_dim)
|
| 193 |
+
)
|
| 194 |
+
# 截面学习网络:从 base (2维) -> fiber_dim
|
| 195 |
+
self.section_net = nn.Sequential(
|
| 196 |
+
nn.Linear(2, 32),
|
| 197 |
+
nn.ReLU(),
|
| 198 |
+
nn.Linear(32, fiber_dim)
|
| 199 |
+
)
|
| 200 |
+
self.to(self.device)
|
| 201 |
+
|
| 202 |
+
def forward(self, x):
|
| 203 |
+
z = self.encoder(x)
|
| 204 |
+
recon = self.decoder(z)
|
| 205 |
+
return z, recon
|
| 206 |
+
|
| 207 |
+
def learn_section(self, base_points):
|
| 208 |
+
"""
|
| 209 |
+
给定 base_points (N x 2),输出 fiber 估计(N x fiber_dim)
|
| 210 |
+
这是一个直接前向调用(训练通过 train_embedding 来执行)
|
| 211 |
+
"""
|
| 212 |
+
with torch.no_grad():
|
| 213 |
+
x = torch.tensor(base_points, dtype=torch.float32, device=self.device)
|
| 214 |
+
return self.section_net(x).cpu().numpy()
|
| 215 |
+
|
| 216 |
+
class GradientDynamics:
|
| 217 |
+
"""
|
| 218 |
+
把势函数 U(b) 的梯度流当成市场的动力路径:db = -eta * grad U(b) dt + sigma dW
|
| 219 |
+
- 能把离散梯度下降(epochs 步)映射为连续路径的模拟
|
| 220 |
+
- 使用 torch 自动求导对 U 做梯度(U可以是任意torch可微函数)
|
| 221 |
+
"""
|
| 222 |
+
def __init__(self, eta=0.1, sigma=0.01, device='cpu'):
|
| 223 |
+
self.eta = eta
|
| 224 |
+
self.sigma = sigma
|
| 225 |
+
self.device = device
|
| 226 |
+
|
| 227 |
+
def U_vrp(self, b):
|
| 228 |
+
"""
|
| 229 |
+
默认势函数:U(b) = (VRP)^2 / 2 ,其中 b = [vix2, rv]
|
| 230 |
+
b 为 torch.tensor shape (..., 2)
|
| 231 |
+
"""
|
| 232 |
+
vix2 = b[...,0]
|
| 233 |
+
rv = b[...,1]
|
| 234 |
+
vrp = vix2 - rv
|
| 235 |
+
return 0.5 * vrp**2
|
| 236 |
+
|
| 237 |
+
def grad_U(self, b):
|
| 238 |
+
b_t = torch.tensor(b, dtype=torch.float32, requires_grad=True, device=self.device)
|
| 239 |
+
U = self.U_vrp(b_t).sum()
|
| 240 |
+
U.backward()
|
| 241 |
+
grad = b_t.grad.detach().cpu().numpy()
|
| 242 |
+
return grad
|
| 243 |
+
|
| 244 |
+
def simulate_flow(self, b0, T=1.0, dt=0.01, seed=None):
|
| 245 |
+
"""
|
| 246 |
+
Euler-Maruyama 模拟:
|
| 247 |
+
b0: 初始点 (2,)
|
| 248 |
+
返回路径 (Nsteps+1, 2)
|
| 249 |
+
"""
|
| 250 |
+
if seed is not None:
|
| 251 |
+
np.random.seed(seed)
|
| 252 |
+
n_steps = int(T / dt)
|
| 253 |
+
path = np.zeros((n_steps+1, 2))
|
| 254 |
+
path[0] = np.array(b0, dtype=float)
|
| 255 |
+
for i in range(n_steps):
|
| 256 |
+
b_cur = path[i]
|
| 257 |
+
grad = self.grad_U(b_cur)
|
| 258 |
+
db_det = - self.eta * grad
|
| 259 |
+
db_stoch = self.sigma * np.sqrt(dt) * np.random.randn(2)
|
| 260 |
+
path[i+1] = b_cur + db_det * dt + db_stoch
|
| 261 |
+
return path
|
| 262 |
+
|
| 263 |
class EquivariantLayer(nn.Module):
|
| 264 |
"""SO(3)等变层"""
|
| 265 |
|
|
|
|
| 407 |
class MathematicalTrainer:
|
| 408 |
"""数学原理驱动的训练器"""
|
| 409 |
|
| 410 |
+
def __init__(self, device='cpu'):
|
| 411 |
+
self.device = device
|
| 412 |
+
self.fiber_bundle = FiberBundleTheory(fiber_dim=config.FIBER_BUNDLE_DIM)
|
| 413 |
+
self.causal_var = CausalVAR(max_lag=config.CAUSAL_LAG)
|
| 414 |
self.model = XPINNsGenerator()
|
| 415 |
self.optimizer = Adam(self.model.parameters(), lr=0.001)
|
| 416 |
self.scaler = StandardScaler()
|
| 417 |
+
# 新增:
|
| 418 |
+
self.noise_explorer = NoiseExplorer()
|
| 419 |
+
self.embedder = None # lazy init
|
| 420 |
+
self.gradient_dynamics = GradientDynamics(eta=0.5, sigma=0.02, device=device)
|
| 421 |
|
| 422 |
def prepare_data(self, df):
|
| 423 |
"""准备训练数据"""
|
|
|
|
| 506 |
logger.info(f"Epoch {epoch}: Loss = {avg_loss:.6f}")
|
| 507 |
|
| 508 |
return losses
|
| 509 |
+
|
| 510 |
+
# ---------- 新增:嵌入器训练接口 ----------
|
| 511 |
+
def init_embedder(self, input_dim=64):
|
| 512 |
+
if self.embedder is None:
|
| 513 |
+
self.embedder = WhitneyEmbedder(input_dim=input_dim, fiber_dim=self.fiber_bundle.fiber_dim, device=self.device)
|
| 514 |
+
|
| 515 |
+
def train_embedding(self, X_np, epochs=100, lr=1e-3):
|
| 516 |
+
"""
|
| 517 |
+
训练 autoencoder,X_np: numpy array (N, input_dim)
|
| 518 |
+
"""
|
| 519 |
+
self.init_embedder(input_dim=X_np.shape[1])
|
| 520 |
+
model = self.embedder
|
| 521 |
+
opt = Adam(model.parameters(), lr=lr)
|
| 522 |
+
X = torch.tensor(X_np, dtype=torch.float32, device=self.device)
|
| 523 |
+
for epoch in range(epochs):
|
| 524 |
+
opt.zero_grad()
|
| 525 |
+
z, recon = model(X)
|
| 526 |
+
loss = F.mse_loss(recon, X) # 重构损失
|
| 527 |
+
loss.backward()
|
| 528 |
+
opt.step()
|
| 529 |
+
if epoch % 20 == 0:
|
| 530 |
+
logger.info(f"[Embedder] Epoch {epoch}, recon loss {loss.item():.6f}")
|
| 531 |
+
return loss.item()
|
| 532 |
+
|
| 533 |
+
def explore_noise(self, df, vix2_col=None, rv_col=None):
|
| 534 |
+
return self.noise_explorer.explore(df, vix2_col=vix2_col, rv_col=rv_col)
|
| 535 |
+
|
| 536 |
+
def simulate_gradient_flow(self, initial_base_point, T=1.0, dt=0.01):
|
| 537 |
+
return self.gradient_dynamics.simulate_flow(initial_base_point, T=T, dt=dt)
|
| 538 |
|
| 539 |
class LLMInterface:
|
| 540 |
"""LLM接口"""
|
|
|
|
| 551 |
"inputs": prompt,
|
| 552 |
"parameters": {
|
| 553 |
"max_new_tokens": max_length,
|
| 554 |
+
"temperature": 0.3,
|
| 555 |
"top_p": 0.9,
|
| 556 |
"do_sample": True
|
| 557 |
}
|
|
|
|
| 569 |
except Exception as e:
|
| 570 |
return f"错误: {str(e)}"
|
| 571 |
|
| 572 |
+
def _pseudocode_template(self, strategy_spec: Dict[str,Any]) -> str:
|
| 573 |
+
"""
|
| 574 |
+
当LLM不可用时,返回一个确定性的伪代码结构
|
| 575 |
+
strategy_spec 包含:entry_rule, exit_rule, position_sizing, risk_params
|
| 576 |
+
"""
|
| 577 |
+
template = f"""# PSEUDOCODE for intraday quant strategy
|
| 578 |
+
# Entry: {strategy_spec.get('entry_rule','待定')}
|
| 579 |
+
# Exit: {strategy_spec.get('exit_rule','待定')}
|
| 580 |
+
# Position sizing: {strategy_spec.get('position_sizing','固定仓位/比例')}
|
| 581 |
+
# Risk: {strategy_spec.get('risk_params','默认')}
|
| 582 |
+
def on_bar(bar):
|
| 583 |
+
features = compute_features(bar) # e.g. VRP, momentum, spread
|
| 584 |
+
signal = 0
|
| 585 |
+
if {strategy_spec.get('entry_condition_code','False')}:
|
| 586 |
+
signal = 1
|
| 587 |
+
entry_price = bar.close
|
| 588 |
+
size = determine_size(entry_price)
|
| 589 |
+
if {strategy_spec.get('exit_condition_code','False')}:
|
| 590 |
+
signal = -1
|
| 591 |
+
manage_risk()
|
| 592 |
+
execute(signal, size)
|
| 593 |
+
"""
|
| 594 |
+
return template
|
| 595 |
+
|
| 596 |
+
def analyze_trading(self, analysis_results, market_data, intraday=False, generate_pseudocode=False):
|
| 597 |
+
"""分析交易策略,扩展:日内与伪代码生成"""
|
| 598 |
+
prompt = f"""你是一个量化交易与日内交易专家。请基于以下数学分析结果和市场数据生成可操作的交易策略。
|
| 599 |
+
|
| 600 |
+
数学分析摘要:
|
| 601 |
+
{json.dumps(analysis_results, indent=2)}
|
| 602 |
|
| 603 |
市场数据摘要:
|
| 604 |
{market_data}
|
| 605 |
|
| 606 |
+
请给出:
|
| 607 |
+
1) 简洁的策略描述(入场、止损、止盈、仓位管理)
|
| 608 |
+
2) 若为日内(intraday=True),请给出明确定义的入场/退出信号(基于短周期,例如1-5分钟或tick),并说明延迟/滑点考虑
|
| 609 |
+
3) 风险控制与回测建议(数据频率、回测窗口)
|
| 610 |
+
4) 若要求generate_pseudocode=True,请以伪代码形式输出策略实现模板(明确函数名、输入特征、信号判断、止损/止盈逻辑)
|
| 611 |
|
| 612 |
+
输出格式:
|
| 613 |
+
- 段落 1: 策略概览
|
| 614 |
+
- 段落 2: 规则要点(枚举)
|
| 615 |
+
- 段落 3: 伪代码(如果要求)
|
| 616 |
|
| 617 |
+
请尽量简洁、直接给出可执行的建议。
|
| 618 |
+
"""
|
| 619 |
+
if generate_pseudocode:
|
| 620 |
+
prompt += "\n请在伪代码中包含:compute_features(), determine_size(), execute() 等函数签名。\n"
|
| 621 |
+
# 调用LLM
|
| 622 |
+
llm_resp = self.query(prompt, max_length=800)
|
| 623 |
+
# 如果返回是错误或API失败,fallback到确定性伪代码
|
| 624 |
+
if isinstance(llm_resp, str) and llm_resp.startswith("错误") or "API调用失败" in str(llm_resp):
|
| 625 |
+
# 构造简单策略说明
|
| 626 |
+
strategy_spec = {
|
| 627 |
+
'entry_rule': '当 VRP 从负值上穿其短期均值且短期动量为正时买入',
|
| 628 |
+
'exit_rule': '亏损超过止损点或达到止盈点或VRP反转',
|
| 629 |
+
'position_sizing': '账户风险百分比方式(例如每单最大亏损 0.5%)',
|
| 630 |
+
'risk_params': '止损与仓位受限,滑点假设 0.02%',
|
| 631 |
+
'entry_condition_code': 'features["vrp"] > features["vrp_sma_short"] and features["mom"] > 0',
|
| 632 |
+
'exit_condition_code': 'price <= entry_price*(1 - stop_loss_pct) or price >= entry_price*(1 + take_profit_pct)'
|
| 633 |
+
}
|
| 634 |
+
fallback = "LLM不可用,返回内置伪代码模板。\n\n" + self._pseudocode_template(strategy_spec)
|
| 635 |
+
return fallback
|
| 636 |
+
return llm_resp
|
| 637 |
+
|
| 638 |
class TradingPlatform:
|
| 639 |
"""主交易平台"""
|
| 640 |
|
| 641 |
+
def __init__(self, device='cpu'):
|
| 642 |
+
self.trainer = MathematicalTrainer(device=device)
|
| 643 |
self.llm = LLMInterface()
|
| 644 |
self.current_data = None
|
| 645 |
self.analysis_results = {}
|
|
|
|
| 727 |
except Exception as e:
|
| 728 |
return f"训练失败: {str(e)}", None
|
| 729 |
|
| 730 |
+
def run_noise_exploration(self):
|
| 731 |
+
"""运行噪声探索(基于 VIX^2 vs RV 回归)"""
|
| 732 |
+
if self.current_data is None:
|
| 733 |
+
return "请先上传数据", None
|
| 734 |
+
try:
|
| 735 |
+
res = self.trainer.explore_noise(self.current_data)
|
| 736 |
+
if res is None:
|
| 737 |
+
return "未找到合适的数值列进行噪声探索", None
|
| 738 |
+
summary = f"噪声探索结果: VRP 均值 {res['vrp_mean']:.6f}, VRP std {res['vrp_std']:.6f}, 残差自相关(1) {res['resid_stats']['ac1']:.4f}"
|
| 739 |
+
return summary, res
|
| 740 |
+
except Exception as e:
|
| 741 |
+
return f"噪声探索失败: {str(e)}", None
|
| 742 |
+
|
| 743 |
+
def train_embedding(self, epochs=100):
|
| 744 |
+
"""训练 Whitney 嵌入(autoencoder)"""
|
| 745 |
+
if self.current_data is None:
|
| 746 |
+
return "请先上传数据", None
|
| 747 |
+
try:
|
| 748 |
+
# 复用之前prepare_data的X构造方式,得到特征矩阵
|
| 749 |
+
X, y, causal = self.trainer.prepare_data(self.current_data)
|
| 750 |
+
if X is None:
|
| 751 |
+
return "数据不足以训练嵌入", None
|
| 752 |
+
X_np = X.numpy()
|
| 753 |
+
final_loss = self.trainer.train_embedding(X_np, epochs=epochs)
|
| 754 |
+
return f"嵌入训练完成, 最终重构损失 {final_loss:.6f}", None
|
| 755 |
+
except Exception as e:
|
| 756 |
+
return f"嵌入训练失败: {str(e)}", None
|
| 757 |
+
|
| 758 |
+
def simulate_dynamics(self, start_vix2=1.0, start_rv=0.8, T=1.0, dt=0.01):
|
| 759 |
+
"""模拟梯度动力学路径"""
|
| 760 |
+
try:
|
| 761 |
+
path = self.trainer.simulate_gradient_flow([start_vix2, start_rv], T=T, dt=dt)
|
| 762 |
+
return f"模拟完成,路径长度 {len(path)}", path
|
| 763 |
+
except Exception as e:
|
| 764 |
+
return f"模拟失败: {str(e)}", None
|
| 765 |
+
|
| 766 |
+
def get_trading_strategy(self, user_question, intraday=False, generate_pseudocode=False, model_name=None):
|
| 767 |
"""获取交易策略"""
|
| 768 |
if not self.analysis_results:
|
| 769 |
return "请先上传并分析数据"
|
|
|
|
| 774 |
if self.current_data is not None:
|
| 775 |
numeric_cols = self.current_data.select_dtypes(include=[np.number]).columns
|
| 776 |
if len(numeric_cols) > 0:
|
| 777 |
+
latest_data = self.current_data[numeric_cols].tail(20).describe()
|
| 778 |
market_summary = latest_data.to_string()
|
| 779 |
|
| 780 |
# 合并用户问题
|
| 781 |
full_prompt = f"{user_question}\n\n当前分析结果:{json.dumps(self.analysis_results, indent=2)}\n\n市场数据:{market_summary}"
|
| 782 |
|
| 783 |
+
# 更新llm模型(可选)
|
| 784 |
+
if model_name:
|
| 785 |
+
self.llm = LLMInterface(model_name)
|
| 786 |
+
|
| 787 |
# 获取LLM回复
|
| 788 |
+
response = self.llm.analyze_trading(self.analysis_results, full_prompt, intraday=intraday, generate_pseudocode=generate_pseudocode)
|
| 789 |
|
| 790 |
return response
|
| 791 |
|
|
|
|
| 798 |
|
| 799 |
with gr.Blocks(title="港股智能分析平台", theme=gr.themes.Soft()) as interface:
|
| 800 |
gr.Markdown("""
|
| 801 |
+
# 🚀 港股智能分析平台 - 数学原理驱动(扩展版)
|
| 802 |
+
|
| 803 |
+
新增功能:
|
| 804 |
+
- 噪声分离 / VRP 残差分析
|
| 805 |
+
- 基于 Whitney 嵌入的 autoencoder 与截面学习
|
| 806 |
+
- 梯度动力学(将梯度下降视作连续演化)模拟
|
| 807 |
+
- LLM 支持日内策略与伪代码输出(集成/回退模板)
|
|
|
|
| 808 |
""")
|
| 809 |
|
| 810 |
with gr.Tabs():
|
|
|
|
| 840 |
outputs=[data_summary, analysis_result, status_text]
|
| 841 |
)
|
| 842 |
|
| 843 |
+
# 噪声探索与嵌入训练
|
| 844 |
+
with gr.TabItem("🔍 噪声探索 & 嵌入"):
|
| 845 |
+
with gr.Row():
|
| 846 |
+
with gr.Column():
|
| 847 |
+
noise_btn = gr.Button("运行噪声探索 (VIX^2 vs RV)", variant="primary")
|
| 848 |
+
noise_summary = gr.Textbox(label="噪声探索摘要", lines=4, interactive=False)
|
| 849 |
+
noise_details = gr.JSON(label="噪声探索详细结果", visible=False)
|
| 850 |
+
embed_epochs = gr.Slider(minimum=20, maximum=500, value=100, step=20, label="嵌入训练轮数")
|
| 851 |
+
embed_btn = gr.Button("训练 Whitney 嵌��", variant="primary")
|
| 852 |
+
embed_status = gr.Textbox(label="嵌入训练状态", lines=2, interactive=False)
|
| 853 |
+
|
| 854 |
+
with gr.Column():
|
| 855 |
+
embed_plot = gr.Plot(label="嵌入(可视化)", visible=False)
|
| 856 |
+
|
| 857 |
+
noise_btn.click(platform.run_noise_exploration, inputs=[], outputs=[noise_summary, noise_details])
|
| 858 |
+
embed_btn.click(platform.train_embedding, inputs=[embed_epochs], outputs=[embed_status, embed_plot])
|
| 859 |
+
|
| 860 |
# 模型训练标签
|
| 861 |
with gr.TabItem("🧮 XPINNs模型训练"):
|
| 862 |
with gr.Row():
|
|
|
|
| 887 |
# 交易策略标签
|
| 888 |
with gr.TabItem("💹 智能交易策略"):
|
| 889 |
gr.Markdown("""
|
| 890 |
+
### 基于数学原理的交易策略生成(扩展)
|
| 891 |
|
| 892 |
系统将结合:
|
| 893 |
- 纤维丛投影的市场状态
|
| 894 |
- 因果VAR的动态关系
|
| 895 |
+
- 梯度动力学的演化模拟
|
| 896 |
+
- LLM智能推理(可输出伪代码)
|
|
|
|
|
|
|
| 897 |
""")
|
| 898 |
|
| 899 |
with gr.Row():
|
|
|
|
| 907 |
|
| 908 |
user_input = gr.Textbox(
|
| 909 |
label="交易问题",
|
| 910 |
+
placeholder="例如:基于当前分析,今天的日内策略如何构造?请给出伪代码。",
|
| 911 |
lines=3
|
| 912 |
)
|
| 913 |
|
| 914 |
+
intraday_check = gr.Checkbox(label="日内策略 (intraday)", value=True)
|
| 915 |
+
pseudocode_check = gr.Checkbox(label="生成伪代码", value=True)
|
| 916 |
strategy_btn = gr.Button("生成策略", variant="primary")
|
| 917 |
|
| 918 |
with gr.Column():
|
| 919 |
strategy_output = gr.Textbox(
|
| 920 |
label="AI交易策略建议",
|
| 921 |
+
lines=20,
|
| 922 |
interactive=False
|
| 923 |
)
|
| 924 |
|
|
|
|
| 934 |
|
| 935 |
strategy_btn.click(
|
| 936 |
platform.get_trading_strategy,
|
| 937 |
+
inputs=[user_input, intraday_check, pseudocode_check, model_dropdown],
|
| 938 |
outputs=[strategy_output]
|
| 939 |
)
|
| 940 |
|
| 941 |
+
# 梯度动力学模拟
|
| 942 |
+
with gr.TabItem("⚙️ 梯度动力学模拟"):
|
| 943 |
+
with gr.Row():
|
| 944 |
+
with gr.Column():
|
| 945 |
+
start_vix2 = gr.Number(value=1.0, label="初始 VIX^2")
|
| 946 |
+
start_rv = gr.Number(value=0.8, label="初始 RV")
|
| 947 |
+
T = gr.Number(value=1.0, label="模拟总时间 T")
|
| 948 |
+
dt = gr.Number(value=0.01, label="时间步长 dt")
|
| 949 |
+
sim_btn = gr.Button("运行模拟", variant="primary")
|
| 950 |
+
sim_result = gr.Textbox(label="模拟结果", lines=3, interactive=False)
|
| 951 |
+
with gr.Column():
|
| 952 |
+
sim_plot = gr.Plot(label="模拟路径 (VIX^2, RV)")
|
| 953 |
+
|
| 954 |
+
sim_btn.click(
|
| 955 |
+
platform.simulate_dynamics,
|
| 956 |
+
inputs=[start_vix2, start_rv, T, dt],
|
| 957 |
+
outputs=[sim_result, sim_plot]
|
| 958 |
+
)
|
| 959 |
+
|
| 960 |
# 数学原理说明
|
| 961 |
with gr.TabItem("📚 数学原理"):
|
| 962 |
+
gr.Markdown(f"""
|
| 963 |
+
## 核心数学原理(参考:你的笔记与论文)
|
| 964 |
+
- 纤维丛理论与VRP截面:参见上传的改进文档(BKKK改进)。:contentReference[oaicite:3]{index=3}
|
| 965 |
+
- Whitney嵌入定理与光滑嵌入:作为嵌入维度上限的理论依据。:contentReference[oaicite:4]{index=4}
|
| 966 |
+
- 等变网络与XPINNs:关于等变与几何保证的更系统论文。:contentReference[oaicite:5]{index=5}
|
| 967 |
+
- 梯度动力学视角:梯度下降路径可看作系统的演化路径、可扩展到SDE与Fokker-Planck 表述。:contentReference[oaicite:6]{index=6}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 968 |
""")
|
| 969 |
|
| 970 |
gr.Markdown("""
|
| 971 |
---
|
| 972 |
### 使用说明:
|
| 973 |
1. 上传您的金融数据(CSV或Excel格式)
|
| 974 |
+
2. 运行噪声探索以获得 VRP/残差统计
|
| 975 |
+
3. 训练嵌入 / XPINNs 并模拟梯度动力学
|
| 976 |
+
4. 使用 LLM 生成日内/量化策略并选择是否输出伪代码
|
| 977 |
|
| 978 |
**注意**: 本系统仅供研究参考,不构成投资建议。
|
| 979 |
""")
|
|
|
|
| 982 |
|
| 983 |
# 主函数
|
| 984 |
if __name__ == "__main__":
|
| 985 |
+
logger.info("启动港股智能分析平台(扩展版)...")
|
| 986 |
interface = create_interface()
|
| 987 |
interface.launch(
|
| 988 |
server_name="0.0.0.0",
|
| 989 |
server_port=7860,
|
| 990 |
share=False
|
| 991 |
+
)
|