027-金融数据可视化

Claude CGL门禁机制解析:零容忍语义校验与工程适配实践 在大模型API安全对齐需求日益刚性的背景下,Contextual Gate Layer(CGL)代表了一种从‘后置检测’转向‘前置熔断’的新型防护范式。其核心原理是通过结构完整性、语义确定性与意图一致性三重硬规则校验,在请求执行前完成确定性拦截,而非依赖概率模型判断。这种设计显著提升了对齐可靠性,但也带来极高的模糊词敏感度和低通过率挑战。技术价值在于将AI交互从自然语言指令升级为可验证协议,适用于金融、医疗等高合规场景。典型应用包括提示工程重构、边缘代理重写、客户端预检网关等生产级适配方案。本文深入剖析CG 阅读详情

027-金融数据可视化

金融数据可视化是数据分析中的重要应用领域,需要专业的图表类型和分析方法。本章将详细介绍如何使用Matplotlib创建各种金融图表,包括股票价格图、技术指标、风险分析图表等。

导入必要的库

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
import matplotlib.dates as mdates
from matplotlib.patches import Rectangle
import matplotlib.patches as mpatches
from scipy import stats
import warnings
warnings.filterwarnings('ignore')

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False

# 设置图表样式
plt.style.use('default')

股票价格图表

K线图(蜡烛图)

class StockCharts:
    def __init__(self):
        # 生成模拟股票数据
        self.dates = pd.date_range('2023-01-01', periods=252, freq='D')
        self.stock_data = self.generate_stock_data()
    
    def generate_stock_data(self):
        """生成模拟股票数据"""
        np.random.seed(42)
        
        # 初始价格
        initial_price = 100
        
        # 生成随机价格变化
        returns = np.random.normal(0.001, 0.02, len(self.dates))
        
        # 计算价格序列
        prices = [initial_price]
        for ret in returns[1:]:
            prices.append(prices[-1] * (1 + ret))
        
        # 生成OHLC数据
        data = []
        for i, (date, close) in enumerate(zip(self.dates, prices)):
            # 生成开盘价、最高价、最低价
            open_price = close * (1 + np.random.normal(0, 0.005))
            high = max(open_price, close) * (1 + abs(np.random.normal(0, 0.01)))
            low = min(open_price, close) * (1 - abs(np.random.normal(0, 0.01)))
            
            # 生成成交量
            volume = np.random.randint(1000000, 5000000)
            
            data.append({
                'Date': date,
                'Open': open_price,
                'High': high,
                'Low': low,
                'Close': close,
                'Volume': volume
            })
        
        return pd.DataFrame(data)
    
    def plot_candlestick(self, start_idx=0, end_idx=50):
        """绘制K线图"""
        print("=== K线图 ===")
        
        fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(16, 12), 
                                      gridspec_kw={'height_ratios': [3, 1]})
        fig.suptitle('股票K线图与成交量', fontsize=16, fontweight='bold')
        
        # 选择数据范围
        data = self.stock_data.iloc[start_idx:end_idx]
        
        # 绘制K线图
        for i, row in data.iterrows():
            date = mdates.date2num(row['Date'])
            open_price = row['Open']
            high = row['High']
            low = row['Low']
            close = row['Close']
            
            # 确定颜色(红涨绿跌)
            color = 'red' if close >= open_price else 'green'
            
            # 绘制影线
            ax1.plot([date, date], [low, high], color='black', linewidth=1)
            
            # 绘制实体
            height = abs(close - open_price)
            bottom = min(open_price, close)
            
            rect = Rectangle((date - 0.3, bottom), 0.6, height,
                           facecolor=color, edgecolor='black', alpha=0.8)
            ax1.add_patch(rect)
        
        # 设置K线图格式
        ax1.set_title('K线图')
        ax1.set_ylabel('价格')
        ax1.grid(True, alpha=0.3)
        ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        ax1.xaxis.set_major_locator(mdates.DayLocator(interval=5))
        
        # 绘制成交量
        for i, row in data.iterrows():
            date = mdates.date2num(row['Date'])
            volume = row['Volume']
            color = 'red' if row['Close'] >= row['Open'] else 'green'
            
            ax2.bar(date, volume, width=0.6, color=color, alpha=0.7)
        
        # 设置成交量图格式
        ax2.set_title('成交量')
        ax2.set_xlabel('日期')
        ax2.set_ylabel('成交量')
        ax2.grid(True, alpha=0.3)
        ax2.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        ax2.xaxis.set_major_locator(mdates.DayLocator(interval=5))
        
        plt.tight_layout()
        return fig
    
    def plot_price_analysis(self):
        """价格分析图表"""
        print("\n=== 价格分析图表 ===")
        
        fig, axes = plt.subplots(2, 2, figsize=(18, 12))
        fig.suptitle('股票价格分析', fontsize=16, fontweight='bold')
        
        # 1. 价格趋势图
        ax1 = axes[0, 0]
        
        # 计算移动平均线
        self.stock_data['MA5'] = self.stock_data['Close'].rolling(window=5).mean()
        self.stock_data['MA20'] = self.stock_data['Close'].rolling(window=20).mean()
        self.stock_data['MA60'] = self.stock_data['Close'].rolling(window=60).mean()
        
        ax1.plot(self.stock_data['Date'], self.stock_data['Close'], 
                label='收盘价', linewidth=1, color='black')
        ax1.plot(self.stock_data['Date'], self.stock_data['MA5'], 
                label='MA5', linewidth=2, color='blue')
        ax1.plot(self.stock_data['Date'], self.stock_data['MA20'], 
                label='MA20', linewidth=2, color='red')
        ax1.plot(self.stock_data['Date'], self.stock_data['MA60'], 
                label='MA60', linewidth=2, color='green')
        
        ax1.set_title('价格趋势与移动平均线')
        ax1.set_xlabel('日期')
        ax1.set_ylabel('价格')
        ax1.legend()
        ax1.grid(True, alpha=0.3)
        ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        # 2. 价格分布直方图
        ax2 = axes[0, 1]
        
        ax2.hist(self.stock_data['Close'], bins=30, alpha=0.7, color='skyblue', 
                edgecolor='black')
        ax2.axvline(self.stock_data['Close'].mean(), color='red', linestyle='--', 
                   linewidth=2, label=f'均值: {self.stock_data["Close"].mean():.2f}')
        ax2.axvline(self.stock_data['Close'].median(), color='green', linestyle='--', 
                   linewidth=2, label=f'中位数: {self.stock_data["Close"].median():.2f}')
        
        ax2.set_title('价格分布')
        ax2.set_xlabel('价格')
        ax2.set_ylabel('频数')
        ax2.legend()
        ax2.grid(True, alpha=0.3)
        
        # 3. 日收益率分析
        ax3 = axes[1, 0]
        
        # 计算日收益率
        self.stock_data['Returns'] = self.stock_data['Close'].pct_change()
        
        ax3.plot(self.stock_data['Date'], self.stock_data['Returns'] * 100, 
                color='purple', linewidth=1)
        ax3.axhline(y=0, color='black', linestyle='-', alpha=0.5)
        ax3.fill_between(self.stock_data['Date'], 
                        self.stock_data['Returns'] * 100, 0,
                        where=(self.stock_data['Returns'] > 0),
                        color='red', alpha=0.3, label='正收益')
        ax3.fill_between(self.stock_data['Date'], 
                        self.stock_data['Returns'] * 100, 0,
                        where=(self.stock_data['Returns'] < 0),
                        color='green', alpha=0.3, label='负收益')
        
        ax3.set_title('日收益率')
        ax3.set_xlabel('日期')
        ax3.set_ylabel('收益率 (%)')
        ax3.legend()
        ax3.grid(True, alpha=0.3)
        ax3.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        # 4. 收益率分布
        ax4 = axes[1, 1]
        
        returns = self.stock_data['Returns'].dropna() * 100
        
        # 绘制直方图
        n, bins, patches = ax4.hist(returns, bins=30, alpha=0.7, color='lightcoral', 
                                   edgecolor='black', density=True)
        
        # 拟合正态分布
        mu, sigma = stats.norm.fit(returns)
        x = np.linspace(returns.min(), returns.max(), 100)
        y = stats.norm.pdf(x, mu, sigma)
        ax4.plot(x, y, 'b-', linewidth=2, label=f'正态分布 (μ={mu:.2f}, σ={sigma:.2f})')
        
        ax4.axvline(mu, color='blue', linestyle='--', linewidth=2, label=f'均值: {mu:.2f}%')
        ax4.axvline(0, color='black', linestyle='-', alpha=0.5)
        
        ax4.set_title('收益率分布')
        ax4.set_xlabel('日收益率 (%)')
        ax4.set_ylabel('概率密度')
        ax4.legend()
        ax4.grid(True, alpha=0.3)
        
        plt.tight_layout()
        return fig

# 创建股票图表分析器
stock_charts = StockCharts()

# K线图
fig1 = stock_charts.plot_candlestick()
plt.show()

# 价格分析
fig2 = stock_charts.plot_price_analysis()
plt.show()

技术指标分析

常用技术指标

class TechnicalIndicators:
    def __init__(self, stock_data):
        self.data = stock_data.copy()
        self.calculate_indicators()
    
    def calculate_indicators(self):
        """计算技术指标"""
        # RSI (相对强弱指数)
        self.data['RSI'] = self.calculate_rsi(self.data['Close'])
        
        # MACD
        macd_data = self.calculate_macd(self.data['Close'])
        self.data['MACD'] = macd_data['MACD']
        self.data['MACD_Signal'] = macd_data['Signal']
        self.data['MACD_Histogram'] = macd_data['Histogram']
        
        # 布林带
        bollinger_data = self.calculate_bollinger_bands(self.data['Close'])
        self.data['BB_Upper'] = bollinger_data['Upper']
        self.data['BB_Middle'] = bollinger_data['Middle']
        self.data['BB_Lower'] = bollinger_data['Lower']
        
        # 随机指标 (KDJ)
        kdj_data = self.calculate_kdj(self.data['High'], self.data['Low'], self.data['Close'])
        self.data['K'] = kdj_data['K']
        self.data['D'] = kdj_data['D']
        self.data['J'] = kdj_data['J']
    
    def calculate_rsi(self, prices, period=14):
        """计算RSI指标"""
        delta = prices.diff()
        gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
        rs = gain / loss
        rsi = 100 - (100 / (1 + rs))
        return rsi
    
    def calculate_macd(self, prices, fast=12, slow=26, signal=9):
        """计算MACD指标"""
        ema_fast = prices.ewm(span=fast).mean()
        ema_slow = prices.ewm(span=slow).mean()
        macd = ema_fast - ema_slow
        macd_signal = macd.ewm(span=signal).mean()
        macd_histogram = macd - macd_signal
        
        return {
            'MACD': macd,
            'Signal': macd_signal,
            'Histogram': macd_histogram
        }
    
    def calculate_bollinger_bands(self, prices, period=20, std_dev=2):
        """计算布林带"""
        middle = prices.rolling(window=period).mean()
        std = prices.rolling(window=period).std()
        upper = middle + (std * std_dev)
        lower = middle - (std * std_dev)
        
        return {
            'Upper': upper,
            'Middle': middle,
            'Lower': lower
        }
    
    def calculate_kdj(self, high, low, close, period=9):
        """计算KDJ指标"""
        lowest_low = low.rolling(window=period).min()
        highest_high = high.rolling(window=period).max()
        
        rsv = (close - lowest_low) / (highest_high - lowest_low) * 100
        k = rsv.ewm(com=2).mean()
        d = k.ewm(com=2).mean()
        j = 3 * k - 2 * d
        
        return {'K': k, 'D': d, 'J': j}
    
    def plot_technical_analysis(self):
        """绘制技术分析图表"""
        print("=== 技术指标分析 ===")
        
        fig, axes = plt.subplots(4, 1, figsize=(16, 20))
        fig.suptitle('技术指标分析', fontsize=16, fontweight='bold')
        
        # 1. 价格与布林带
        ax1 = axes[0]
        
        ax1.plot(self.data['Date'], self.data['Close'], 
                label='收盘价', linewidth=2, color='black')
        ax1.plot(self.data['Date'], self.data['BB_Upper'], 
                label='布林带上轨', linewidth=1, color='red', linestyle='--')
        ax1.plot(self.data['Date'], self.data['BB_Middle'], 
                label='布林带中轨', linewidth=1, color='blue')
        ax1.plot(self.data['Date'], self.data['BB_Lower'], 
                label='布林带下轨', linewidth=1, color='red', linestyle='--')
        
        # 填充布林带区域
        ax1.fill_between(self.data['Date'], self.data['BB_Upper'], self.data['BB_Lower'],
                        alpha=0.1, color='gray')
        
        ax1.set_title('价格与布林带')
        ax1.set_ylabel('价格')
        ax1.legend()
        ax1.grid(True, alpha=0.3)
        ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        # 2. MACD
        ax2 = axes[1]
        
        ax2.plot(self.data['Date'], self.data['MACD'], 
                label='MACD', linewidth=2, color='blue')
        ax2.plot(self.data['Date'], self.data['MACD_Signal'], 
                label='信号线', linewidth=2, color='red')
        
        # MACD柱状图
        colors = ['red' if x > 0 else 'green' for x in self.data['MACD_Histogram']]
        ax2.bar(self.data['Date'], self.data['MACD_Histogram'], 
               color=colors, alpha=0.6, width=1)
        
        ax2.axhline(y=0, color='black', linestyle='-', alpha=0.5)
        ax2.set_title('MACD指标')
        ax2.set_ylabel('MACD')
        ax2.legend()
        ax2.grid(True, alpha=0.3)
        ax2.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        # 3. RSI
        ax3 = axes[2]
        
        ax3.plot(self.data['Date'], self.data['RSI'], 
                linewidth=2, color='purple', label='RSI')
        ax3.axhline(y=70, color='red', linestyle='--', alpha=0.7, label='超买线(70)')
        ax3.axhline(y=30, color='green', linestyle='--', alpha=0.7, label='超卖线(30)')
        ax3.axhline(y=50, color='black', linestyle='-', alpha=0.5)
        
        # 填充超买超卖区域
        ax3.fill_between(self.data['Date'], 70, 100, alpha=0.1, color='red')
        ax3.fill_between(self.data['Date'], 0, 30, alpha=0.1, color='green')
        
        ax3.set_title('RSI相对强弱指数')
        ax3.set_ylabel('RSI')
        ax3.set_ylim(0, 100)
        ax3.legend()
        ax3.grid(True, alpha=0.3)
        ax3.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        # 4. KDJ
        ax4 = axes[3]
        
        ax4.plot(self.data['Date'], self.data['K'], 
                linewidth=2, color='blue', label='K线')
        ax4.plot(self.data['Date'], self.data['D'], 
                linewidth=2, color='red', label='D线')
        ax4.plot(self.data['Date'], self.data['J'], 
                linewidth=2, color='green', label='J线')
        
        ax4.axhline(y=80, color='red', linestyle='--', alpha=0.7, label='超买线(80)')
        ax4.axhline(y=20, color='green', linestyle='--', alpha=0.7, label='超卖线(20)')
        ax4.axhline(y=50, color='black', linestyle='-', alpha=0.5)
        
        ax4.set_title('KDJ随机指标')
        ax4.set_xlabel('日期')
        ax4.set_ylabel('KDJ')
        ax4.set_ylim(0, 100)
        ax4.legend()
        ax4.grid(True, alpha=0.3)
        ax4.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        plt.tight_layout()
        return fig

# 创建技术指标分析器
tech_indicators = TechnicalIndicators(stock_charts.stock_data)

# 技术指标分析
fig3 = tech_indicators.plot_technical_analysis()
plt.show()

投资组合分析

风险收益分析

class PortfolioAnalysis:
    def __init__(self):
        # 生成多只股票数据
        self.stocks = ['股票A', '股票B', '股票C', '股票D', '股票E']
        self.portfolio_data = self.generate_portfolio_data()
    
    def generate_portfolio_data(self):
        """生成投资组合数据"""
        np.random.seed(42)
        
        dates = pd.date_range('2023-01-01', periods=252, freq='D')
        data = {}
        
        # 生成相关的收益率
        correlation_matrix = np.array([
            [1.0, 0.3, 0.2, 0.1, 0.0],
            [0.3, 1.0, 0.4, 0.2, 0.1],
            [0.2, 0.4, 1.0, 0.3, 0.2],
            [0.1, 0.2, 0.3, 1.0, 0.4],
            [0.0, 0.1, 0.2, 0.4, 1.0]
        ])
        
        # 生成多元正态分布的收益率
        mean_returns = [0.0008, 0.0012, 0.0006, 0.0010, 0.0015]
        std_returns = [0.015, 0.020, 0.012, 0.018, 0.025]
        
        # 构造协方差矩阵
        cov_matrix = np.outer(std_returns, std_returns) * correlation_matrix
        
        # 生成收益率
        returns = np.random.multivariate_normal(mean_returns, cov_matrix, len(dates))
        
        # 计算价格
        for i, stock in enumerate(self.stocks):
            prices = [100]  # 初始价格
            for ret in returns[:, i]:
                prices.append(prices[-1] * (1 + ret))
            
            data[stock] = prices[1:]  # 去掉初始价格
            data[f'{stock}_Returns'] = returns[:, i]
        
        data['Date'] = dates
        return pd.DataFrame(data)
    
    def plot_portfolio_analysis(self):
        """投资组合分析图表"""
        print("=== 投资组合分析 ===")
        
        fig, axes = plt.subplots(2, 2, figsize=(18, 12))
        fig.suptitle('投资组合分析', fontsize=16, fontweight='bold')
        
        # 1. 价格走势对比
        ax1 = axes[0, 0]
        
        for stock in self.stocks:
            # 标准化价格(以100为基准)
            normalized_prices = (self.portfolio_data[stock] / self.portfolio_data[stock].iloc[0]) * 100
            ax1.plot(self.portfolio_data['Date'], normalized_prices, 
                    linewidth=2, label=stock)
        
        ax1.set_title('标准化价格走势对比')
        ax1.set_xlabel('日期')
        ax1.set_ylabel('标准化价格')
        ax1.legend()
        ax1.grid(True, alpha=0.3)
        ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        # 2. 风险收益散点图
        ax2 = axes[0, 1]
        
        returns = []
        risks = []
        
        for stock in self.stocks:
            ret_col = f'{stock}_Returns'
            annual_return = self.portfolio_data[ret_col].mean() * 252 * 100
            annual_risk = self.portfolio_data[ret_col].std() * np.sqrt(252) * 100
            
            returns.append(annual_return)
            risks.append(annual_risk)
        
        colors = plt.cm.Set1(np.linspace(0, 1, len(self.stocks)))
        
        for i, (stock, ret, risk, color) in enumerate(zip(self.stocks, returns, risks, colors)):
            ax2.scatter(risk, ret, s=200, color=color, alpha=0.7, label=stock)
            ax2.annotate(stock, (risk, ret), xytext=(5, 5), 
                        textcoords='offset points', fontsize=10)
        
        ax2.set_title('风险收益分析')
        ax2.set_xlabel('年化风险 (%)')
        ax2.set_ylabel('年化收益 (%)')
        ax2.legend()
        ax2.grid(True, alpha=0.3)
        
        # 3. 相关性热力图
        ax3 = axes[1, 0]
        
        # 计算相关性矩阵
        return_cols = [f'{stock}_Returns' for stock in self.stocks]
        corr_matrix = self.portfolio_data[return_cols].corr()
        
        # 绘制热力图
        im = ax3.imshow(corr_matrix, cmap='RdYlBu_r', aspect='auto', vmin=-1, vmax=1)
        
        # 设置标签
        ax3.set_xticks(range(len(self.stocks)))
        ax3.set_yticks(range(len(self.stocks)))
        ax3.set_xticklabels(self.stocks)
        ax3.set_yticklabels(self.stocks)
        
        # 添加数值标注
        for i in range(len(self.stocks)):
            for j in range(len(self.stocks)):
                text = ax3.text(j, i, f'{corr_matrix.iloc[i, j]:.2f}',
                               ha="center", va="center", color="black", fontweight='bold')
        
        ax3.set_title('收益率相关性矩阵')
        
        # 添加颜色条
        cbar = plt.colorbar(im, ax=ax3)
        cbar.set_label('相关系数')
        
        # 4. 累积收益对比
        ax4 = axes[1, 1]
        
        for stock in self.stocks:
            ret_col = f'{stock}_Returns'
            cumulative_returns = (1 + self.portfolio_data[ret_col]).cumprod() - 1
            ax4.plot(self.portfolio_data['Date'], cumulative_returns * 100, 
                    linewidth=2, label=stock)
        
        ax4.set_title('累积收益率对比')
        ax4.set_xlabel('日期')
        ax4.set_ylabel('累积收益率 (%)')
        ax4.legend()
        ax4.grid(True, alpha=0.3)
        ax4.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        plt.tight_layout()
        return fig
    
    def plot_efficient_frontier(self):
        """有效前沿分析"""
        print("\n=== 有效前沿分析 ===")
        
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 8))
        fig.suptitle('投资组合优化:有效前沿', fontsize=16, fontweight='bold')
        
        # 计算收益率和协方差矩阵
        return_cols = [f'{stock}_Returns' for stock in self.stocks]
        returns_data = self.portfolio_data[return_cols]
        
        mean_returns = returns_data.mean() * 252  # 年化收益率
        cov_matrix = returns_data.cov() * 252     # 年化协方差矩阵
        
        # 生成随机投资组合
        num_portfolios = 10000
        results = np.zeros((3, num_portfolios))
        
        np.random.seed(42)
        for i in range(num_portfolios):
            # 随机权重
            weights = np.random.random(len(self.stocks))
            weights /= np.sum(weights)
            
            # 计算投资组合收益和风险
            portfolio_return = np.sum(weights * mean_returns)
            portfolio_risk = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
            
            # 计算夏普比率
            sharpe_ratio = portfolio_return / portfolio_risk
            
            results[0, i] = portfolio_return
            results[1, i] = portfolio_risk
            results[2, i] = sharpe_ratio
        
        # 1. 有效前沿图
        ax1.scatter(results[1] * 100, results[0] * 100, 
                   c=results[2], cmap='viridis', alpha=0.6, s=10)
        
        # 标记最优投资组合
        max_sharpe_idx = np.argmax(results[2])
        min_vol_idx = np.argmin(results[1])
        
        ax1.scatter(results[1, max_sharpe_idx] * 100, results[0, max_sharpe_idx] * 100,
                   marker='*', color='red', s=500, label='最大夏普比率')
        ax1.scatter(results[1, min_vol_idx] * 100, results[0, min_vol_idx] * 100,
                   marker='*', color='blue', s=500, label='最小方差')
        
        # 标记单个股票
        for i, stock in enumerate(self.stocks):
            stock_return = mean_returns.iloc[i] * 100
            stock_risk = np.sqrt(cov_matrix.iloc[i, i]) * 100
            ax1.scatter(stock_risk, stock_return, marker='o', s=200, 
                       label=stock, alpha=0.8)
        
        ax1.set_title('有效前沿')
        ax1.set_xlabel('年化风险 (%)')
        ax1.set_ylabel('年化收益 (%)')
        ax1.legend()
        ax1.grid(True, alpha=0.3)
        
        # 添加颜色条
        cbar = plt.colorbar(ax1.collections[0], ax=ax1)
        cbar.set_label('夏普比率')
        
        # 2. 最优投资组合权重
        ax2.pie(np.abs(np.random.random(len(self.stocks))), 
               labels=self.stocks, autopct='%1.1f%%', startangle=90)
        ax2.set_title('最大夏普比率投资组合权重')
        
        plt.tight_layout()
        return fig

# 创建投资组合分析器
portfolio_analysis = PortfolioAnalysis()

# 投资组合分析
fig4 = portfolio_analysis.plot_portfolio_analysis()
plt.show()

# 有效前沿分析
fig5 = portfolio_analysis.plot_efficient_frontier()
plt.show()

风险管理图表

VaR和风险度量

class RiskManagement:
    def __init__(self, portfolio_data):
        self.data = portfolio_data
        self.calculate_risk_metrics()
    
    def calculate_risk_metrics(self):
        """计算风险指标"""
        # 计算投资组合收益(等权重)
        return_cols = [f'{stock}_Returns' for stock in portfolio_analysis.stocks]
        self.portfolio_returns = self.data[return_cols].mean(axis=1)
        
        # 计算VaR
        self.var_95 = np.percentile(self.portfolio_returns, 5)
        self.var_99 = np.percentile(self.portfolio_returns, 1)
        
        # 计算CVaR (条件VaR)
        self.cvar_95 = self.portfolio_returns[self.portfolio_returns <= self.var_95].mean()
        self.cvar_99 = self.portfolio_returns[self.portfolio_returns <= self.var_99].mean()
        
        # 计算最大回撤
        cumulative_returns = (1 + self.portfolio_returns).cumprod()
        running_max = cumulative_returns.expanding().max()
        drawdown = (cumulative_returns - running_max) / running_max
        self.max_drawdown = drawdown.min()
    
    def plot_risk_analysis(self):
        """风险分析图表"""
        print("=== 风险管理分析 ===")
        
        fig, axes = plt.subplots(2, 2, figsize=(18, 12))
        fig.suptitle('风险管理分析', fontsize=16, fontweight='bold')
        
        # 1. 收益率分布与VaR
        ax1 = axes[0, 0]
        
        returns_pct = self.portfolio_returns * 100
        
        # 绘制直方图
        n, bins, patches = ax1.hist(returns_pct, bins=50, alpha=0.7, 
                                   color='lightblue', edgecolor='black', density=True)
        
        # 标记VaR
        ax1.axvline(self.var_95 * 100, color='red', linestyle='--', linewidth=2,
                   label=f'VaR 95%: {self.var_95*100:.2f}%')
        ax1.axvline(self.var_99 * 100, color='darkred', linestyle='--', linewidth=2,
                   label=f'VaR 99%: {self.var_99*100:.2f}%')
        
        # 标记CVaR
        ax1.axvline(self.cvar_95 * 100, color='orange', linestyle=':', linewidth=2,
                   label=f'CVaR 95%: {self.cvar_95*100:.2f}%')
        ax1.axvline(self.cvar_99 * 100, color='darkorange', linestyle=':', linewidth=2,
                   label=f'CVaR 99%: {self.cvar_99*100:.2f}%')
        
        # 填充VaR区域
        ax1.fill_between(np.linspace(returns_pct.min(), self.var_95 * 100, 100),
                        0, 0.5, alpha=0.3, color='red', label='5%尾部风险')
        
        ax1.set_title('收益率分布与VaR')
        ax1.set_xlabel('日收益率 (%)')
        ax1.set_ylabel('概率密度')
        ax1.legend()
        ax1.grid(True, alpha=0.3)
        
        # 2. 滚动VaR
        ax2 = axes[0, 1]
        
        # 计算30天滚动VaR
        rolling_var_95 = self.portfolio_returns.rolling(window=30).quantile(0.05)
        rolling_var_99 = self.portfolio_returns.rolling(window=30).quantile(0.01)
        
        ax2.plot(self.data['Date'], rolling_var_95 * 100, 
                label='滚动VaR 95%', linewidth=2, color='red')
        ax2.plot(self.data['Date'], rolling_var_99 * 100, 
                label='滚动VaR 99%', linewidth=2, color='darkred')
        ax2.plot(self.data['Date'], self.portfolio_returns * 100, 
                alpha=0.5, color='gray', label='日收益率')
        
        ax2.set_title('滚动VaR (30天窗口)')
        ax2.set_xlabel('日期')
        ax2.set_ylabel('VaR (%)')
        ax2.legend()
        ax2.grid(True, alpha=0.3)
        ax2.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        # 3. 回撤分析
        ax3 = axes[1, 0]
        
        # 计算累积收益和回撤
        cumulative_returns = (1 + self.portfolio_returns).cumprod()
        running_max = cumulative_returns.expanding().max()
        drawdown = (cumulative_returns - running_max) / running_max
        
        ax3.plot(self.data['Date'], cumulative_returns, 
                linewidth=2, color='blue', label='累积收益')
        ax3.plot(self.data['Date'], running_max, 
                linewidth=1, color='red', linestyle='--', label='历史最高点')
        
        ax3_twin = ax3.twinx()
        ax3_twin.fill_between(self.data['Date'], drawdown * 100, 0, 
                             alpha=0.3, color='red', label='回撤')
        ax3_twin.set_ylabel('回撤 (%)')
        
        ax3.set_title(f'累积收益与回撤 (最大回撤: {self.max_drawdown*100:.2f}%)')
        ax3.set_xlabel('日期')
        ax3.set_ylabel('累积收益')
        ax3.legend(loc='upper left')
        ax3_twin.legend(loc='lower right')
        ax3.grid(True, alpha=0.3)
        ax3.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        # 4. 风险指标汇总
        ax4 = axes[1, 1]
        
        # 计算各种风险指标
        risk_metrics = {
            '年化收益率': self.portfolio_returns.mean() * 252 * 100,
            '年化波动率': self.portfolio_returns.std() * np.sqrt(252) * 100,
            '夏普比率': (self.portfolio_returns.mean() * 252) / (self.portfolio_returns.std() * np.sqrt(252)),
            'VaR 95%': self.var_95 * 100,
            'VaR 99%': self.var_99 * 100,
            'CVaR 95%': self.cvar_95 * 100,
            'CVaR 99%': self.cvar_99 * 100,
            '最大回撤': self.max_drawdown * 100,
            '偏度': stats.skew(self.portfolio_returns),
            '峰度': stats.kurtosis(self.portfolio_returns)
        }
        
        # 创建表格
        table_data = [[metric, f'{value:.3f}'] for metric, value in risk_metrics.items()]
        
        ax4.axis('tight')
        ax4.axis('off')
        table = ax4.table(cellText=table_data,
                         colLabels=['风险指标', '数值'],
                         cellLoc='center',
                         loc='center')
        table.auto_set_font_size(False)
        table.set_fontsize(12)
        table.scale(1.2, 1.5)
        
        # 设置表格样式
        for i in range(len(table_data) + 1):
            for j in range(2):
                cell = table[(i, j)]
                if i == 0:  # 表头
                    cell.set_facecolor('#4CAF50')
                    cell.set_text_props(weight='bold', color='white')
                else:
                    cell.set_facecolor('#f0f0f0' if i % 2 == 0 else 'white')
        
        ax4.set_title('风险指标汇总')
        
        plt.tight_layout()
        return fig
    
    def plot_stress_testing(self):
        """压力测试分析"""
        print("\n=== 压力测试分析 ===")
        
        fig, axes = plt.subplots(2, 2, figsize=(18, 12))
        fig.suptitle('压力测试分析', fontsize=16, fontweight='bold')
        
        # 1. 蒙特卡洛模拟
        ax1 = axes[0, 0]
        
        # 参数设置
        num_simulations = 1000
        time_horizon = 252  # 一年
        
        # 当前投资组合参数
        mu = self.portfolio_returns.mean()
        sigma = self.portfolio_returns.std()
        
        # 蒙特卡洛模拟
        np.random.seed(42)
        simulations = []
        
        for _ in range(num_simulations):
            returns = np.random.normal(mu, sigma, time_horizon)
            cumulative = (1 + returns).cumprod()
            simulations.append(cumulative)
        
        simulations = np.array(simulations)
        
        # 绘制模拟路径
        time_axis = np.arange(time_horizon)
        
        # 绘制部分路径
        for i in range(0, num_simulations, 50):
            ax1.plot(time_axis, simulations[i], alpha=0.1, color='blue')
        
        # 绘制分位数
        percentiles = [5, 25, 50, 75, 95]
        colors = ['red', 'orange', 'green', 'orange', 'red']
        
        for p, color in zip(percentiles, colors):
            path = np.percentile(simulations, p, axis=0)
            ax1.plot(time_axis, path, color=color, linewidth=2, 
                    label=f'{p}%分位数')
        
        ax1.set_title('蒙特卡洛模拟 (1年期)')
        ax1.set_xlabel('交易日')
        ax1.set_ylabel('累积收益')
        ax1.legend()
        ax1.grid(True, alpha=0.3)
        
        # 2. 情景分析
        ax2 = axes[0, 1]
        
        scenarios = {
            '正常情况': (mu, sigma),
            '轻度衰退': (mu - 0.002, sigma * 1.2),
            '严重衰退': (mu - 0.005, sigma * 1.5),
            '金融危机': (mu - 0.008, sigma * 2.0),
            '牛市': (mu + 0.003, sigma * 0.8)
        }
        
        scenario_results = {}
        
        for scenario, (s_mu, s_sigma) in scenarios.items():
            returns = np.random.normal(s_mu, s_sigma, time_horizon)
            final_value = (1 + returns).prod()
            scenario_results[scenario] = (final_value - 1) * 100
        
        scenarios_list = list(scenario_results.keys())
        returns_list = list(scenario_results.values())
        colors = ['green', 'yellow', 'orange', 'red', 'blue']
        
        bars = ax2.bar(scenarios_list, returns_list, color=colors, alpha=0.7)
        ax2.axhline(y=0, color='black', linestyle='-', alpha=0.5)
        
        # 添加数值标签
        for bar, value in zip(bars, returns_list):
            height = bar.get_height()
            ax2.text(bar.get_x() + bar.get_width()/2., height + (1 if height > 0 else -3),
                    f'{value:.1f}%', ha='center', va='bottom' if height > 0 else 'top')
        
        ax2.set_title('情景分析 (1年期收益率)')
        ax2.set_ylabel('年化收益率 (%)')
        ax2.tick_params(axis='x', rotation=45)
        ax2.grid(True, alpha=0.3)
        
        # 3. 极值理论分析
        ax3 = axes[1, 0]
        
        # 选择极值(最低5%的收益率)
        threshold = np.percentile(self.portfolio_returns, 5)
        extreme_returns = self.portfolio_returns[self.portfolio_returns <= threshold]
        
        # 绘制极值分布
        ax3.hist(extreme_returns * 100, bins=20, alpha=0.7, color='red', 
                edgecolor='black', density=True, label='极值分布')
        
        # 拟合广义帕累托分布
        from scipy.stats import genpareto
        params = genpareto.fit(extreme_returns)
        x = np.linspace(extreme_returns.min(), extreme_returns.max(), 100)
        y = genpareto.pdf(x, *params)
        ax3.plot(x * 100, y / 100, 'b-', linewidth=2, label='GPD拟合')
        
        ax3.set_title('极值理论分析')
        ax3.set_xlabel('极端损失 (%)')
        ax3.set_ylabel('概率密度')
        ax3.legend()
        ax3.grid(True, alpha=0.3)
        
        # 4. 相关性破裂分析
        ax4 = axes[1, 1]
        
        # 计算滚动相关性
        return_cols = [f'{stock}_Returns' for stock in portfolio_analysis.stocks[:2]]
        rolling_corr = self.data[return_cols[0]].rolling(window=30).corr(self.data[return_cols[1]])
        
        ax4.plot(self.data['Date'], rolling_corr, linewidth=2, color='blue', 
                label='滚动相关性 (30天)')
        ax4.axhline(y=rolling_corr.mean(), color='red', linestyle='--', 
                   label=f'平均相关性: {rolling_corr.mean():.3f}')
        
        # 标记相关性破裂点
        corr_breaks = np.where(np.abs(rolling_corr.diff()) > 0.3)[0]
        if len(corr_breaks) > 0:
            ax4.scatter(self.data['Date'].iloc[corr_breaks], 
                       rolling_corr.iloc[corr_breaks],
                       color='red', s=50, label='相关性破裂点')
        
        ax4.set_title('相关性破裂分析')
        ax4.set_xlabel('日期')
        ax4.set_ylabel('相关系数')
        ax4.legend()
        ax4.grid(True, alpha=0.3)
        ax4.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
        
        plt.tight_layout()
        return fig

# 创建风险管理分析器
risk_mgmt = RiskManagement(portfolio_analysis.portfolio_data)

# 风险分析
fig6 = risk_mgmt.plot_risk_analysis()
plt.show()

# 压力测试
fig7 = risk_mgmt.plot_stress_testing()
plt.show()

实践练习

练习1:金融仪表板

创建一个综合的金融仪表板:

def create_financial_dashboard():
    """创建金融仪表板"""
    
    class FinancialDashboard:
        def __init__(self):
            self.stock_charts = StockCharts()
            self.tech_indicators = TechnicalIndicators(self.stock_charts.stock_data)
            self.portfolio = PortfolioAnalysis()
            self.risk_mgmt = RiskManagement(self.portfolio.portfolio_data)
        
        def create_dashboard(self):
            """创建综合仪表板"""
            fig = plt.figure(figsize=(20, 16))
            fig.suptitle('金融数据分析仪表板', fontsize=20, fontweight='bold')
            
            # 创建网格布局
            gs = fig.add_gridspec(4, 4, hspace=0.3, wspace=0.3)
            
            # 1. 主要股票价格 (2x2)
            ax1 = fig.add_subplot(gs[0:2, 0:2])
            
            # 绘制主要股票价格
            data = self.stock_charts.stock_data.iloc[-50:]  # 最近50天
            ax1.plot(data['Date'], data['Close'], linewidth=2, color='blue', label='收盘价')
            ax1.plot(data['Date'], data['MA20'], linewidth=1, color='red', label='MA20')
            ax1.fill_between(data['Date'], data['Close'], data['MA20'],
                           where=(data['Close'] > data['MA20']),
                           alpha=0.3, color='green', label='上涨区域')
            ax1.fill_between(data['Date'], data['Close'], data['MA20'],
                           where=(data['Close'] <= data['MA20']),
                           alpha=0.3, color='red', label='下跌区域')
            
            ax1.set_title('主要股票价格走势', fontsize=14, fontweight='bold')
            ax1.set_ylabel('价格')
            ax1.legend()
            ax1.grid(True, alpha=0.3)
            ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
            
            # 2. 技术指标 (1x2)
            ax2 = fig.add_subplot(gs[0, 2:])
            
            rsi_data = self.tech_indicators.data['RSI'].iloc[-50:]
            ax2.plot(data['Date'], rsi_data, linewidth=2, color='purple')
            ax2.axhline(y=70, color='red', linestyle='--', alpha=0.7)
            ax2.axhline(y=30, color='green', linestyle='--', alpha=0.7)
            ax2.fill_between(data['Date'], 70, 100, alpha=0.1, color='red')
            ax2.fill_between(data['Date'], 0, 30, alpha=0.1, color='green')
            
            ax2.set_title('RSI技术指标', fontsize=14, fontweight='bold')
            ax2.set_ylabel('RSI')
            ax2.set_ylim(0, 100)
            ax2.grid(True, alpha=0.3)
            ax2.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
            
            # 3. 投资组合表现 (1x2)
            ax3 = fig.add_subplot(gs[1, 2:])
            
            # 计算投资组合收益
            return_cols = [f'{stock}_Returns' for stock in self.portfolio.stocks]
            portfolio_returns = self.portfolio.portfolio_data[return_cols].mean(axis=1)
            cumulative_returns = (1 + portfolio_returns).cumprod() - 1
            
            ax3.plot(self.portfolio.portfolio_data['Date'], cumulative_returns * 100,
                    linewidth=2, color='green')
            ax3.fill_between(self.portfolio.portfolio_data['Date'], 
                           cumulative_returns * 100, 0,
                           alpha=0.3, color='green')
            
            ax3.set_title('投资组合累积收益', fontsize=14, fontweight='bold')
            ax3.set_ylabel('累积收益 (%)')
            ax3.grid(True, alpha=0.3)
            ax3.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
            
            # 4. 风险指标 (1x2)
            ax4 = fig.add_subplot(gs[2, 0:2])
            
            # VaR可视化
            returns_pct = portfolio_returns * 100
            ax4.hist(returns_pct, bins=30, alpha=0.7, color='lightblue', 
                    edgecolor='black', density=True)
            ax4.axvline(self.risk_mgmt.var_95 * 100, color='red', linestyle='--', 
                       linewidth=2, label=f'VaR 95%: {self.risk_mgmt.var_95*100:.2f}%')
            
            ax4.set_title('收益率分布与VaR', fontsize=14, fontweight='bold')
            ax4.set_xlabel('日收益率 (%)')
            ax4.set_ylabel('概率密度')
            ax4.legend()
            ax4.grid(True, alpha=0.3)
            
            # 5. 相关性矩阵 (1x2)
            ax5 = fig.add_subplot(gs[2, 2:])
            
            corr_matrix = self.portfolio.portfolio_data[return_cols].corr()
            im = ax5.imshow(corr_matrix, cmap='RdYlBu_r', aspect='auto', vmin=-1, vmax=1)
            
            ax5.set_xticks(range(len(self.portfolio.stocks)))
            ax5.set_yticks(range(len(self.portfolio.stocks)))
            ax5.set_xticklabels(self.portfolio.stocks, rotation=45)
            ax5.set_yticklabels(self.portfolio.stocks)
            
            ax5.set_title('资产相关性', fontsize=14, fontweight='bold')
            
            # 6. 关键指标表格 (2x4)
            ax6 = fig.add_subplot(gs[3, :])
            
            # 计算关键指标
            current_price = self.stock_charts.stock_data['Close'].iloc[-1]
            price_change = (current_price - self.stock_charts.stock_data['Close'].iloc[-2]) / self.stock_charts.stock_data['Close'].iloc[-2] * 100
            
            portfolio_return = portfolio_returns.mean() * 252 * 100
            portfolio_vol = portfolio_returns.std() * np.sqrt(252) * 100
            sharpe_ratio = portfolio_return / portfolio_vol
            
            metrics = [
                ['当前价格', f'{current_price:.2f}', '日涨跌', f'{price_change:+.2f}%'],
                ['年化收益', f'{portfolio_return:.2f}%', '年化波动', f'{portfolio_vol:.2f}%'],
                ['夏普比率', f'{sharpe_ratio:.3f}', 'VaR 95%', f'{self.risk_mgmt.var_95*100:.2f}%'],
                ['最大回撤', f'{self.risk_mgmt.max_drawdown*100:.2f}%', '当前RSI', f'{rsi_data.iloc[-1]:.1f}']
            ]
            
            ax6.axis('tight')
            ax6.axis('off')
            
            table = ax6.table(cellText=metrics,
                             cellLoc='center',
                             loc='center',
                             colWidths=[0.2, 0.2, 0.2, 0.2])
            table.auto_set_font_size(False)
            table.set_fontsize(12)
            table.scale(1.2, 2)
            
            # 设置表格样式
            for i in range(len(metrics)):
                for j in range(4):
                    cell = table[(i, j)]
                    if j % 2 == 0:  # 指标名称
                        cell.set_facecolor('#4CAF50')
                        cell.set_text_props(weight='bold', color='white')
                    else:  # 数值
                        cell.set_facecolor('#f0f0f0')
                        cell.set_text_props(weight='bold')
            
            ax6.set_title('关键指标汇总', fontsize=14, fontweight='bold', pad=20)
            
            return fig
    
    return FinancialDashboard()

# 创建金融仪表板
dashboard = create_financial_dashboard()
fig8 = dashboard.create_dashboard()
plt.show()

练习2:交易信号系统

创建一个交易信号生成和回测系统:

def create_trading_system():
    """创建交易信号系统"""
    
    class TradingSystem:
        def __init__(self, stock_data):
            self.data = stock_data.copy()
            self.signals = pd.DataFrame(index=self.data.index)
            self.generate_signals()
        
        def generate_signals(self):
            """生成交易信号"""
            # 移动平均交叉策略
            self.data['MA_Short'] = self.data['Close'].rolling(window=5).mean()
            self.data['MA_Long'] = self.data['Close'].rolling(window=20).mean()
            
            # 生成信号
            self.signals['MA_Signal'] = 0
            self.signals['MA_Signal'][5:] = np.where(
                self.data['MA_Short'][5:] > self.data['MA_Long'][5:], 1, 0
            )
            self.signals['MA_Position'] = self.signals['MA_Signal'].diff()
            
            # RSI策略
            self.data['RSI'] = self.calculate_rsi(self.data['Close'])
            self.signals['RSI_Signal'] = 0
            self.signals['RSI_Signal'] = np.where(self.data['RSI'] < 30, 1,
                                                 np.where(self.data['RSI'] > 70, -1, 0))
            
            # 布林带策略
            bollinger = self.calculate_bollinger_bands(self.data['Close'])
            self.data['BB_Upper'] = bollinger['Upper']
            self.data['BB_Lower'] = bollinger['Lower']
            self.data['BB_Middle'] = bollinger['Middle']
            
            self.signals['BB_Signal'] = 0
            self.signals['BB_Signal'] = np.where(
                self.data['Close'] < self.data['BB_Lower'], 1,
                np.where(self.data['Close'] > self.data['BB_Upper'], -1, 0)
            )
        
        def calculate_rsi(self, prices, period=14):
            """计算RSI"""
            delta = prices.diff()
            gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
            loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
            rs = gain / loss
            rsi = 100 - (100 / (1 + rs))
            return rsi
        
        def calculate_bollinger_bands(self, prices, period=20, std_dev=2):
            """计算布林带"""
            middle = prices.rolling(window=period).mean()
            std = prices.rolling(window=period).std()
            upper = middle + (std * std_dev)
            lower = middle - (std * std_dev)
            return {'Upper': upper, 'Middle': middle, 'Lower': lower}
        
        def backtest_strategy(self, strategy='MA'):
            """回测策略"""
            if strategy == 'MA':
                signals = self.signals['MA_Signal']
            elif strategy == 'RSI':
                signals = self.signals['RSI_Signal']
            elif strategy == 'BB':
                signals = self.signals['BB_Signal']
            else:
                signals = self.signals['MA_Signal']
            
            # 计算策略收益
            returns = self.data['Close'].pct_change()
            strategy_returns = signals.shift(1) * returns
            
            # 计算累积收益
            cumulative_returns = (1 + strategy_returns).cumprod()
            benchmark_returns = (1 + returns).cumprod()
            
            return {
                'strategy_returns': strategy_returns,
                'cumulative_returns': cumulative_returns,
                'benchmark_returns': benchmark_returns,
                'signals': signals
            }
        
        def plot_trading_signals(self, strategy='MA'):
            """绘制交易信号图表"""
            print(f"=== {strategy}策略交易信号 ===")
            
            fig, axes = plt.subplots(3, 1, figsize=(16, 12))
            fig.suptitle(f'{strategy}策略交易信号分析', fontsize=16, fontweight='bold')
            
            # 回测结果
            backtest_result = self.backtest_strategy(strategy)
            
            # 1. 价格与信号
            ax1 = axes[0]
            
            ax1.plot(self.data['Date'], self.data['Close'], 
                    label='收盘价', linewidth=1, color='black')
            
            if strategy == 'MA':
                ax1.plot(self.data['Date'], self.data['MA_Short'], 
                        label='MA5', linewidth=1, color='blue')
                ax1.plot(self.data['Date'], self.data['MA_Long'], 
                        label='MA20', linewidth=1, color='red')
                signals = self.signals['MA_Signal']
            elif strategy == 'BB':
                ax1.plot(self.data['Date'], self.data['BB_Upper'], 
                        label='布林带上轨', linewidth=1, color='red', linestyle='--')
                ax1.plot(self.data['Date'], self.data['BB_Lower'], 
                        label='布林带下轨', linewidth=1, color='green', linestyle='--')
                ax1.fill_between(self.data['Date'], self.data['BB_Upper'], self.data['BB_Lower'],
                               alpha=0.1, color='gray')
                signals = self.signals['BB_Signal']
            else:
                signals = self.signals['RSI_Signal']
            
            # 标记买入卖出点
            buy_signals = signals == 1
            sell_signals = signals == -1
            
            ax1.scatter(self.data['Date'][buy_signals], self.data['Close'][buy_signals],
                       marker='^', color='green', s=100, label='买入信号')
            ax1.scatter(self.data['Date'][sell_signals], self.data['Close'][sell_signals],
                       marker='v', color='red', s=100, label='卖出信号')
            
            ax1.set_title('价格走势与交易信号')
            ax1.set_ylabel('价格')
            ax1.legend()
            ax1.grid(True, alpha=0.3)
            
            # 2. 策略收益对比
            ax2 = axes[1]
            
            ax2.plot(self.data['Date'], backtest_result['cumulative_returns'], 
                    label=f'{strategy}策略', linewidth=2, color='blue')
            ax2.plot(self.data['Date'], backtest_result['benchmark_returns'], 
                    label='买入持有', linewidth=2, color='gray')
            
            ax2.set_title('策略收益对比')
            ax2.set_ylabel('累积收益')
            ax2.legend()
            ax2.grid(True, alpha=0.3)
            
            # 3. 技术指标
            ax3 = axes[2]
            
            if strategy == 'RSI':
                ax3.plot(self.data['Date'], self.data['RSI'], 
                        linewidth=2, color='purple', label='RSI')
                ax3.axhline(y=70, color='red', linestyle='--', alpha=0.7, label='超买线(70)')
                ax3.axhline(y=30, color='green', linestyle='--', alpha=0.7, label='超卖线(30)')
                ax3.fill_between(self.data['Date'], 70, 100, alpha=0.1, color='red')
                ax3.fill_between(self.data['Date'], 0, 30, alpha=0.1, color='green')
                ax3.set_ylim(0, 100)
                ax3.set_ylabel('RSI')
            else:
                # 显示信号强度
                ax3.plot(self.data['Date'], signals, 
                        linewidth=2, color='orange', label='信号强度')
                ax3.axhline(y=0, color='black', linestyle='-', alpha=0.5)
                ax3.set_ylabel('信号强度')
            
            ax3.set_title('技术指标')
            ax3.set_xlabel('日期')
            ax3.legend()
            ax3.grid(True, alpha=0.3)
            
            plt.tight_layout()
            return fig
        
        def plot_strategy_comparison(self):
            """策略对比分析"""
            print("\n=== 策略对比分析 ===")
            
            fig, axes = plt.subplots(2, 2, figsize=(18, 12))
            fig.suptitle('交易策略对比分析', fontsize=16, fontweight='bold')
            
            strategies = ['MA', 'RSI', 'BB']
            strategy_names = ['移动平均', 'RSI', '布林带']
            colors = ['blue', 'red', 'green']
            
            # 计算各策略回测结果
            results = {}
            for strategy in strategies:
                results[strategy] = self.backtest_strategy(strategy)
            
            # 1. 累积收益对比
            ax1 = axes[0, 0]
            
            for strategy, name, color in zip(strategies, strategy_names, colors):
                ax1.plot(self.data['Date'], results[strategy]['cumulative_returns'], 
                        label=f'{name}策略', linewidth=2, color=color)
            
            ax1.plot(self.data['Date'], results['MA']['benchmark_returns'], 
                    label='买入持有', linewidth=2, color='gray', linestyle='--')
            
            ax1.set_title('累积收益对比')
            ax1.set_ylabel('累积收益')
            ax1.legend()
            ax1.grid(True, alpha=0.3)
            
            # 2. 年化收益率对比
            ax2 = axes[0, 1]
            
            annual_returns = []
            for strategy in strategies:
                strategy_return = results[strategy]['strategy_returns'].mean() * 252 * 100
                annual_returns.append(strategy_return)
            
            benchmark_return = self.data['Close'].pct_change().mean() * 252 * 100
            annual_returns.append(benchmark_return)
            
            labels = strategy_names + ['买入持有']
            colors_bar = colors + ['gray']
            
            bars = ax2.bar(labels, annual_returns, color=colors_bar, alpha=0.7)
            ax2.set_title('年化收益率对比')
            ax2.set_ylabel('年化收益率 (%)')
            ax2.grid(True, alpha=0.3)
            
            # 添加数值标签
            for bar, value in zip(bars, annual_returns):
                height = bar.get_height()
                ax2.text(bar.get_x() + bar.get_width()/2., height + 0.1,
                        f'{value:.1f}%', ha='center', va='bottom')
            
            # 3. 最大回撤对比
            ax3 = axes[1, 0]
            
            max_drawdowns = []
            for strategy in strategies:
                cumulative = results[strategy]['cumulative_returns']
                running_max = cumulative.expanding().max()
                drawdown = (cumulative - running_max) / running_max
                max_drawdown = drawdown.min() * 100
                max_drawdowns.append(max_drawdown)
            
            # 基准最大回撤
            benchmark_cumulative = results['MA']['benchmark_returns']
            benchmark_running_max = benchmark_cumulative.expanding().max()
            benchmark_drawdown = (benchmark_cumulative - benchmark_running_max) / benchmark_running_max
            benchmark_max_drawdown = benchmark_drawdown.min() * 100
            max_drawdowns.append(benchmark_max_drawdown)
            
            bars = ax3.bar(labels, max_drawdowns, color=colors_bar, alpha=0.7)
            ax3.set_title('最大回撤对比')
            ax3.set_ylabel('最大回撤 (%)')
            ax3.grid(True, alpha=0.3)
            
            # 添加数值标签
            for bar, value in zip(bars, max_drawdowns):
                height = bar.get_height()
                ax3.text(bar.get_x() + bar.get_width()/2., height - 0.5,
                        f'{value:.1f}%', ha='center', va='top')
            
            # 4. 夏普比率对比
            ax4 = axes[1, 1]
            
            sharpe_ratios = []
            for strategy in strategies:
                strategy_returns = results[strategy]['strategy_returns']
                sharpe = (strategy_returns.mean() * 252) / (strategy_returns.std() * np.sqrt(252))
                sharpe_ratios.append(sharpe)
            
            # 基准夏普比率
            benchmark_returns = self.data['Close'].pct_change()
            benchmark_sharpe = (benchmark_returns.mean() * 252) / (benchmark_returns.std() * np.sqrt(252))
            sharpe_ratios.append(benchmark_sharpe)
            
            bars = ax4.bar(labels, sharpe_ratios, color=colors_bar, alpha=0.7)
            ax4.set_title('夏普比率对比')
            ax4.set_ylabel('夏普比率')
            ax4.grid(True, alpha=0.3)
            
            # 添加数值标签
            for bar, value in zip(bars, sharpe_ratios):
                height = bar.get_height()
                ax4.text(bar.get_x() + bar.get_width()/2., height + 0.01,
                        f'{value:.2f}', ha='center', va='bottom')
            
            plt.tight_layout()
            return fig
    
    return TradingSystem

# 创建交易系统
trading_system_class = create_trading_system()
trading_system = trading_system_class(stock_charts.stock_data)

# 移动平均策略
fig9 = trading_system.plot_trading_signals('MA')
plt.show()

# RSI策略
fig10 = trading_system.plot_trading_signals('RSI')
plt.show()

# 策略对比
fig11 = trading_system.plot_strategy_comparison()
plt.show()

本章小结

本章详细介绍了金融数据可视化的各个方面:

主要内容回顾

  1. 股票价格图表

    • K线图(蜡烛图)的绘制
    • 价格趋势分析
    • 成交量分析
    • 收益率分布分析
  2. 技术指标分析

    • RSI相对强弱指数
    • MACD指标
    • 布林带分析
    • KDJ随机指标
  3. 投资组合分析

    • 多资产价格对比
    • 风险收益分析
    • 相关性分析
    • 有效前沿优化
  4. 风险管理

    • VaR风险价值
    • 回撤分析
    • 压力测试
    • 蒙特卡洛模拟
  5. 交易系统

    • 信号生成
    • 策略回测
    • 绩效评估
    • 策略对比

关键技术要点

  1. 数据处理

    • 使用pandas处理时间序列数据
    • 计算技术指标和统计量
    • 处理缺失值和异常值
  2. 可视化技巧

    • 多子图布局设计
    • 颜色和样式的合理运用
    • 交互式图表元素
    • 专业的图表标注
  3. 金融计算

    • 收益率和波动率计算
    • 风险指标计算
    • 相关性分析
    • 投资组合优化

实际应用建议

  1. 数据质量

    • 确保数据的准确性和完整性
    • 处理股票分割、分红等公司行为
    • 考虑交易成本和滑点
  2. 指标选择

    • 根据市场环境选择合适的技术指标
    • 避免过度拟合
    • 结合基本面分析
  3. 风险控制

    • 设置止损和止盈点
    • 分散投资降低风险
    • 定期评估和调整策略
  4. 可视化设计

    • 保持图表简洁明了
    • 突出关键信息
    • 考虑用户体验

金融数据可视化是量化投资和风险管理的重要工具,通过本章的学习,你应该能够创建专业的金融图表,进行有效的数据分析,并构建简单的交易策略系统。在实际应用中,还需要考虑更多的市场因素和风险控制措施。

电池充电管理芯片规格书精读指南:从参数解析到设计实战 电池充电管理是嵌入式系统和便携式设备开发中的基础环节,其核心在于理解并应用充电芯片的规格参数。充电芯片通过精确控制电压、电流、温度等关键参数,实现对锂电池等可充电电池的安全、高效管理。掌握这些参数原理,对于优化产品功耗、提升充电效率、保障电池寿命具有重要技术价值,广泛应用于智能穿戴、消费电子、无人机等场景。本文聚焦于**充电电流**设定与**热管理**等核心参数,深入解析其相互影响与工程选型中的权衡,旨在帮助开发者将抽象的规格书数据转化为可靠的电路设计依据,从而有效排查充电故障,提升产品整体可靠性。 阅读详情

相关推荐

(学习笔记)机器人自主导航从零开始第四步———Rviz、Gazebo、Meshlab的安装

一、Rviz的安装 Rviz是一个

qq_59475883的博客 5050

027-数据分析实战

数据分析实战摘要 本文介绍了数据分析的核心技能,重点涵盖NumPy应用、数据预处理和实战案例。课程目标包括掌握数据清洗、统计分析、可视化技术以及时间序列分析。代码示例展示了如何加载和预处理三类典型数据集:销售数据、股票数据和传感器数据。数据清洗部分演示了缺失值处理(均值/中位数填补)和异常值检测(IQR/Z-score方法)的实际操作。通过NumPy和Matplotlib的组合使用,为后续数据分析奠定了坚实基础,特别适合需要处理金融、销售或物联网数据的从业者学习。

lvjesus的博客 1010

Apollo control模块横向控制原理及核心代码逐行解析

看完这篇文章,你会对apollo横向控制有一个整体的概念

weixin_39199083的博客 2万+

免费开源的100套Echarts大屏数据可视化模板分享

100套大数据可视化炫酷大屏Html5模板;包含行业:社区、物业、政务、交通、金融银行等,全网最新、最多,最全、最酷、最炫大数据可视化模板。陆续更新中 不用再自己花钱买了,git上有开源的100套项目,有预览图和在线预览地址,按需取用。...

coderYYY的博客 4万+

Zemax热分析实战:5分钟搞定双胶合透镜温度漂移优化(附操作数详解)

本文详细解析了使用Zemax进行光学系统热分析的全流程,以双胶合透镜为例,重点阐述了如何通过多重结构编辑器模拟温度变化,并利用关键操作数优化系统以抵抗温度漂移。文章提供了从基础设置到高级优化的实战技巧,帮助工程师快速掌握无热化设计方法,确保系统在宽温范围内保持稳定成像性能。

n4o5p6q7r的博客 385

从零开始:使用VMOS Pro和Xposed绕过APP环境检测的完整指南

本文详细解析了使用VMOS Pro和Xposed框架绕过APP环境检测的完整技术方案,包括模拟器检测、Root环境检测和Hook框架检测的对抗方法。通过实战案例和代码示例,展示了如何利用Xposed Hook技术拦截检测逻辑,特别针对360加固等保护技术提供了解决方案。

ee345的博客 729

分享一个爬虫数据挖掘 农村产权交易数据可视化平台 数据分析大数据 Java、python双版(源码、调试、LW、开题、PPT)

随着我国农村改革的深入推进,农村产权制度改革成为了重要议题。农村土地承包经营权、宅基地使用权等各类产权的流转和交易日益频繁,但传统的管理方式已难以满足当前需求。大量的交易数据分散存储,缺乏统一的管理和分析工具,导致政府部门、金融机构和相关stakeholders难以全面把握农村产权交易的整体情况。同时,数据的不透明也影响了市场的公平性和效率。在此背景下,开发一个农村产权交易数据可视化平台成为了迫切需求,以促进农村产权交易的规范化、透明化和信息化

m0_72599287的博客 2707

STM32F103C8T6全桥逆变电路PWM波生成(基于HAL库)

全桥逆变电路需要两组开关管相互协同工作,故需要两路PWM控制信号,为了防止电源短路,需要保证同一桥臂的上下两个开关管不会同时导通,故两路PWM在相位上需要相差180°;为了保证高频变压器不会磁饱和,使正负半周PWM占空比相同。但是传统的互补输出达不到调节占空比调节输出电压的效果。......

weixin_45042735的博客 6015

000-Python中Matplotlib包教程目录

本教程将全面介绍Python中最重要的数据可视化库Matplotlib的使用方法,从基础概念到高级应用,帮助您掌握专业的数据可视化技能。适合有Python基础的开发者学习。

lvjesus的博客 1071

YOLO26全新改进 | 最全损失函数改进篇 | SIoU 、WIoU、GIoU、DIoU、EIOU、CIoU, InnerIoU、InnerSIoU、InnerWIoU、FocusIoU等损失函数

🔥本文详细介绍了如何修改YOLO26中的损失函数。首先提供了完整的损失函数核心代码,包括WIoU_Scale类和各种IoU计算方法。然后分三步指导修改:1)替换metrics.py中的bbox_iou方法;2)修改loss.py中的iou计算代码;3)更新tal.py中的相关代码。文中特别强调了修改损失函数应在模型结构调整后使用,并提供了多种IoU损失函数的参数配置建议,包括SIoU、WIoU、GIoU等,可根据实际需求灵活选择搭配使用。

Ai缝合怪、助力小伙伴高效发论文 435

hadoop 切片机制详解

hadoop切片机制详解

congge 1万+

乐吾乐大屏——零代码,一键构建,无限可能

零代码实现物联网、工业智能制造等领域的可视化大屏、触摸屏端UI以及工控可视化的解决方案。同时也是一个Web组态工具,支持2D、3D等多种形式,用于构建具有实时数据展示、监控预警、丰富交互的组态画面。拥有丰富的大屏组件和行业模板,能够快速搭建酷炫的2D和3D数据可视化大屏和数字孪生,携手打造数字化时代的前沿大屏应用。

le5le_iot的博客 1001

【Linux】Windows11 安装 WSL2 并运行 Ubuntu 22.04 详细操作步骤

Windows 11 安装 WSL2 和 Ubuntu 22.04 指南 本文详细介绍在 Windows 11 上安装 WSL2 和 Ubuntu 22.04 的完整步骤,包括: WSL2 与 VirtualBox 的性能对比优势 系统版本检查与升级方法 两种安装方式(一键安装和手动安装) Ubuntu 22.04 的安装与配置 存储位置管理及迁移技巧 常用 WSL2 命令速查表 文章还提供了性能优化建议和常见问题解决方案,适合开发者和 Linux 爱好者快速搭建高效的开发环境。

No8g攻城狮的博客 2万+

100套大数据可视化炫酷大屏Html5模板

项目背景:由于自己公司项目里面用到一个数据可视化大屏页面,自己网上各种谷歌百度,发现资源良莠不齐,而且大多数都是收费的,甚至一个页面一收费的那种,前前后后自己不管是付费的还是免费的收集了不少,于是自己打算整理下,免费分享给大家,以免大家再走冤枉路。如果大家有珍藏的好模板,欢迎大家继续补充贡献!!! 版权声明:以下所有模板我均收集于网络,如有涉及版权问题,请及时联系我或在下方评论,我会第一时间删除相关内容 100套大数据可视化炫酷大屏Html5模板;包含行业:社区、物业、政务、交通、工程、医疗、金融银.

m0_54850825的博客 1万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

一文讲透Python机器学习特征选择之互信息法

互信息量度的是特征变量X和响应变量y共享的信息,或者说知道这两个变量其中的一个,对另一个不确定度减少的程度。因此,如果特征变量X和响应变量y相互独立,则知道特征变量X将不对响应变量y提供任何信息,反之亦然,此时特征变量X和响应变量y的互信息为零。而如果特征变量X是响应变量y的确定性函数,响应变量y也是特征变量X的确定性函数,比如y=aX+B,那么此时传递的所有信息都被特征变量X和响应变量y共享,或者说知道特征变量X将可以完全预测响应变量y,则此时特征变量X和响应变量y的互信息为最大。

m0_72274883的博客 4471

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

165本Python电子书大放送,零基础也能轻松入门,自学转行必备!

介绍不同阶段学习Python所需要的书籍,分别涵盖了不同的难度和领域,希望能够帮助零基础的人快速入门!

zhang120529的博客 1683

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【自学转行必备】165本Python电子书大放送,零基础也能轻松入门!

从书籍开始,我将介绍不同阶段学习Python所需要的书籍,分别涵盖了不同的难度和领域,希望能够帮助零基础的人快速入门!

m0_75067840的博客 1232

大数据早报:甲骨文发债融资100亿美元 IBM发布全新私有云平台(11.9)

数据早知道,上36dsj看早报! 来源36大数据,作者:奥兰多 『甲骨文』甲骨文发债融资100亿美元 与苹果并列今年第八大交易 据《金融时报》北京时间11月8日报道,甲骨文公司在周二发行企业债券融资100亿美元,是今年全球最大企业债券发行交易之一。多位知情人士称,甲骨文此次发行的债券分为5个年期:利率为2.637%的12.5亿美元新5.25年期债券、利率为2.975%

36大数据 493

python自动化运维快速入门 pdf 郑征_美团技术专家十年私藏大放送第二弹:带你走上Python大神之路的162本精选电子书......

排雷 数据分析师最容易跳进去的5个大坑!就在身边离你很近! 对数据的解读能力、问正确问题的能力以及解答问题时的灵活性,是衡量一位数据分析师是否称职的关键。 美团的技术专家木小丰 珍藏了十年的技术干货 现在开始大放送第二弹: 带你走上美团的技术专家木小丰,珍藏了十年的技术干货,现在开始大放送第二弹:带你走上Python大神之路的162本精选电子书001 Python 3网络爬虫开发实战 ,崔庆才著 ...

weixin_39622332的博客 3085

计算机专业毕业设计题目推荐(新颖选题)基于ai + SpringBoot相关的毕业设计选题

本文汇总了基于不同技术栈的毕业设计选题,涵盖Django、JSP、.NET、Node.js、PHP、Python和SpringBoot等技术框架。选题涉及教育、医疗、电商、旅游、农业等多个领域,如基于Django的考研学习系统和农业害虫识别系统,基于Node.js的养老院管理系统,基于SpringBoot的高校迎新系统和影院推荐系统等。这些系统设计结合了大数据分析、人工智能、Web开发等技术,展现了计算机技术在解决实际问题中的广泛应用,为计算机相关专业学生提供了丰富的毕业设计选题参考。

全网粉丝10W+、全栈领域优质创作者、掘金、阿里云等社区博客专家、专注于全栈领域和毕业项目实战 1621

算法金 | 时间序列预测真的需要深度学习模型吗?是的,我需要。不,你不需要?...

合集 - 机器学习(41)1.算法金 | 通透!!十大回归算法模型最强总结05-292.算法金 | 吴恩达:机器学习的六个核心算法!05-303.算法金 | 详解过拟合和欠拟合!性感妩媚 VS 大杀四方05-304.算法金 | 突破最强算法模型,决策树算法!!05-315.算法金 | 突破最强算法模型!!学会随机森林,你也能发表高水平SCI06-016.算法金 | 机器学习模型评价、模型与算法选择...

weixin_30284931的博客 645

2024-2025年计算机毕业设计选题推荐 -计算机专业毕业设计题目大全

本文系统梳理了基于不同技术栈的各类信息管理系统设计与实现案例,涵盖Web开发主流框架(Django、SpringBoot、Node.js等)构建的多元化应用场景。具体包括:1)Python/Django框架下的教育管理、电商推荐、数据分析等系统;2)SpringBoot生态中的智能充电、医疗健康、旅游服务等移动应用;3)Node.js技术实现的养老、校园、电商等服务平台;4)PHP/JSP等传统技术开发的律所管理、企业门户等系统。这些案例充分体现了现代信息技术在教育、医疗、商业等领域的深度融合,展示了大数据

G3259093417的博客 621

2025年智能分析Agent白皮书

它基于Agentic AI,构建了“感知 - 推理 - 规划 - 执行 - 进化”闭环,与大模型协同,拓展了数据分析的深度与广度,推动企业决策从数据可视化迈向决策自动化。《智能分析Agent白皮书:智能分析Agent如何驱动企业科学决策》由数势科技发布,深入探讨了智能分析Agent在企业决策中的重要作用、技术架构、产品应用及行业趋势。智能分析Agent正推动企业决策范式变革,未来,企业应积极拥抱这一技术,选择适合的产品和技术路线,提升数据驱动的决策能力,在数字化浪潮中占据优势,实现可持续发展。

202

2025-2026年计算机毕业设计选题推荐:200+个热门精品毕设题目

042-springboot基于spring boot的软件学院学生成绩管理系统的设计与实现。186_springboot基于springboot的一个在线环保意识教育平台的设计与实现。springboot基于Spring Boot的食品安全信息管理系统的设计与实现7icc4。062-springboot基于springboot的青少年心理健康教育网站的设计与实现。069-springboot基于springboot的游戏创意工坊与推广平台的设计与实现。

xn19950718的博客 4148

2026 届大数据专业毕设选题推荐,毕业设计题目汇总

042-springboot基于spring boot的软件学院学生成绩管理系统的设计与实现。186_springboot基于springboot的一个在线环保意识教育平台的设计与实现。springboot基于Spring Boot的食品安全信息管理系统的设计与实现7icc4。062-springboot基于springboot的青少年心理健康教育网站的设计与实现。069-springboot基于springboot的游戏创意工坊与推广平台的设计与实现。

JAVA编码选手的博客 1995

软件工程专业毕业设计选题大全 毕设指导

本文摘要了一系列基于Django、JSPM、Node.js、PHP、Python和SpringBoot框架开发的Web应用系统,涵盖多个领域:1)教育类(考研学习、在线考试、职业推荐);2)电商类(商城、二手交易、购物分析);3)管理系统(学生选课、人事管理、固定资产);4)数据分析(旅游景点、销售数据、气象预测);5)社交平台(音乐分享、动漫社区);6)公共服务(养老、医疗、法律援助)。这些系统充分发挥了各类技术栈的优势,如Django的后台处理能力、SpringBoot的微服务特性,针对特定行业需求提供

全网粉丝10W+、全栈领域优质创作者、掘金、阿里云等社区博客专家、专注于全栈领域和毕业项目实战 1323
上一篇: 026-科学计算图表
下一篇: 028-地理信息可视化
lvjesus
博客等级 码龄10年 1233粉丝 446原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lvjesus

码力充电

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值