TOP

市场宽度实时定时版

from jqdata import *
import datetime as dt
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import json, textwrap

# 获取股票和价格数据
stocks = get_index_stocks("000985.XSHG")
now = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
yesterday = (dt.datetime.now() - dt.timedelta(days=1)).strftime("%Y-%m-%d")

count = 90  #1年的交易日
price_hist = get_price(stocks,end_date=yesterday,frequency="1d",fields=["close"],count=count + 19,panel=False)


# 获取行业信息
def getStockIndustry(stocks):
    industry = get_industry(stocks)
    dict = {
        stock: info["sw_l1"]["industry_name"]
        for stock, info in industry.items()
        if "sw_l1" in info
    }
    industry_2_stock = {}
    for stock, info in industry.items():
        if "sw_l1" in info:
            industry_name = info["sw_l1"]["industry_name"]
            ls = industry_2_stock.get(industry_name, [])
            ls.append(stock)
            industry_2_stock[industry_name] = ls
    return pd.Series(dict), industry_2_stock


def update_price():
    cur_price = get_price(stocks,end_date=now,frequency="1m",fields=["close"],count=1,panel=False)
    cur_price['time'] = pd.to_datetime(cur_price['time']).dt.floor('d')
    new_price = pd.concat([price_hist, cur_price], ignore_index=True)
    new_price= new_price.sort_values(['code', 'time']).reset_index(drop=True)
    return new_price


def update_df():
    # 处理数据
    h = update_price()
    h["date"] = pd.DatetimeIndex(h.time).date
    df_close = h.pivot(index="code", columns="date", values="close").dropna(axis=0)
    df_ma20 = df_close.rolling(window=20, axis=1).mean().iloc[:, -count:]
    df_bias = df_close.iloc[:, -count:] > df_ma20
    df_bias["industry_name"], industry_2_stock = getStockIndustry(stocks)
    df_ratio = (
        (df_bias.groupby("industry_name").sum() * 100.0)
        / df_bias.groupby("industry_name").count()
    ).round()
    df_ratio.loc['合计'] = df_ratio.sum().astype("int32")
    DF = df_ratio.T
    return DF


#======================================================================

def update_html(DF):
    industry_cols = DF.columns[:-1]
    df = DF.iloc[:, :-1].copy()  # 创建数据框副本,避免修改原始数据
    df.insert(len(industry_cols), '市场平均', df[industry_cols].mean(axis=1).round().astype(int))
    df.index = pd.to_datetime(df.index)
    use_cols = [c for c in df.columns if c not in {'合计'}]

    # 1. 只保留 [x, y, value] 三元组给 ECharts
    data_js = [
        [j, i, int(df.iloc[i, j])]
        for i in range(len(df))
        for j in range(len(df.columns))
    ]
#     print("data_js:", data_js)

    # 2. 行业名、日期单独传两份数组,前端用索引去取
    cols   = df.columns.tolist()
#     print("cols:", cols)
    dates  = [str(d) for d in df.index.date]
#     print("dates:", dates)

    cell_w  = 40          # 每列宽 80 px
    cell_h  = 40          # 每行高 26 px(足够看清文字)
    width   = len(use_cols) * cell_w    # 留边给 visualMap
    height  = len(df)        * cell_h    # 随行情自动增高
    # 写进单文件 HTML

    html = textwrap.dedent(f'''\
    <!doctype html>
    <html>
    <head>
        <meta charset="utf-8"><title>行业热力图(可点击)</title>
        <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
        <style>
            #wrapper{{width:100%;max-height:70vh;overflow:auto;border:1px solid #ddd}}
            #chart{{width:{width}px;height:{height}px;margin:auto}}
        </style>
    </head>
    <body>
    <div id="chart" style="width:{{width}}px;height:{{height}}px;margin:auto"></div>
    <script>
    const raw      = {json.dumps(data_js)};
    const cols     = {json.dumps(cols)};
    const dates    = {json.dumps(dates)};
    const myChart = echarts.init(document.getElementById('chart'));

    const option = {{
        tooltip: {{
            position: 'top',
            formatter: p => {{
                const [x, y, val] = p.data;
                return `${{dates[y]}}<br/>${{cols[x]}}: <b>${{val}}</b>`;
            }}
        }},
        animation: true,
        grid: {{left: '10%', right: '5%', bottom: '5%', top: '2%'}},
        xAxis: {{
            type: 'category',
            data: cols,
            position: 'top', 
            axisLabel: {{interval: 0,rotate: 45,fontSize: 11}},
            splitArea: {{show: true}}
        }},
        yAxis: {{
            type: 'category',
            data: dates,
            splitArea: {{show: true}}
        }},
        visualMap: {{
            min: 0, max: 100, calculable: true, type: 'continuous',
            orient: 'horizontal', left: 'center', bottom: '2%',
            inRange: {{color: [
                '#005824','#238b45','#5cb85c','#90ee90','#ffff99',
                '#ffaa99','#ff7d66','#f54437','#b00000'
            ]}}
        }},
        series: [{{
            name: '热力图',
            type: 'heatmap',
            data: raw,
            label: {{show: true, fontSize: 11, color: '#000'}},
            itemStyle: {{borderWidth: 1, borderColor: '#fff'}},
            emphasis: {{itemStyle: {{shadowBlur: 12, shadowColor: 'rgba(0,0,0,.4'}}}}
        }}]
    }};
    myChart.setOption(option);

    // 点击事件
    myChart.on('click', params => {{
        const [,,val,ind,dt] = params.data;
        alert(`${{dt}}\\n${{ind}}: ${{val}}`);
    }});
    </script>
    </body>
    </html>''')

    open('行业宽度-定时刷新版.html','w',encoding='utf-8').write(html)
    print("可以打开-行业宽度-定时刷新版.html")

# 定时刷新 10分钟一次
while True:
    DF = update_df()
    update_html(DF)
    time.sleep(60 * 10) 

    

 

posted @ 2025-10-09 14:21  羊驼之歌  阅读(10)  评论(0)    收藏  举报