iT邦幫忙

2025 iThome 鐵人賽

DAY 25
0

小明的模擬農場實驗

今天要學習 Paper Trade(模擬交易),這就像爸爸在決定種新作物前,先在小塊試驗田做實驗一樣。在真正投入資金之前,我們需要在安全的環境中驗證策略,累積經驗,就像先用假錢練習,等熟練了再用真錢!

什麼是 Paper Trade?

Paper Trade 是使用虛擬資金進行的模擬交易:

  • 無真實資金風險:使用模擬資金,不會有實際損失
  • 真實市場數據:使用實時或歷史市場數據
  • 完整交易流程:模擬真實的下單、成交、平倉過程
  • 學習和驗證工具:測試策略和累積經驗

Paper Trade 的重要性

Paper Trade 的重要性

Paper Trade 系統設計

核心架構

import pandas as pd
import numpy as np
from datetime import datetime, timedelta
from typing import Dict, List, Optional
import uuid

class PaperTrade:
    """模擬交易系統"""
    
    def __init__(self, initial_balance=100000):
        self.initial_balance = initial_balance
        self.balance = initial_balance
        self.positions = {}  # {symbol: {'side', 'size', 'entry_price', 'entry_time'}}
        self.orders = {}     # {order_id: order_info}
        self.trade_history = []
        self.pnl_history = []
        self.market_data = {}
        
    def add_market_data(self, symbol, data):
        """添加市場數據"""
        self.market_data[symbol] = data
    
    def get_current_price(self, symbol, timestamp=None):
        """獲取當前價格"""
        if symbol not in self.market_data:
            raise ValueError(f"No market data for {symbol}")
        
        if timestamp is None:
            return self.market_data[symbol]['close'].iloc[-1]
        
        # 根據時間戳獲取歷史價格
        data = self.market_data[symbol]
        try:
            return data.loc[data.index <= timestamp, 'close'].iloc[-1]
        except IndexError:
            return data['close'].iloc[0]
    
    def place_order(self, symbol, side, size, order_type='market', price=None):
        """下單"""
        
        order_id = str(uuid.uuid4())
        timestamp = datetime.now()
        
        order = {
            'order_id': order_id,
            'symbol': symbol,
            'side': side,  # 'buy' or 'sell'
            'size': size,
            'order_type': order_type,
            'price': price,
            'timestamp': timestamp,
            'status': 'pending'
        }
        
        self.orders[order_id] = order
        
        # 立即執行市價單
        if order_type == 'market':
            return self.execute_order(order_id)
        
        return order_id
    
    def execute_order(self, order_id):
        """執行訂單"""
        
        if order_id not in self.orders:
            return False, "Order not found"
        
        order = self.orders[order_id]
        symbol = order['symbol']
        side = order['side']
        size = order['size']
        
        try:
            # 獲取執行價格
            if order['order_type'] == 'market':
                execution_price = self.get_current_price(symbol)
            else:
                execution_price = order['price']
            
            # 檢查餘額是否足夠
            if side == 'buy':
                required_balance = size * execution_price
                if required_balance > self.balance:
                    order['status'] = 'rejected'
                    return False, "Insufficient balance"
            
            # 更新持倉
            self._update_position(symbol, side, size, execution_price, order['timestamp'])
            
            # 更新餘額
            if side == 'buy':
                self.balance -= size * execution_price
            else:
                self.balance += size * execution_price
            
            # 記錄交易
            trade = {
                'trade_id': str(uuid.uuid4()),
                'order_id': order_id,
                'symbol': symbol,
                'side': side,
                'size': size,
                'price': execution_price,
                'timestamp': order['timestamp'],
                'pnl': self._calculate_trade_pnl(symbol, side, size, execution_price)
            }
            
            self.trade_history.append(trade)
            order['status'] = 'filled'
            order['execution_price'] = execution_price
            
            return True, f"Order executed: {side} {size} {symbol} at {execution_price}"
            
        except Exception as e:
            order['status'] = 'error'
            return False, f"Execution error: {str(e)}"
    
    def _update_position(self, symbol, side, size, price, timestamp):
        """更新持倉"""
        
        if symbol not in self.positions:
            self.positions[symbol] = {
                'side': side,
                'size': size,
                'entry_price': price,
                'entry_time': timestamp
            }
        else:
            position = self.positions[symbol]
            
            if position['side'] == side:
                # 加倉
                total_size = position['size'] + size
                weighted_price = (position['size'] * position['entry_price'] + 
                                size * price) / total_size
                
                position['size'] = total_size
                position['entry_price'] = weighted_price
            else:
                # 反向交易
                if size >= position['size']:
                    # 完全平倉或反向
                    remaining_size = size - position['size']
                    if remaining_size > 0:
                        position['side'] = side
                        position['size'] = remaining_size
                        position['entry_price'] = price
                        position['entry_time'] = timestamp
                    else:
                        del self.positions[symbol]
                else:
                    # 部分平倉
                    position['size'] -= size
    
    def _calculate_trade_pnl(self, symbol, side, size, price):
        """計算交易損益"""
        
        if symbol not in self.positions:
            return 0
        
        position = self.positions[symbol]
        
        if position['side'] != side:
            # 平倉交易
            if side == 'sell':
                return size * (price - position['entry_price'])
            else:
                return size * (position['entry_price'] - price)
        
        return 0  # 開倉交易無立即損益
    
    def calculate_portfolio_value(self):
        """計算投資組合總值"""
        
        total_value = self.balance
        
        for symbol, position in self.positions.items():
            try:
                current_price = self.get_current_price(symbol)
                position_value = position['size'] * current_price
                
                if position['side'] == 'buy':
                    total_value += position_value
                else:
                    # 做空倉位的價值計算
                    entry_value = position['size'] * position['entry_price']
                    pnl = position['size'] * (position['entry_price'] - current_price)
                    total_value += entry_value + pnl
                    
            except Exception:
                continue
        
        return total_value
    
    def get_unrealized_pnl(self):
        """計算未實現損益"""
        
        unrealized_pnl = {}
        total_unrealized = 0
        
        for symbol, position in self.positions.items():
            try:
                current_price = self.get_current_price(symbol)
                
                if position['side'] == 'buy':
                    pnl = position['size'] * (current_price - position['entry_price'])
                else:
                    pnl = position['size'] * (position['entry_price'] - current_price)
                
                unrealized_pnl[symbol] = {
                    'position': position,
                    'current_price': current_price,
                    'unrealized_pnl': pnl,
                    'pnl_percentage': pnl / (position['size'] * position['entry_price'])
                }
                
                total_unrealized += pnl
                
            except Exception:
                continue
        
        return unrealized_pnl, total_unrealized
    
    def get_performance_metrics(self):
        """計算績效指標"""
        
        if not self.trade_history:
            return {}
        
        trades = pd.DataFrame(self.trade_history)
        
        # 計算總收益
        total_pnl = trades['pnl'].sum()
        
        # 計算勝率
        winning_trades = trades[trades['pnl'] > 0]
        losing_trades = trades[trades['pnl'] < 0]
        
        win_rate = len(winning_trades) / len(trades) if len(trades) > 0 else 0
        
        # 計算平均盈虧
        avg_win = winning_trades['pnl'].mean() if len(winning_trades) > 0 else 0
        avg_loss = losing_trades['pnl'].mean() if len(losing_trades) > 0 else 0
        
        # 計算收益率
        portfolio_value = self.calculate_portfolio_value()
        total_return = (portfolio_value - self.initial_balance) / self.initial_balance
        
        return {
            'total_trades': len(trades),
            'winning_trades': len(winning_trades),
            'losing_trades': len(losing_trades),
            'win_rate': win_rate,
            'total_pnl': total_pnl,
            'avg_win': avg_win,
            'avg_loss': avg_loss,
            'profit_factor': abs(avg_win / avg_loss) if avg_loss != 0 else float('inf'),
            'initial_balance': self.initial_balance,
            'current_balance': self.balance,
            'portfolio_value': portfolio_value,
            'total_return': total_return
        }

策略模擬測試

簡單移動平均策略

class MAStrategy:
    """移動平均策略 - Paper Trade 版本"""
    
    def __init__(self, paper_trader, fast_period=20, slow_period=50):
        self.paper_trader = paper_trader
        self.fast_period = fast_period
        self.slow_period = slow_period
        self.last_signal = None
    
    def calculate_signals(self, symbol, data):
        """計算交易信號"""
        
        # 計算移動平均
        data['fast_ma'] = data['close'].rolling(self.fast_period).mean()
        data['slow_ma'] = data['close'].rolling(self.slow_period).mean()
        
        # 生成信號
        data['signal'] = 0
        data.loc[data['fast_ma'] > data['slow_ma'], 'signal'] = 1  # 買入信號
        data.loc[data['fast_ma'] < data['slow_ma'], 'signal'] = -1  # 賣出信號
        
        return data
    
    def execute_strategy(self, symbol, current_data):
        """執行策略"""
        
        # 計算當前信號
        signals = self.calculate_signals(symbol, current_data)
        current_signal = signals['signal'].iloc[-1]
        
        # 如果信號改變,執行交易
        if current_signal != self.last_signal:
            
            # 檢查當前持倉
            current_position = self.paper_trader.positions.get(symbol)
            
            if current_signal == 1:  # 買入信號
                if current_position is None or current_position['side'] == 'sell':
                    # 平掉空倉(如果有的話)
                    if current_position and current_position['side'] == 'sell':
                        self.paper_trader.place_order(
                            symbol, 'buy', current_position['size']
                        )
                    
                    # 開多倉
                    trade_size = self.calculate_position_size(symbol)
                    self.paper_trader.place_order(symbol, 'buy', trade_size)
                    
            elif current_signal == -1:  # 賣出信號
                if current_position is None or current_position['side'] == 'buy':
                    # 平掉多倉(如果有的話)
                    if current_position and current_position['side'] == 'buy':
                        self.paper_trader.place_order(
                            symbol, 'sell', current_position['size']
                        )
                    
                    # 開空倉
                    trade_size = self.calculate_position_size(symbol)
                    self.paper_trader.place_order(symbol, 'sell', trade_size)
            
            self.last_signal = current_signal
    
    def calculate_position_size(self, symbol):
        """計算倉位大小"""
        
        # 簡單的固定比例倉位管理
        current_price = self.paper_trader.get_current_price(symbol)
        max_position_value = self.paper_trader.balance * 0.2  # 最大20%倉位
        
        return max_position_value / current_price

# 策略回測示例
def run_strategy_simulation():
    """運行策略模擬"""
    
    # 創建模擬交易器
    paper_trader = PaperTrade(initial_balance=100000)
    
    # 生成模擬數據
    dates = pd.date_range('2024-01-01', '2024-12-31', freq='1D')
    np.random.seed(42)
    
    # 模擬 BTC 價格數據
    returns = np.random.normal(0.001, 0.03, len(dates))  # 平均日收益0.1%,波動3%
    prices = [50000]  # 起始價格
    
    for r in returns[1:]:
        prices.append(prices[-1] * (1 + r))
    
    btc_data = pd.DataFrame({
        'close': prices,
        'timestamp': dates
    }, index=dates)
    
    paper_trader.add_market_data('BTC', btc_data)
    
    # 創建策略
    strategy = MAStrategy(paper_trader)
    
    # 執行回測
    for i in range(50, len(btc_data)):  # 從第50天開始(確保有足夠數據計算移動平均)
        current_data = btc_data.iloc[:i+1]
        strategy.execute_strategy('BTC', current_data)
    
    # 獲取績效報告
    metrics = paper_trader.get_performance_metrics()
    
    print("模擬交易績效報告:")
    print(f"總交易次數: {metrics['total_trades']}")
    print(f"勝率: {metrics['win_rate']:.2%}")
    print(f"總損益: ${metrics['total_pnl']:.2f}")
    print(f"總收益率: {metrics['total_return']:.2%}")
    print(f"盈虧比: {metrics['profit_factor']:.2f}")
    
    return paper_trader, metrics

# 執行模擬
trader, results = run_strategy_simulation()

模擬交易 vs 實盤交易差異

關鍵差異分析

模擬交易 vs 實盤交易差異

現實因子模擬

class RealisticPaperTrade(PaperTrade):
    """更真實的模擬交易系統"""
    
    def __init__(self, initial_balance=100000, commission_rate=0.001, slippage_rate=0.0005):
        super().__init__(initial_balance)
        self.commission_rate = commission_rate
        self.slippage_rate = slippage_rate
        self.max_order_size_ratio = 0.1  # 最大單筆訂單佔市場成交量比例
    
    def simulate_slippage(self, symbol, side, size, theoretical_price):
        """模擬滑價"""
        
        # 根據訂單大小和方向計算滑價
        market_impact = size * 0.00001  # 簡化的市場衝擊模型
        
        if side == 'buy':
            slippage = theoretical_price * (self.slippage_rate + market_impact)
            execution_price = theoretical_price + slippage
        else:
            slippage = theoretical_price * (self.slippage_rate + market_impact)
            execution_price = theoretical_price - slippage
        
        return execution_price, slippage
    
    def calculate_commission(self, symbol, size, price):
        """計算手續費"""
        notional_value = size * price
        commission = notional_value * self.commission_rate
        return commission
    
    def execute_order(self, order_id):
        """重寫訂單執行,加入現實因子"""
        
        if order_id not in self.orders:
            return False, "Order not found"
        
        order = self.orders[order_id]
        symbol = order['symbol']
        side = order['side']
        size = order['size']
        
        try:
            # 獲取理論執行價格
            theoretical_price = self.get_current_price(symbol)
            
            # 模擬滑價
            execution_price, slippage = self.simulate_slippage(
                symbol, side, size, theoretical_price
            )
            
            # 計算手續費
            commission = self.calculate_commission(symbol, size, execution_price)
            
            # 檢查餘額(包含手續費)
            if side == 'buy':
                required_balance = size * execution_price + commission
                if required_balance > self.balance:
                    order['status'] = 'rejected'
                    return False, "Insufficient balance including commission"
            
            # 執行交易
            self._update_position(symbol, side, size, execution_price, order['timestamp'])
            
            # 更新餘額(扣除手續費)
            if side == 'buy':
                self.balance -= (size * execution_price + commission)
            else:
                self.balance += (size * execution_price - commission)
            
            # 記錄詳細交易資訊
            trade = {
                'trade_id': str(uuid.uuid4()),
                'order_id': order_id,
                'symbol': symbol,
                'side': side,
                'size': size,
                'theoretical_price': theoretical_price,
                'execution_price': execution_price,
                'slippage': slippage,
                'commission': commission,
                'timestamp': order['timestamp'],
                'pnl': self._calculate_trade_pnl(symbol, side, size, execution_price)
            }
            
            self.trade_history.append(trade)
            order['status'] = 'filled'
            order['execution_price'] = execution_price
            order['slippage'] = slippage
            order['commission'] = commission
            
            return True, f"Order executed with slippage: {side} {size} {symbol} at {execution_price:.2f} (slippage: {slippage:.2f})"
            
        except Exception as e:
            order['status'] = 'error'
            return False, f"Execution error: {str(e)}"
    
    def get_detailed_performance_metrics(self):
        """獲取包含現實因子的詳細績效指標"""
        
        base_metrics = self.get_performance_metrics()
        
        if not self.trade_history:
            return base_metrics
        
        trades = pd.DataFrame(self.trade_history)
        
        # 計算成本分析
        total_commission = trades['commission'].sum()
        total_slippage = trades['slippage'].sum()
        total_trading_costs = total_commission + total_slippage
        
        # 計算成本對績效的影響
        gross_pnl = trades['pnl'].sum() + total_trading_costs
        net_pnl = trades['pnl'].sum()
        
        cost_impact = total_trading_costs / abs(gross_pnl) if gross_pnl != 0 else 0
        
        base_metrics.update({
            'total_commission': total_commission,
            'total_slippage': total_slippage,
            'total_trading_costs': total_trading_costs,
            'gross_pnl': gross_pnl,
            'net_pnl': net_pnl,
            'cost_impact_percentage': cost_impact,
            'average_commission_per_trade': total_commission / len(trades),
            'average_slippage_per_trade': total_slippage / len(trades)
        })
        
        return base_metrics

# 現實模擬示例
realistic_trader = RealisticPaperTrade(
    initial_balance=100000,
    commission_rate=0.001,  # 0.1% 手續費
    slippage_rate=0.0005    # 0.05% 滑價
)

Paper Trade 最佳實踐

1. 真實環境模擬

class ComprehensivePaperTrade:
    """綜合模擬交易環境"""
    
    def __init__(self):
        self.trading_hours = {
            'crypto': {'start': 0, 'end': 24},    # 24/7
            'forex': {'start': 0, 'end': 24},     # 週日晚到週五晚
            'stocks': {'start': 9.5, 'end': 16}   # 9:30-16:00
        }
        
        self.market_conditions = {
            'normal': {'volatility_multiplier': 1.0, 'liquidity_multiplier': 1.0},
            'high_volatility': {'volatility_multiplier': 2.0, 'liquidity_multiplier': 0.7},
            'low_liquidity': {'volatility_multiplier': 1.2, 'liquidity_multiplier': 0.3},
            'market_crash': {'volatility_multiplier': 5.0, 'liquidity_multiplier': 0.1}
        }
    
    def simulate_market_conditions(self, base_data, condition='normal'):
        """模擬不同市場條件"""
        
        condition_params = self.market_conditions[condition]
        volatility_mult = condition_params['volatility_multiplier']
        liquidity_mult = condition_params['liquidity_multiplier']
        
        # 調整價格波動
        returns = base_data['close'].pct_change()
        adjusted_returns = returns * volatility_mult
        
        # 重新計算價格
        adjusted_prices = [base_data['close'].iloc[0]]
        for ret in adjusted_returns[1:]:
            if not np.isnan(ret):
                adjusted_prices.append(adjusted_prices[-1] * (1 + ret))
            else:
                adjusted_prices.append(adjusted_prices[-1])
        
        # 調整流動性(影響滑價)
        adjusted_data = base_data.copy()
        adjusted_data['close'] = adjusted_prices
        adjusted_data['liquidity_multiplier'] = liquidity_mult
        
        return adjusted_data
    
    def validate_strategy_robustness(self, strategy, test_scenarios):
        """測試策略在不同場景下的穩健性"""
        
        results = {}
        
        for scenario_name, scenario_data in test_scenarios.items():
            # 為每個場景創建新的模擬交易器
            trader = RealisticPaperTrade()
            
            # 運行策略
            strategy_instance = MAStrategy(trader)
            
            for i in range(50, len(scenario_data)):
                current_data = scenario_data.iloc[:i+1]
                strategy_instance.execute_strategy('BTC', current_data)
            
            # 記錄結果
            metrics = trader.get_detailed_performance_metrics()
            results[scenario_name] = metrics
        
        return results

# 多場景測試
def comprehensive_strategy_test():
    """綜合策略測試"""
    
    simulator = ComprehensivePaperTrade()
    
    # 創建基礎數據
    dates = pd.date_range('2024-01-01', '2024-12-31', freq='1D')
    np.random.seed(42)
    
    base_returns = np.random.normal(0.001, 0.02, len(dates))
    base_prices = [50000]
    for r in base_returns[1:]:
        base_prices.append(base_prices[-1] * (1 + r))
    
    base_data = pd.DataFrame({
        'close': base_prices,
        'timestamp': dates
    }, index=dates)
    
    # 創建不同市場場景
    test_scenarios = {
        'normal_market': simulator.simulate_market_conditions(base_data, 'normal'),
        'high_volatility': simulator.simulate_market_conditions(base_data, 'high_volatility'),
        'low_liquidity': simulator.simulate_market_conditions(base_data, 'low_liquidity'),
        'market_crash': simulator.simulate_market_conditions(base_data, 'market_crash')
    }
    
    # 測試策略穩健性
    results = simulator.validate_strategy_robustness(MAStrategy, test_scenarios)
    
    # 分析結果
    print("策略穩健性測試結果:")
    print("-" * 50)
    
    for scenario, metrics in results.items():
        print(f"\n{scenario.upper()}:")
        print(f"總收益率: {metrics.get('total_return', 0):.2%}")
        print(f"勝率: {metrics.get('win_rate', 0):.2%}")
        print(f"最大回撤: {metrics.get('max_drawdown', 0):.2%}")
        print(f"交易成本影響: {metrics.get('cost_impact_percentage', 0):.2%}")
    
    return results

# 執行測試
test_results = comprehensive_strategy_test()

2. 心理因素模擬

class PsychologicalPaperTrade(RealisticPaperTrade):
    """包含心理因素的模擬交易"""
    
    def __init__(self, initial_balance=100000):
        super().__init__(initial_balance)
        self.emotional_state = 'neutral'  # 'confident', 'fearful', 'greedy', 'neutral'
        self.consecutive_losses = 0
        self.consecutive_wins = 0
        self.stress_level = 0  # 0-10
    
    def update_emotional_state(self):
        """更新情緒狀態"""
        
        if self.consecutive_losses >= 3:
            self.emotional_state = 'fearful'
            self.stress_level = min(10, self.stress_level + 2)
        elif self.consecutive_wins >= 3:
            self.emotional_state = 'greedy'
            self.stress_level = max(0, self.stress_level - 1)
        else:
            self.emotional_state = 'neutral'
            self.stress_level = max(0, self.stress_level - 0.5)
    
    def emotional_adjustment(self, original_size):
        """根據情緒狀態調整倉位大小"""
        
        if self.emotional_state == 'fearful':
            return original_size * 0.5  # 恐懼時減少倉位
        elif self.emotional_state == 'greedy':
            return original_size * 1.5  # 貪婪時增加倉位
        else:
            return original_size
    
    def execute_order_with_emotion(self, order_id):
        """執行帶有情緒影響的訂單"""
        
        if order_id not in self.orders:
            return False, "Order not found"
        
        order = self.orders[order_id]
        
        # 情緒影響訂單執行
        if self.emotional_state == 'fearful' and np.random.random() < 0.3:
            order['status'] = 'cancelled'
            return False, "Order cancelled due to fear"
        
        # 調整訂單大小
        original_size = order['size']
        adjusted_size = self.emotional_adjustment(original_size)
        order['size'] = adjusted_size
        
        # 執行訂單
        result = super().execute_order(order_id)
        
        # 更新連續盈虧記錄
        if result[0]:  # 如果成功執行
            trade = self.trade_history[-1]
            if trade['pnl'] > 0:
                self.consecutive_wins += 1
                self.consecutive_losses = 0
            elif trade['pnl'] < 0:
                self.consecutive_losses += 1
                self.consecutive_wins = 0
            
            self.update_emotional_state()
        
        return result

Paper Trade 結果分析

績效評估框架

class PerformanceAnalyzer:
    """績效分析器"""
    
    def __init__(self, paper_trader):
        self.trader = paper_trader
    
    def generate_comprehensive_report(self):
        """生成綜合報告"""
        
        metrics = self.trader.get_detailed_performance_metrics()
        
        report = {
            'basic_metrics': self._analyze_basic_metrics(metrics),
            'risk_metrics': self._analyze_risk_metrics(),
            'trade_analysis': self._analyze_trades(),
            'recommendations': self._generate_recommendations(metrics)
        }
        
        return report
    
    def _analyze_basic_metrics(self, metrics):
        """分析基本指標"""
        
        return {
            'total_return': metrics.get('total_return', 0),
            'win_rate': metrics.get('win_rate', 0),
            'profit_factor': metrics.get('profit_factor', 0),
            'average_trade': metrics.get('total_pnl', 0) / max(metrics.get('total_trades', 1), 1),
            'trading_frequency': metrics.get('total_trades', 0) / 365  # 假設一年數據
        }
    
    def _analyze_risk_metrics(self):
        """分析風險指標"""
        
        if not self.trader.trade_history:
            return {}
        
        trades = pd.DataFrame(self.trader.trade_history)
        daily_pnl = trades.groupby(trades['timestamp'].dt.date)['pnl'].sum()
        
        # 計算夏普比率
        if len(daily_pnl) > 1:
            sharpe_ratio = daily_pnl.mean() / daily_pnl.std() * np.sqrt(252)
        else:
            sharpe_ratio = 0
        
        # 計算最大回撤
        cumulative_pnl = daily_pnl.cumsum()
        peak = cumulative_pnl.expanding().max()
        drawdown = (cumulative_pnl - peak) / peak
        max_drawdown = drawdown.min()
        
        return {
            'sharpe_ratio': sharpe_ratio,
            'max_drawdown': max_drawdown,
            'volatility': daily_pnl.std() * np.sqrt(252),
            'downside_deviation': daily_pnl[daily_pnl < 0].std() * np.sqrt(252)
        }
    
    def _analyze_trades(self):
        """分析交易模式"""
        
        if not self.trader.trade_history:
            return {}
        
        trades = pd.DataFrame(self.trader.trade_history)
        
        # 分析交易時間分佈
        trades['hour'] = trades['timestamp'].dt.hour
        hourly_pnl = trades.groupby('hour')['pnl'].sum()
        
        # 分析持倉時間(需要配對買賣訂單)
        # 這裡簡化處理
        
        return {
            'best_trading_hours': hourly_pnl.idxmax(),
            'worst_trading_hours': hourly_pnl.idxmin(),
            'total_commission_paid': trades['commission'].sum() if 'commission' in trades else 0,
            'total_slippage_cost': trades['slippage'].sum() if 'slippage' in trades else 0
        }
    
    def _generate_recommendations(self, metrics):
        """生成改進建議"""
        
        recommendations = []
        
        if metrics.get('win_rate', 0) < 0.4:
            recommendations.append("勝率偏低,考慮優化進場信號")
        
        if metrics.get('profit_factor', 0) < 1.5:
            recommendations.append("盈虧比需要改善,考慮調整停損停利設定")
        
        if metrics.get('cost_impact_percentage', 0) > 0.3:
            recommendations.append("交易成本過高,考慮降低交易頻率")
        
        if not recommendations:
            recommendations.append("策略表現良好,可考慮小額實盤測試")
        
        return recommendations

# 生成完整報告
def generate_paper_trade_report(trader):
    """生成完整的模擬交易報告"""
    
    analyzer = PerformanceAnalyzer(trader)
    report = analyzer.generate_comprehensive_report()
    
    print("=" * 60)
    print("模擬交易完整報告")
    print("=" * 60)
    
    print("\n基本指標:")
    for key, value in report['basic_metrics'].items():
        print(f"{key}: {value:.4f}")
    
    print("\n風險指標:")
    for key, value in report['risk_metrics'].items():
        print(f"{key}: {value:.4f}")
    
    print("\n交易分析:")
    for key, value in report['trade_analysis'].items():
        print(f"{key}: {value}")
    
    print("\n改進建議:")
    for i, rec in enumerate(report['recommendations'], 1):
        print(f"{i}. {rec}")
    
    return report

小結

今天我們深入學習了 Paper Trade(模擬交易),就像在試驗田裡先測試新品種一樣。模擬交易的重要價值:

Paper Trade 的優勢:

  • 零風險學習環境
  • 策略驗證和優化
  • 心理訓練和經驗累積
  • 系統測試和除錯

關鍵注意事項:

  • 模擬與實盤存在差異
  • 需要加入現實因子模擬
  • 心理因素的重要性
  • 持續改進和優化

最佳實踐:

  • 使用真實的市場數據
  • 模擬完整的交易成本
  • 測試多種市場場景
  • 記錄詳細的交易日誌

從模擬到實盤的過渡:

  • 在模擬中證明策略有效性
  • 小額資金開始實盤
  • 持續監控和調整
  • 保持學習和改進的心態

Paper Trade 就像農夫的試驗田,是通往成功交易的必經之路。明天我們將開始實作部分,把理論和模擬轉化為真正的交易系統!


下一篇:Day 26 - 範例實作說明


上一篇
Day 24: Backtesting
下一篇
Day 26: 範例實作說明
系列文
小資族的量化交易 10128
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言