SpectralStrategy更新

This commit is contained in:
2025-11-29 16:35:02 +08:00
parent 29199f9492
commit 687d8a180b
35 changed files with 40381 additions and 1153 deletions

View File

@@ -25,12 +25,12 @@ class ResultAnalyzer:
"""
def __init__(
self,
portfolio_snapshots: List[PortfolioSnapshot],
trade_history: List[Trade],
bars: List[Bar],
initial_capital: float,
indicator_list: List[Indicator] = [],
self,
portfolio_snapshots: List[PortfolioSnapshot],
trade_history: List[Trade],
bars: List[Bar],
initial_capital: float,
indicator_list: List[Indicator] = [],
):
"""
Args:
@@ -234,7 +234,7 @@ class ResultAnalyzer:
# 绘制最大值标注
plt.axvline(optimal_indi_value, color="red", linestyle="--", alpha=0.7)
plt.annotate(
f"Max Cum. PnL: {max_cumulative_pnl:.2f}",
f"optimal_indi_value: {optimal_indi_value:.4f}",
xy=(optimal_indi_value, max_cumulative_pnl),
xytext=(max_xytext_x, max_cumulative_pnl),
arrowprops=dict(facecolor="red", shrink=0.05),
@@ -247,7 +247,7 @@ class ResultAnalyzer:
plt.axvline(min_indi_value_at_pnl, color="blue", linestyle=":", alpha=0.7)
min_text_y_offset = -(max_cumulative_pnl - min_cumulative_pnl) * 0.1
plt.annotate(
f"Min Cum. PnL: {min_cumulative_pnl:.2f}",
f"min_indi_value_at_pnl: {min_indi_value_at_pnl:.4f}",
xy=(min_indi_value_at_pnl, min_cumulative_pnl),
xytext=(min_xytext_x, min_cumulative_pnl + min_text_y_offset),
arrowprops=dict(facecolor="blue", shrink=0.05),
@@ -265,3 +265,157 @@ class ResultAnalyzer:
plt.show()
print("\n所有指标分析完成。")
def analyze_indicators_v2(self, profit_offset: float = 0.0) -> None:
"""
分析指标值区间与盈亏的关系。
核心逻辑:
1. 按指标值排序。
2. 计算累积盈亏。
3. 找出累积盈亏曲线上涨幅度最大的一段,即为“最佳盈利区间”。
"""
# 1. 分离开仓和平仓
open_trades = [t for t in self.trade_history if t.is_open_trade]
close_trades = [t for t in self.trade_history if t.is_close_trade]
if not close_trades:
print("没有平仓交易可供分析。")
return
num_pairs = min(len(open_trades), len(close_trades))
if num_pairs == 0:
return
print(f"正在分析 {num_pairs} 组交易...")
for indicator in self.indicator_list:
indicator_name = indicator.get_name()
indi_values = []
pnls = []
# 2. 收集数据
for i in range(num_pairs):
open_trade = open_trades[i]
close_trade = close_trades[i]
if (open_trade.indicator_dict is not None and
indicator_name in open_trade.indicator_dict):
value = open_trade.indicator_dict[indicator_name]
if not (isinstance(value, float) and np.isnan(value)):
indi_values.append(value)
pnls.append(close_trade.realized_pnl - profit_offset)
if not indi_values:
continue
# 3. 创建 DataFrame 并清洗
df = pd.DataFrame({
"indicator_value": indi_values,
"realized_pnl": pnls
})
# 去极值
def remove_extreme(d, col='indicator_value', k=3):
q1, q3 = d[col].quantile([0.25, 0.75])
iqr = q3 - q1
mask = d[col].between(q1 - k * iqr, q3 + k * iqr)
return d[mask].copy()
df = remove_extreme(df)
if df.empty:
continue
# ==========================================================
# 4. 核心计算:排序与累积
# ==========================================================
df = df.sort_values(by="indicator_value").reset_index(drop=True)
df["cumulative_pnl"] = df["realized_pnl"].cumsum()
x_values = df["indicator_value"].values
y_values = df["cumulative_pnl"].values
# ==========================================================
# 5. 寻找“最佳盈利区间”算法
# 目标:找到索引 i 和 j (i < j),使得 y[j] - y[i] 最大
# ==========================================================
min_pnl_so_far = float('inf')
min_idx_so_far = -1
best_profit = -float('inf')
start_idx = -1
end_idx = -1
# 简单的线性扫描算法 O(N)
for idx, current_pnl in enumerate(y_values):
# 更新此前的最低点(作为潜在的起点)
if current_pnl < min_pnl_so_far:
min_pnl_so_far = current_pnl
min_idx_so_far = idx
# 计算如果在当前点卖出,从最低点买入能赚多少
current_drawup = current_pnl - min_pnl_so_far
if current_drawup > best_profit:
best_profit = current_drawup
start_idx = min_idx_so_far
end_idx = idx
# 获取最佳区间的数值
best_start_val = x_values[start_idx] if start_idx != -1 else x_values[0]
best_end_val = x_values[end_idx] if end_idx != -1 else x_values[-1]
# 同时也获取全局最低点和最高点用于展示
global_min_idx = np.argmin(y_values)
global_max_idx = np.argmax(y_values)
# ==========================================================
# 6. 绘图
# ==========================================================
plt.figure(figsize=(12, 7))
# 绘制主曲线
plt.plot(x_values, y_values, label="Cumulative PnL", color='#1f77b4', drawstyle='steps-post')
# --- 标记 A: 全局最低点 (蓝点) ---
plt.plot(x_values[global_min_idx], y_values[global_min_idx], 'v', color='blue', markersize=8,
label='Global Min')
# --- 标记 B: 全局最高点 (红点) ---
plt.plot(x_values[global_max_idx], y_values[global_max_idx], '^', color='red', markersize=8,
label='Global Max')
# --- 标记 C: 最佳盈利区间 (绿色阴影区域) ---
if start_idx != -1 and end_idx != -1 and start_idx < end_idx:
# 在图上画出绿色区间背景
plt.axvspan(best_start_val, best_end_val, color='green', alpha=0.15, label='Best Profit Interval')
# 标注区间信息
mid_x = (best_start_val + best_end_val) / 2
mid_y = (y_values[start_idx] + y_values[end_idx]) / 2
plt.annotate(
f"Best Interval: [{best_start_val:.2f}, {best_end_val:.2f}]\n"
f"Section Profit: {best_profit:.2f}",
xy=(best_end_val, y_values[end_idx]),
xytext=(20, -40),
textcoords="offset points",
bbox=dict(boxstyle="round,pad=0.3", fc="white", ec="green", alpha=0.9),
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2", color='green')
)
# 标记起涨点和止盈点
plt.plot(best_start_val, y_values[start_idx], 'go', markersize=6)
plt.plot(best_end_val, y_values[end_idx], 'go', markersize=6)
plt.axhline(0, color='black', linewidth=1, linestyle='--', alpha=0.3)
plt.title(f"Indicator: {indicator_name} - Interval Analysis")
plt.xlabel("Indicator Value")
plt.ylabel("Cumulative PnL")
plt.legend(loc='best')
plt.grid(True, alpha=0.3)
plt.show()
print("分析完成。")