SpectralStrategy更新
This commit is contained in:
@@ -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("分析完成。")
|
||||
|
||||
Reference in New Issue
Block a user