From f474b31f57a0ad78bcd978a3a4a0ca7920fd7346 Mon Sep 17 00:00:00 2001 From: bingyi Date: Sun, 2 Nov 2025 19:32:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=93=E5=8D=B0=E4=BA=A4=E6=98=93=E6=98=8E?= =?UTF-8?q?=E7=BB=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gogogo/data_fetcher.py | 2 +- gogogo/result_visualizer.py | 210 +++++++++++++++++++++++++++++++++++- 2 files changed, 210 insertions(+), 2 deletions(-) diff --git a/gogogo/data_fetcher.py b/gogogo/data_fetcher.py index 7d9e75d..2e8419e 100644 --- a/gogogo/data_fetcher.py +++ b/gogogo/data_fetcher.py @@ -54,7 +54,7 @@ def get_processed_data(): # 限制为最近一年数据 end_date = smic_data.index.max() - start_date = end_date - pd.Timedelta(days=360*3) + start_date = end_date - pd.Timedelta(days=360) print(f"\n限制回测时间范围: {start_date} 到 {end_date}") diff --git a/gogogo/result_visualizer.py b/gogogo/result_visualizer.py index d937ebc..133e8a0 100644 --- a/gogogo/result_visualizer.py +++ b/gogogo/result_visualizer.py @@ -175,6 +175,209 @@ def print_statistics(strategy_data, portfolio): print(f"做空信号次数: {short_count}") print(f"总信号次数: {total_signals}") +def print_trade_orders(strategy_data, stock_portfolio): + """打印所有交易订单信息""" + if stock_portfolio is None: + return + + print("\n" + "="*60) + print("=== 详细交易订单信息 ===") + print("="*60) + + try: + # 获取订单记录 + orders_df = stock_portfolio.orders.records_readable + + if len(orders_df) > 0: + print(f"\n总订单数量: {len(orders_df)}") + + # 按时间排序 + time_column = None + possible_time_columns = ['Timestamp', 'Date', 'Time'] + for col in possible_time_columns: + if col in orders_df.columns: + time_column = col + break + + if time_column: + orders_sorted = orders_df.sort_values(time_column) + else: + orders_sorted = orders_df + + # 显示所有订单(按时间排序) + pd.set_option('display.max_rows', None) + pd.set_option('display.width', None) + + print("\n【所有交易订单(按时间排序)】") + print(orders_sorted.to_string(index=False)) + + # 按股票分组统计 + print(f"\n【按股票统计】") + for column in ['SMIC', 'HHIC']: + column_orders = orders_sorted[orders_sorted['Column'] == column] + if len(column_orders) > 0: + buy_orders = column_orders[column_orders['Side'] == 'Buy'] + sell_orders = column_orders[column_orders['Side'] == 'Sell'] + + print(f"\n{column} 订单统计:") + print(f" 总订单数: {len(column_orders)}") + print(f" 买入订单: {len(buy_orders)}") + print(f" 卖出订单: {len(sell_orders)}") + print(f" 总交易金额: {column_orders['Size'].abs().sum():.2f} 股") + print(f" 总手续费: {column_orders['Fees'].sum():.2f}") + + # 按日期分组统计 + if time_column: + print(f"\n【按日期统计】") + daily_orders = orders_sorted.groupby(time_column).agg({ + 'Column': 'count', + 'Size': 'sum', + 'Fees': 'sum' + }).rename(columns={'Column': '订单数'}) + + print(daily_orders.to_string()) + else: + print(f"\n【按日期统计】- 未找到时间列") + + # 重置pandas显示选项 + pd.reset_option('display.max_rows') + pd.reset_option('display.width') + + else: + print("没有找到交易订单") + + except Exception as e: + print(f"打印订单信息时出错: {e}") + import traceback + traceback.print_exc() + +def print_detailed_trade_analysis(strategy_data, stock_portfolio): + """打印详细的交易分析""" + if stock_portfolio is None: + return + + print("\n" + "="*60) + print("=== 详细交易分析 ===") + print("="*60) + + try: + # 获取交易记录 + trades_df = stock_portfolio.trades.records_readable + + if len(trades_df) > 0: + print(f"\n总交易次数: {len(trades_df)}") + + # 按开仓时间排序 + trades_sorted = trades_df.sort_values('Entry Timestamp') + + # 显示所有交易(按时间排序) + pd.set_option('display.max_rows', None) + pd.set_option('display.width', None) + + print("\n【所有交易记录(按开仓时间排序)】") + print(trades_sorted.to_string(index=False)) + + # 按股票分组分析 + print(f"\n【按股票交易分析】") + for column in ['SMIC', 'HHIC']: + column_trades = trades_sorted[trades_sorted['Column'] == column] + if len(column_trades) > 0: + winning_trades = column_trades[column_trades['PnL'] > 0] + losing_trades = column_trades[column_trades['PnL'] < 0] + + print(f"\n{column} 交易分析:") + print(f" 总交易次数: {len(column_trades)}") + print(f" 盈利交易: {len(winning_trades)}") + print(f" 亏损交易: {len(losing_trades)}") + print(f" 胜率: {len(winning_trades)/len(column_trades)*100:.1f}%") + print(f" 总盈亏: {column_trades['PnL'].sum():.2f}") + print(f" 平均每笔盈亏: {column_trades['PnL'].mean():.2f}") + print(f" 最大盈利: {column_trades['PnL'].max():.2f}") + print(f" 最大亏损: {column_trades['PnL'].min():.2f}") + if len(winning_trades) > 0: + print(f" 平均盈利: {winning_trades['PnL'].mean():.2f}") + if len(losing_trades) > 0: + print(f" 平均亏损: {losing_trades['PnL'].mean():.2f}") + + # 正确的配对交易分析 + print(f"\n【配对交易分析(修正)】") + + # 按开仓时间分组,找出同一天开仓的SMIC和HHIC交易 + entry_groups = trades_sorted.groupby('Entry Timestamp').agg({ + 'Column': list, + 'PnL': list, + 'Size': list, + 'Direction': list, + 'Exit Timestamp': list + }) + + pair_trades = [] + for entry_date, group_data in entry_groups.iterrows(): + columns = group_data['Column'] + pnls = group_data['PnL'] + directions = group_data['Direction'] + exit_times = group_data['Exit Timestamp'] + + # 检查是否同时有SMIC和HHIC的交易 + if 'SMIC' in columns and 'HHIC' in columns: + smic_idx = columns.index('SMIC') + hhic_idx = columns.index('HHIC') + + # 检查交易方向是否配对(一个做多,一个做空) + if directions[smic_idx] != directions[hhic_idx]: + pair_pnl = pnls[smic_idx] + pnls[hhic_idx] + pair_trades.append({ + 'entry_date': entry_date, + 'exit_date_smic': exit_times[smic_idx], + 'exit_date_hhic': exit_times[hhic_idx], + 'smic_pnl': pnls[smic_idx], + 'hhic_pnl': pnls[hhic_idx], + 'total_pnl': pair_pnl, + 'smic_direction': directions[smic_idx], + 'hhic_direction': directions[hhic_idx] + }) + + if pair_trades: + # 按开仓时间排序配对交易 + pair_trades_sorted = sorted(pair_trades, key=lambda x: x['entry_date']) + + print(f"配对交易次数: {len(pair_trades_sorted)}") + + total_pair_pnl = sum(trade['total_pnl'] for trade in pair_trades_sorted) + avg_pair_pnl = total_pair_pnl / len(pair_trades_sorted) + + winning_pairs = [t for t in pair_trades_sorted if t['total_pnl'] > 0] + losing_pairs = [t for t in pair_trades_sorted if t['total_pnl'] < 0] + + print(f"配对交易总盈亏: {total_pair_pnl:.2f}") + print(f"平均每对盈亏: {avg_pair_pnl:.2f}") + print(f"盈利配对: {len(winning_pairs)}") + print(f"亏损配对: {len(losing_pairs)}") + print(f"配对胜率: {len(winning_pairs)/len(pair_trades_sorted)*100:.1f}%") + + # 显示每对交易的详细信息(按时间排序) + print(f"\n【各配对交易详情(按开仓时间排序)】") + for i, trade in enumerate(pair_trades_sorted, 1): + status = "盈利" if trade['total_pnl'] > 0 else "亏损" + print(f"第{i}对 - 开仓: {trade['entry_date']}") + print(f" SMIC({trade['smic_direction']}): {trade['smic_pnl']:.2f} (平仓: {trade['exit_date_smic']})") + print(f" HHIC({trade['hhic_direction']}): {trade['hhic_pnl']:.2f} (平仓: {trade['exit_date_hhic']})") + print(f" 总盈亏: {trade['total_pnl']:.2f} [{status}]") + print() + else: + print("没有找到配对交易") + + # 重置pandas显示选项 + pd.reset_option('display.max_rows') + pd.reset_option('display.width') + + else: + print("没有找到交易记录") + + except Exception as e: + print(f"打印交易分析时出错: {e}") + import traceback + traceback.print_exc() def main(): """主函数""" @@ -197,8 +400,13 @@ def main(): # 绘制基于比率的结果 plot_results(strategy_data, ratio_portfolio) + # 打印详细交易订单信息 + print_trade_orders(strategy_data, stock_portfolio) + + # 打印详细交易分析 + print_detailed_trade_analysis(strategy_data, stock_portfolio) + print("程序执行完成!") if __name__ == "__main__": main() - \ No newline at end of file