| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- import pandas as pd
- import numpy as np
- from datetime import datetime
- import warnings
- import sys
- warnings.filterwarnings('ignore')
- # Redirect stdout to suppress verbose output
- from io import StringIO
- old_stdout = sys.stdout
- sys.stdout = StringIO()
- from cyb50_30min_dual_direction import ConfigManager, IntradayDataFetcher, DualDirectionSignalGenerator, DualDirectionExecutor
- from t1_converter import simulate_t1_trades
- def load_local_data(csv_file='cyb50_30min_2023_to_20260325.csv'):
- df = pd.read_csv(csv_file)
- df['DateTime'] = pd.to_datetime(df['DateTime'])
- df.set_index('DateTime', inplace=True)
- df.sort_index(inplace=True)
- if 'Open' not in df.columns and 'o' in df.columns:
- df.rename(columns={'o':'Open','h':'High','l':'Low','c':'Close','v':'Volume','a':'Amount'}, inplace=True)
- for col in ['Open', 'High', 'Low', 'Close', 'Volume']:
- if col in df.columns:
- df[col] = pd.to_numeric(df[col], errors='coerce')
- df['Returns'] = df['Close'].pct_change()
- df['High_Low_Pct'] = (df['High'] - df['Low']) / df['Close'].shift(1)
- df['Close_Open_Pct'] = (df['Close'] - df['Open']) / df['Open']
- df.ffill(inplace=True)
- df.dropna(inplace=True)
- return df
- initial_capital = 1000000
- raw_data = load_local_data('cyb50_30min_2023_to_20260325.csv')
- config_manager = ConfigManager('config.json')
- fetcher = IntradayDataFetcher(config_manager)
- data_with_indicators = fetcher.calculate_intraday_indicators(raw_data)
- signal_generator = DualDirectionSignalGenerator()
- signals_df = signal_generator.generate_dual_direction_signals(data_with_indicators)
- executor = DualDirectionExecutor(initial_capital=initial_capital)
- results_df, trades_df = executor.execute_dual_direction_trades(signals_df)
- long_trades = trades_df[trades_df['交易方向'] == '做多'].copy()
- t1_trades = simulate_t1_trades(data_with_indicators, long_trades, initial_capital)
- t1_adjusted = t1_trades[t1_trades['T+1调整'] == '是(T0→T1)']
- final_capital = t1_trades['平仓时资金'].iloc[-1] if len(t1_trades) > 0 else initial_capital
- total_return = (final_capital - initial_capital) / initial_capital * 100
- total_trades = len(t1_trades)
- winning_trades = t1_trades[t1_trades['盈亏金额'] > 0]
- losing_trades = t1_trades[t1_trades['盈亏金额'] < 0]
- win_rate = len(winning_trades) / total_trades * 100 if total_trades > 0 else 0
- total_profit = winning_trades['盈亏金额'].sum() if len(winning_trades) > 0 else 0
- total_loss = abs(losing_trades['盈亏金额'].sum()) if len(losing_trades) > 0 else 0
- profit_factor = total_profit / total_loss if total_loss > 0 else 0
- normal_trades = t1_trades[t1_trades['T+1调整'] != '是(T0→T1)']
- normal_pnl = normal_trades['盈亏金额'].sum() if len(normal_trades) > 0 else 0
- t1_pnl = t1_adjusted['盈亏金额'].sum() if len(t1_adjusted) > 0 else 0
- # Restore stdout
- sys.stdout = old_stdout
- print('='*60)
- print('创业板50 T+1 回测结果汇总')
- print('='*60)
- print(f'数据加载: {len(raw_data)}条K线')
- print(f'数据区间: {raw_data.index[0]} ~ {raw_data.index[-1]}')
- print(f'多空策略: 共{len(trades_df)}笔交易, 做多{len(long_trades)}笔')
- print(f'T+1转换: {len(t1_trades)}笔交易')
- print(f'T+1调整: {len(t1_adjusted)}笔')
- print()
- print('='*60)
- print('总体绩效')
- print('='*60)
- print(f'初始资金: {initial_capital:,.0f}元')
- print(f'最终资金: {final_capital:,.0f}元')
- print(f'总收益率: {total_return:+.2f}%')
- print(f'总交易次数: {total_trades}笔')
- print(f'盈利次数: {len(winning_trades)}笔')
- print(f'亏损次数: {len(losing_trades)}笔')
- print(f'胜率: {win_rate:.1f}%')
- print(f'盈亏比: {profit_factor:.2f}')
- print()
- print('='*60)
- print('T+1调整统计')
- print('='*60)
- print(f'正常交易: {len(normal_trades)}笔, 盈亏{normal_pnl:+,.0f}元')
- print(f'T+1调整(T0→T1): {len(t1_adjusted)}笔, 盈亏{t1_pnl:+,.0f}元')
- print()
- print('='*60)
- print('最近5笔交易')
- print('='*60)
- print(t1_trades.tail(5)[['开仓时间', '平仓时间', '盈亏金额', 'T+1调整']].to_string(index=False))
|