""" 缠论可视化模块:使用Plotly生成交互式图表 """ import plotly.graph_objects as go import plotly.express as px from plotly.subplots import make_subplots import pandas as pd import numpy as np from typing import Dict, List, Optional import logging logger = logging.getLogger(__name__) class ChanVisualizer: """缠论可视化器""" def __init__(self): """初始化可视化器""" self.colors = { 'up_candle': '#26a69a', 'down_candle': '#ef5350', 'fractal_top': '#ff6b6b', 'fractal_bottom': '#4ecdc4', 'stroke_up': '#2e86de', 'stroke_down': '#f39c12', 'segment_up': '#0984e3', 'segment_down': '#e17055', 'central_bank': 'rgba(155, 89, 182, 0.3)', 'buy_signal': '#00b894', 'sell_signal': '#d63031' } def create_comprehensive_chart(self, data: Dict) -> go.Figure: """ 创建综合缠论分析图表 Args: data: 包含所有分析数据的字典 Returns: Plotly图表对象 """ if not data or 'klines' not in data: return go.Figure() # 创建子图 fig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, subplot_titles=('缠论分析图', '成交量'), row_heights=[0.8, 0.2] ) # 添加K线图 self._add_candlestick(fig, data['klines']) # 添加分型 if 'fractals' in data and data['fractals']: self._add_fractals(fig, data['fractals']) # 添加笔 if 'strokes' in data and data['strokes']: self._add_strokes(fig, data['strokes']) # 添加线段 if 'segments' in data and data['segments']: self._add_segments(fig, data['segments']) # 添加中枢 if 'central_banks' in data and data['central_banks']: self._add_central_banks(fig, data['central_banks']) # 添加买卖点 if 'trading_points' in data and data['trading_points']: self._add_trading_signals(fig, data['trading_points']) # 添加成交量 self._add_volume(fig, data['klines']) # 更新布局 self._update_layout(fig) return fig def _add_candlestick(self, fig: go.Figure, klines: pd.DataFrame): """添加K线图""" fig.add_trace( go.Candlestick( x=klines.index, open=klines['open'], high=klines['high'], low=klines['low'], close=klines['close'], name='K线', increasing=dict(line=dict(color=self.colors['up_candle'])), decreasing=dict(line=dict(color=self.colors['down_candle'])) ), row=1, col=1 ) def _add_fractals(self, fig: go.Figure, fractals: List): """添加分型标记""" top_fractals = [f for f in fractals if f.fractal_type == 'top'] bottom_fractals = [f for f in fractals if f.fractal_type == 'bottom'] if top_fractals: fig.add_trace( go.Scatter( x=[f.timestamp for f in top_fractals], y=[f.price for f in top_fractals], mode='markers', marker=dict( symbol='triangle-down', size=8, color=self.colors['fractal_top'] ), name='顶分型', hovertemplate='顶分型
时间: %{x}
价格: %{y}
强度: %{customdata}', customdata=[f.strength for f in top_fractals] ), row=1, col=1 ) if bottom_fractals: fig.add_trace( go.Scatter( x=[f.timestamp for f in bottom_fractals], y=[f.price for f in bottom_fractals], mode='markers', marker=dict( symbol='triangle-up', size=8, color=self.colors['fractal_bottom'] ), name='底分型', hovertemplate='底分型
时间: %{x}
价格: %{y}
强度: %{customdata}', customdata=[f.strength for f in bottom_fractals] ), row=1, col=1 ) def _add_strokes(self, fig: go.Figure, strokes: List): """添加笔""" for stroke in strokes: color = self.colors['stroke_up'] if stroke.direction == 1 else self.colors['stroke_down'] fig.add_trace( go.Scatter( x=[stroke.start_fractal.timestamp, stroke.end_fractal.timestamp], y=[stroke.start_fractal.price, stroke.end_fractal.price], mode='lines', line=dict(color=color, width=2), name='笔' if stroke == strokes[0] else '', showlegend=stroke == strokes[0], hovertemplate=f'笔
方向: {"上升" if stroke.direction == 1 else "下降"}
长度: {stroke.length:.2f}
强度: {stroke.strength:.2f}' ), row=1, col=1 ) def _add_segments(self, fig: go.Figure, segments: List): """添加线段""" for segment in segments: color = self.colors['segment_up'] if segment.direction == 1 else self.colors['segment_down'] fig.add_trace( go.Scatter( x=[segment.start_time, segment.end_time], y=[segment.start_price, segment.end_price], mode='lines', line=dict(color=color, width=4, dash='dash'), name='线段' if segment == segments[0] else '', showlegend=segment == segments[0], hovertemplate=f'线段
方向: {"上升" if segment.direction == 1 else "下降"}
长度: {segment.length:.2f}
强度: {segment.strength:.2f}' ), row=1, col=1 ) def _add_central_banks(self, fig: go.Figure, central_banks: List): """添加中枢""" for cb in central_banks: # 添加中枢矩形区域 fig.add_shape( type="rect", x0=cb.start_time, y0=cb.low_price, x1=cb.end_time, y1=cb.high_price, fillcolor=self.colors['central_bank'], opacity=0.3, line=dict(color="rgba(155, 89, 182, 0.8)", width=1), row=1, col=1 ) # 添加中枢标签 fig.add_annotation( x=cb.start_time + (cb.end_time - cb.start_time) / 2, y=cb.center_price, text=f"中枢({cb.level})", showarrow=False, font=dict(size=10, color="purple"), bgcolor="rgba(255,255,255,0.8)", row=1, col=1 ) def _add_trading_signals(self, fig: go.Figure, trading_points: List): """添加买卖点信号""" buy_points = [p for p in trading_points if p.signal_type == 'buy'] sell_points = [p for p in trading_points if p.signal_type == 'sell'] if buy_points: fig.add_trace( go.Scatter( x=[p.timestamp for p in buy_points], y=[p.price for p in buy_points], mode='markers', marker=dict( symbol='triangle-up', size=12, color=self.colors['buy_signal'] ), name='买点', hovertemplate='%{customdata}
时间: %{x}
价格: %{y}
强度: %{text}', customdata=[p.description for p in buy_points], text=[f"{p.strength:.3f}" for p in buy_points] ), row=1, col=1 ) if sell_points: fig.add_trace( go.Scatter( x=[p.timestamp for p in sell_points], y=[p.price for p in sell_points], mode='markers', marker=dict( symbol='triangle-down', size=12, color=self.colors['sell_signal'] ), name='卖点', hovertemplate='%{customdata}
时间: %{x}
价格: %{y}
强度: %{text}', customdata=[p.description for p in sell_points], text=[f"{p.strength:.3f}" for p in sell_points] ), row=1, col=1 ) def _add_volume(self, fig: go.Figure, klines: pd.DataFrame): """添加成交量""" colors = [self.colors['up_candle'] if close >= open_ else self.colors['down_candle'] for close, open_ in zip(klines['close'], klines['open'])] fig.add_trace( go.Bar( x=klines.index, y=klines['volume'], name='成交量', marker_color=colors, showlegend=False ), row=2, col=1 ) def _update_layout(self, fig: go.Figure): """更新图表布局""" fig.update_layout( title=dict( text="缠论分析图表", x=0.5, font=dict(size=20) ), xaxis_rangeslider_visible=False, height=800, showlegend=True, legend=dict( orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1 ), margin=dict(l=50, r=50, t=100, b=50), plot_bgcolor='white', paper_bgcolor='white' ) # 更新X轴 fig.update_xaxes( title_text="时间", showgrid=True, gridwidth=1, gridcolor='lightgray' ) # 更新Y轴 fig.update_yaxes( title_text="价格", showgrid=True, gridwidth=1, gridcolor='lightgray', row=1, col=1 ) fig.update_yaxes( title_text="成交量", row=2, col=1 ) def create_statistics_charts(self, data: Dict) -> List[go.Figure]: """ 创建统计分析图表 Args: data: 分析数据 Returns: 统计图表列表 """ charts = [] # 分型强度分布 if 'fractals' in data and data['fractals']: fractal_chart = self._create_fractal_strength_chart(data['fractals']) charts.append(fractal_chart) # 买卖点类别分布 if 'trading_points' in data and data['trading_points']: signal_chart = self._create_signal_distribution_chart(data['trading_points']) charts.append(signal_chart) # 中枢级别分布 if 'central_banks' in data and data['central_banks']: cb_chart = self._create_central_bank_chart(data['central_banks']) charts.append(cb_chart) return charts def _create_fractal_strength_chart(self, fractals: List) -> go.Figure: """创建分型强度分布图""" strengths = [f.strength for f in fractals] types = [f.fractal_type for f in fractals] df = pd.DataFrame({'strength': strengths, 'type': types}) fig = px.histogram( df, x='strength', color='type', title='分型强度分布', labels={'strength': '强度', 'type': '类型'}, color_discrete_map={'top': self.colors['fractal_top'], 'bottom': self.colors['fractal_bottom']} ) return fig def _create_signal_distribution_chart(self, trading_points: List) -> go.Figure: """创建买卖点分布图""" classes = [f"{p.point_class}类{p.signal_type}点" for p in trading_points] fig = px.pie( values=[classes.count(c) for c in set(classes)], names=list(set(classes)), title='买卖点类别分布' ) return fig def _create_central_bank_chart(self, central_banks: List) -> go.Figure: """创建中枢分析图""" levels = [cb.level for cb in central_banks] strengths = [cb.strength for cb in central_banks] fig = go.Figure() for level in set(levels): level_strengths = [s for l, s in zip(levels, strengths) if l == level] fig.add_trace(go.Box( y=level_strengths, name=level, boxpoints='all' )) fig.update_layout( title='中枢强度分布(按级别)', xaxis_title='中枢级别', yaxis_title='强度' ) return fig def create_market_structure_chart(self, market_structure: Dict) -> go.Figure: """ 创建市场结构图 Args: market_structure: 市场结构数据 Returns: 市场结构图表 """ fig = go.Figure() # 当前价格线 if 'current_price' in market_structure: fig.add_hline( y=market_structure['current_price'], line_dash="dash", line_color="black", annotation_text=f"当前价格: {market_structure['current_price']:.2f}" ) # 支撑阻力位 if 'support_resistance' in market_structure: sr = market_structure['support_resistance'] # 支撑位 if 'support_levels' in sr: for i, support in enumerate(sr['support_levels']): fig.add_hline( y=support, line_dash="dot", line_color=self.colors['buy_signal'], annotation_text=f"支撑{i+1}: {support:.2f}" ) # 阻力位 if 'resistance_levels' in sr: for i, resistance in enumerate(sr['resistance_levels']): fig.add_hline( y=resistance, line_dash="dot", line_color=self.colors['sell_signal'], annotation_text=f"阻力{i+1}: {resistance:.2f}" ) fig.update_layout( title="市场结构分析", yaxis_title="价格", height=400 ) return fig