kline_app_matplotlib.py


import dearpygui.dearpygui as dpg
import numpy as np
from datetime import datetime, timedelta
import random
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle


# 全局变量
kline_data = None
current_timeframe = "1D"  # 默认时间周期
view_mode = "K线图"  # 默认视图模式


def generate_random_kline_data(num_points=200, timeframe="1D"):
    """生成随机K线数据"""
    global kline_data
    now = datetime.now()
    data = []
    
    # 根据时间周期计算时间间隔
    if timeframe == "1D":
        delta = timedelta(days=1)
    elif timeframe == "4H":
        delta = timedelta(hours=4)
    elif timeframe == "1H":
        delta = timedelta(hours=1)
    elif timeframe == "15M":
        delta = timedelta(minutes=15)
    elif timeframe == "5M":
        delta = timedelta(minutes=5)
    elif timeframe == "1M":
        delta = timedelta(minutes=1)
    else:
        delta = timedelta(days=1)
    
    # 初始价格
    open_price = 100.0
    for i in range(num_points):
        timestamp = now - delta * (num_points - i)
        
        # 随机价格波动
        volatility = 0.02
        high = open_price * (1 + random.uniform(0, volatility))
        low = open_price * (1 - random.uniform(0, volatility))
        close = open_price + random.uniform(-volatility * open_price, volatility * open_price)
        
        # 随机成交量
        volume = random.randint(1000, 10000)
        
        data.append({
            "timestamp": timestamp,
            "open": open_price,
            "high": high,
            "low": low,
            "close": close,
            "volume": volume
        })
        
        open_price = close
    
    kline_data = data
    return data


def draw_kline_chart():
    """使用matplotlib绘制K线图"""
    if kline_data is None:
        return None
    
    # 创建图形
    fig, ax = plt.subplots(figsize=(10, 6), facecolor='#1a1a1a')
    ax.set_facecolor('#1a1a1a')
    
    # 绘制K线
    for i, data in enumerate(kline_data):
        timestamp = i
        open_p = data["open"]
        high_p = data["high"]
        low_p = data["low"]
        close_p = data["close"]
        
        # 确定K线颜色(涨/跌)
        color = '#00ff00' if close_p >= open_p else '#ff0000'
        
        # 绘制K线实体
        rect = Rectangle((timestamp - 0.3, min(open_p, close_p)),
                        0.6, max(open_p, close_p) - min(open_p, close_p),
                        facecolor=color, edgecolor=color)
        ax.add_patch(rect)
        
        # 绘制影线
        plt.plot([timestamp, timestamp], [high_p, low_p], color=color, linewidth=1.5)
    
    # 设置坐标轴
    ax.set_xlim(-1, len(kline_data))
    ax.set_ylim(min(d["low"] for d in kline_data) * 0.98, max(d["high"] for d in kline_data) * 1.02)
    
    # 设置X轴标签
    ax.set_xlabel("时间", color='#ffffff')
    ax.set_ylabel("价格", color='#ffffff')
    ax.set_title(f"{current_timeframe} K线图", color='#ffffff')
    
    # 配置坐标轴刻度
    ax.tick_params(axis='x', colors='#ffffff')
    ax.tick_params(axis='y', colors='#ffffff')
    
    # 配置网格
    ax.grid(True, color='#444444', linestyle='--', alpha=0.3)
    
    # 移除顶部和右侧边框
    for spine in ax.spines.values():
        spine.set_edgecolor('#444444')
    
    # 保存为PNG图像
    plt.tight_layout()
    plt.savefig("kline_chart.png", facecolor='#1a1a1a', transparent=True)
    plt.close()
    
    return "kline_chart.png"


def draw_time_line_chart():
    """使用matplotlib绘制分时图"""
    if kline_data is None:
        return None
    
    # 创建图形
    fig, ax = plt.subplots(figsize=(10, 6), facecolor='#1a1a1a')
    ax.set_facecolor('#1a1a1a')
    
    # 绘制价格走势
    times = list(range(len(kline_data)))
    closes = [d["close"] for d in kline_data]
    
    ax.plot(times, closes, color='#0099ff', linewidth=2)
    
    # 填充区域
    ax.fill_between(times, closes, color='#0099ff', alpha=0.2)
    
    # 设置坐标轴
    ax.set_xlim(0, len(kline_data) - 1)
    ax.set_ylim(min(closes) * 0.98, max(closes) * 1.02)
    
    # 设置X轴标签
    ax.set_xlabel("时间", color='#ffffff')
    ax.set_ylabel("价格", color='#ffffff')
    ax.set_title(f"{current_timeframe} 分时图", color='#ffffff')
    
    # 配置坐标轴刻度
    ax.tick_params(axis='x', colors='#ffffff')
    ax.tick_params(axis='y', colors='#ffffff')
    
    # 配置网格
    ax.grid(True, color='#444444', linestyle='--', alpha=0.3)
    
    # 移除顶部和右侧边框
    for spine in ax.spines.values():
        spine.set_edgecolor('#444444')
    
    # 保存为PNG图像
    plt.tight_layout()
    plt.savefig("time_line_chart.png", facecolor='#1a1a1a', transparent=True)
    plt.close()
    
    return "time_line_chart.png"


def update_kline_display():
    """更新K线图显示"""
    if kline_data is None:
        return
    
    # 绘制K线图
    chart_path = draw_kline_chart()
    
    # 显示图像
    if chart_path:
        # 加载新图像
        width, height, channels, data = dpg.load_image(chart_path)
        
        # 更新现有的纹理
        dpg.configure_item("kline_image", width=width, height=height, default_value=data)


def update_time_line_chart():
    """更新分时图显示"""
    if kline_data is None:
        return
    
    # 绘制分时图
    chart_path = draw_time_line_chart()
    
    # 显示图像
    if chart_path:
        # 加载新图像
        width, height, channels, data = dpg.load_image(chart_path)
        
        # 更新现有的纹理
        dpg.configure_item("time_line_image", width=width, height=height, default_value=data)


def regenerate_data():
    """重新生成随机数据"""
    generate_random_kline_data(timeframe=current_timeframe)
    if view_mode == "K线图":
        update_kline_display()
    else:
        update_time_line_chart()


def change_timeframe(sender, app_data):
    """改变时间周期"""
    global current_timeframe
    current_timeframe = app_data
    regenerate_data()


def change_view_mode(sender, app_data):
    """改变视图模式"""
    global view_mode
    view_mode = app_data
    if view_mode == "K线图":
        update_kline_display()
        dpg.show_item("kline_display")
        dpg.hide_item("time_line_display")
    else:
        update_time_line_chart()
        dpg.hide_item("kline_display")
        dpg.show_item("time_line_display")


def create_main_window():
    """创建主窗口"""
    # 加载图像纹理
    with dpg.texture_registry():
        dpg.add_static_texture(1, 1, [0, 0, 0], tag="kline_image")
        dpg.add_static_texture(1, 1, [0, 0, 0], tag="time_line_image")
    
    with dpg.window(label="K线图分析系统", tag="main_window", width=1200, height=800):
        
        # 顶部工具栏
        with dpg.group(horizontal=True):
            dpg.add_text("时间周期:")
            dpg.add_radio_button(
                ["1M", "5M", "15M", "1H", "4H", "1D"],
                label="时间周期",
                default_value="1D",
                callback=change_timeframe,
                tag="timeframe_radio"
            )
            
            dpg.add_spacer(width=20)
            
            dpg.add_text("视图模式:")
            dpg.add_radio_button(
                ["K线图", "分时图"],
                label="视图模式",
                default_value="K线图",
                callback=change_view_mode,
                tag="view_mode_radio"
            )
            
            dpg.add_spacer(width=20)
            
            dpg.add_button(
                label="重新生成数据",
                callback=regenerate_data,
                tag="regenerate_btn",
                width=150
            )
        
        dpg.add_separator()
        
        # 主图区域
        with dpg.child_window(height=-1, width=-1, border=True):
            # K线图显示区域
            with dpg.group(tag="kline_display"):
                dpg.add_image("kline_image", tag="kline_display_image", width=1100, height=600)
            
            # 分时图显示区域(初始隐藏)
            with dpg.group(tag="time_line_display"):
                dpg.add_image("time_line_image", tag="time_line_display_image", width=1100, height=600)
            dpg.hide_item("time_line_display")


def create_sidebar():
    """创建侧边栏"""
    with dpg.window(label="指标", tag="indicator_window", width=250, height=800, pos=(1200, 0)):
        
        dpg.add_text("技术指标")
        dpg.add_separator()
        
        with dpg.collapsing_header(label="MA均线"):
            dpg.add_checkbox(label="MA5", default_value=True)
            dpg.add_checkbox(label="MA10", default_value=True)
            dpg.add_checkbox(label="MA20", default_value=True)
            dpg.add_checkbox(label="MA60", default_value=True)
        
        with dpg.collapsing_header(label="MACD"):
            dpg.add_checkbox(label="MACD", default_value=True)
        
        with dpg.collapsing_header(label="KDJ"):
            dpg.add_checkbox(label="KDJ", default_value=True)
        
        with dpg.collapsing_header(label="RSI"):
            dpg.add_checkbox(label="RSI", default_value=True)
        
        dpg.add_separator()
        
        dpg.add_text("数据统计")
        if kline_data:
            dpg.add_text(f"最高价格: {max(d['high'] for d in kline_data):.2f}")
            dpg.add_text(f"最低价格: {min(d['low'] for d in kline_data):.2f}")
            dpg.add_text(f"平均价格: {sum(d['close'] for d in kline_data)/len(kline_data):.2f}")
            dpg.add_text(f"总成交量: {sum(d['volume'] for d in kline_data):,}")


def main():
    """主函数"""
    try:
        # 初始化Dear PyGui
        dpg.create_context()
        
        # 配置主题
        with dpg.theme(tag="theme"):
            with dpg.theme_component(dpg.mvAll):
                dpg.add_theme_color(dpg.mvThemeCol_WindowBg, (30, 30, 30, 255))
                dpg.add_theme_color(dpg.mvThemeCol_ChildBg, (40, 40, 40, 255))
                dpg.add_theme_color(dpg.mvThemeCol_Text, (255, 255, 255, 255))
                dpg.add_theme_style(dpg.mvStyleVar_WindowPadding, 10, 10)
                dpg.add_theme_style(dpg.mvStyleVar_FramePadding, 5, 5)
        
        dpg.bind_theme("theme")
        
        # 创建主窗口和侧边栏
        create_main_window()
        create_sidebar()
        
        # 生成初始数据
        generate_random_kline_data()
        
        # 更新图表显示
        update_kline_display()
        
        # 创建视图
        dpg.create_viewport(title="K线图分析系统", width=1450, height=800)
        dpg.setup_dearpygui()
        
        # 显示窗口
        dpg.show_viewport()
        dpg.start_dearpygui()
        dpg.destroy_context()
    except Exception as e:
        print(f"程序启动错误: {e}")
        import traceback
        print("完整错误信息:")
        print(traceback.format_exc())


if __name__ == "__main__":
    main()