菲林霖
  • 摄影百科
    • 数码相机
    • 胶片/胶卷
      • 110胶卷
      • 120胶卷
      • 35胶卷
    • CCD相机
    • M43
    • 镜头
    • 相机配件
  • 胶片相机
    • 135相机
    • 110相机
    • 120相机
  • 无反相机
  • 数码相机
    • VHS&DV
    • 单反相机
    • CCD相机
  • 手机摄影
  • 作品展示
    • 视频作品
    • 图片作品
  • 摄影软件
    • 插件
  • 专栏
    • Chat.
    • 问答
  • 福利信息
  • 摄影相关
  • 云笔记
  • 摄影工具
    • 摄影参考

【个人笔记】基金量化面板 模块化核心代码拆分

基金量化面板 模块化核心代码拆分(独立可复制复用,脱离本次对话直接新建 HTML 可用)

前置统一说明

依赖唯一外部 CDN:ECharts 图表库,所有图表模块必须引入:

1
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>

数据源 API:东方财富基金净值历史接口,永久可用格式

1
`https://fund.eastmoney.com/pingzhongdata/${基金代码}.js?t=${时间戳}`

量化统一规则(所有信号模块共用判断逻辑)

加仓:2 年估值分位 ≤30% 且 当前净值 ≤ 布林下轨

减仓出售:2 年估值分位 ≥70% 且 当前净值 ≥ 布林上轨

持有:其余区间 无持仓、无交易台账,仅净值 + 布林分位量化

模块 1:全局基础基金配置数据(独立复制,修改基金仅改这里)

JS 基金基础信息(FUND_LIST)

1
const FUND_LIST = { "011609":{ name:"易方达上证科创50ETF联接C", alloc:{stock:"0.30%",bond:"0.00%",cash:"5.22%",asset:"98.62亿"} }, "024481":{ name:"财通品质甄选混合C", alloc:{stock:"68.22%",bond:"2.15%",cash:"8.76%",asset:"2.73亿"} }, "016873":{ name:"广发远见智选混合A", alloc:{stock:"89.35%",bond:"0.12%",cash:"7.64%",asset:"13.31亿"} }, "007339":{ name:"易方达沪深300ETF联接C", alloc:{stock:"0.45%",bond:"0.00%",cash:"4.16%",asset:"72.14亿"} }, "018124":{ name:"永赢先进制造智选混合发起A", alloc:{stock:"88.64%",bond:"0.08%",cash:"8.37%",asset:"30.82亿"} } }; // 快速提取所有基金代码数组 const FUND_CODES = Object.keys(FUND_LIST); // 全局缓存容器:存放每只基金历史净值、量化指标 let fundCache = {};

模块 2:通用数学工具函数(量化计算核心,不可缺失)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 平均值
function mean(arr){return arr.reduce((s,v)=>s+v,0)/arr.length;}
// 中位数
function median(arr){
    const sort = [...arr].sort((a,b)=>a-b);
    const m = Math.floor(sort.length/2);
    return sort.length%2 ? sort[m] : (sort[m-1]+sort[m])/2;
}
// 标准差
function stdDev(arr,avg){
    const m = avg ?? mean(arr);
    let sum = 0;
    arr.forEach(v=>sum += Math.pow(v-m,2));
    return Math.sqrt(sum/arr.length);
}
// 计算数值在数组内的历史分位百分比
function getPercent(val,arr){
    let cnt = 0;
    arr.forEach(v=>{if(v<val)cnt++;});
    return (cnt/arr.length)*100;
}
// 分位数计算(30%、70%阈值使用)
function quantile(arr,q){
    const s = [...arr].sort((a,b)=>a-b);
    const pos = (s.length-1)*q;
    const f = Math.floor(pos);
    const c = Math.ceil(pos);
    if(f===c) return s[pos];
    const w = pos-f;
    return s[f]*(1-w)+s[c]*w;
}
// 时间戳转YYYY-MM-DD日期
function formatDate(ts){
    const d = new Date(ts);
    const pad = n=>String(n).padStart(2,"0");
    return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
}
// 估值星级★转换
function starStr(num){return "★".repeat(num)+"☆".repeat(5-num);}
// 更新页面刷新时间
function updateTime(){
    const d = new Date();
    const pad = n=>String(n).padStart(2,"0");
    document.getElementById("updateTime").innerText = `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getMinutes())}:${pad(d.getSeconds())}:${pad(d.getSeconds())}`;
}

模块 3:核心量化计算函数(布林带 + 2 年历史分位,信号生成源头)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function calcQuant(history){
    // 取近两年净值数据
    const twoYear = history.slice(-500);
    const vals = twoYear.map(d=>d.value);
    const latest = history[history.length-1].value;
    // 60日均线数据
    const last60 = history.slice(-60).map(d=>d.value);
    const ma60 = mean(last60);
 
    const avg2 = mean(vals);
    const med2 = median(vals);
    const std = stdDev(vals,avg2);
    // 布林轨道 1.2倍标准差
    const bollDown = avg2 - 1.2 * std;
    const bollUp = avg2 + 1.2 * std;
    // 当前净值两年历史分位
    const pct = getPercent(latest,vals);
    const minV = Math.min(...vals);
    const maxV = Math.max(...vals);
 
    // 估值星级
    let star = 3;
    if(pct<=20)star=1;
    else if(pct<=30)star=2;
    else if(pct>=70)star=4;
    else if(pct>=80)star=5;
 
    return {
        avg2y:avg2, med2y:med2,
        bollDown:bollDown, bollUp:bollUp,
        min:minV, max:maxV,
        pct:pct, ma60:ma60, latest:latest, star:star, std:std
    };
}

模块 4:API 净值拉取函数(2 套:前台渲染加载 / 后台静默预加载)

4.1 前台加载单基金(切换标签、刷新当前基金使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function loadNavData(code){
    const tabEl = document.querySelector(`.fund-tab[data-code="${code}"]`);
    tabEl.classList.add("loading");
    const script = document.createElement("script");
    script.src = `https://fund.eastmoney.com/pingzhongdata/${code}.js?t=${Date.now()}`;
    script.onload = function(){
        const rawData = window.Data_netWorthTrend;
        window.Data_netWorthTrend = null;
        if(!rawData || rawData.length === 0){
            showError("净值接口无数据");
            tabEl.classList.remove("loading");
            return;
        }
        const history = [];
        rawData.forEach(item=>{
            if(item && typeof item.y === "number" && item.y > 0){
                history.push({date:formatDate(item.x),value:item.y,change:item.equityReturn||0});
            }
        });
        if(history.length < 150){
            showError("净值历史不足150条,无法量化");
            tabEl.classList.remove("loading");
            return;
        }
        fundCache[code] = {history:history,quant:calcQuant(history),loaded:true};
        renderAll(code);
        renderSignalBoard();
        tabEl.classList.remove("loading");
        updateTime();
    };
    script.onerror = function(){
        showError("净值API请求失败");
        tabEl.classList.remove("loading");
    };
    document.body.appendChild(script);
}
// 快捷刷新当前选中基金
function loadCurrentFund(){loadNavData(currentFundCode);}

4.2 静默后台预加载(一键刷新全部基金看板使用,无页面渲染)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function loadBgData(code,cb){
    const script = document.createElement("script");
    script.src = `https://fund.eastmoney.com/pingzhongdata/${code}.js?t=${Date.now()}`;
    script.onload = function(){
        const raw = window.Data_netWorthTrend;
        window.Data_netWorthTrend = null;
        if(!raw || raw.length === 0){cb();return;}
        const history = [];
        raw.forEach(item=>{
            if(item && typeof item.y === "number" && item.y > 0){
                history.push({date:formatDate(item.x),value:item.y,change:item.equityReturn||0});
            }
        });
        if(history.length >= 150){
            fundCache[code] = {history:history,quant:calcQuant(history),loaded:true};
        }
        cb();
    };
    script.onerror = ()=>cb();
    document.body.appendChild(script);
}
// 一键刷新全部5只基金,更新顶部总览看板
function refreshAllBoard(){
    let loadIndex = 0;
    function loadNext(){
        if(loadIndex >= FUND_CODES.length){
            renderSignalBoard();
            updateTime();
            return;
        }
        const code = FUND_CODES[loadIndex];
        loadBgData(code,()=>{
            loadIndex++;
            setTimeout(loadNext,600);
        })
    }
    loadNext();
}

模块 5:页面渲染函数(分 5 个独立渲染单元,按需复制)

5.1 顶部 5 基金总览看板渲染(核心直观加仓 / 出售板块)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function renderSignalBoard(){
    const boardWrap = document.getElementById("signalBoard");
    boardWrap.innerHTML = "";
    FUND_CODES.forEach(code=>{
        const fundInfo = FUND_LIST[code];
        const cache = fundCache[code];
        let cardClass, signalText, signalColor, pctText;
        if(!cache || !cache.loaded){
            cardClass = "board-card board-loading";
            signalText = "数据加载中";
            pctText = "-";
        }else{
            const q = cache.quant;
            pctText = `估值分位:${q.pct.toFixed(1)}%`;
            if(q.pct <= 30 && q.latest <= q.bollDown){
                cardClass = "board-card board-buy";
                signalText = "✅ 适合加仓";
                signalColor = "#2e7d32";
            }else if(q.pct >= 70 && q.latest >= q.bollUp){
                cardClass = "board-card board-sell";
                signalText = "⚠️ 适合减仓出售";
                signalColor = "#e53935";
            }else{
                cardClass = "board-card board-watch";
                signalText = "⏸️ 持有观望";
                signalColor = "#ff8800";
            }
        }
        const div = document.createElement("div");
        div.className = cardClass;
        div.innerHTML = `
            <div class="board-code">${code}</div>
            <div class="board-name">${fundInfo.name}</div>
            <div class="board-signal" style="color:${signalColor}">${signalText}</div>
            <div class="board-pct">${pctText}</div>
        `;
        div.onclick = ()=>switchFund(code);
        boardWrap.appendChild(div);
    })
}

5.2 单基金切换逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let currentFundCode = "011609";
function switchFund(code){
    currentFundCode = code;
    document.querySelectorAll(".fund-tab").forEach(t=>t.classList.remove("active"));
    document.querySelector(`.fund-tab[data-code="${code}"]`).classList.add("active");
    document.getElementById("fundNameTitle").innerText = `${code} ${FUND_LIST[code].name}`;
    document.getElementById("navContent").innerHTML = '<div class="loading">切换基金,加载净值API</div>';
    document.getElementById("signalContent").innerHTML = '<div class="loading">量化计算中</div>';
    document.getElementById("allocWrap").innerHTML = '<div class="loading">资产配置</div>';
    if(fundCache[code] && fundCache[code].loaded){
        renderAll(code);
    }else{
        loadNavData(code);
    }
}
// 统一渲染当前基金全部面板
function renderAll(code){
    const cache = fundCache[code];
    const h = cache.history;
    const q = cache.quant;
    renderNav(h,q);
    renderSignal(q,h);
    renderAlloc(code);
    renderChart(h,q);
}

5.3 单位净值面板渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function renderNav(history,q){
    const last = history[history.length-1];
    const prev = history.length>=2 ? history[history.length-2].value : last.value;
    const dayRate = ((last.value-prev)/prev)*100;
    const cls = dayRate>=0 ? "up" : "down";
    const sign = dayRate>=0 ? "+" : "";
    const star = starStr(q.star);
    document.getElementById("navContent").innerHTML = `
        <div class="star-rating">${star}</div>
        <div class="nav-value">${last.value.toFixed(4)}</div>
        <div class="nav-date">净值日期:${last.date} <span class="badge">API自动获取</span></div>
        <div class="nav-change ${cls}">当日涨跌幅 ${sign}${dayRate.toFixed(2)}%</div>
        <div style="margin-top:8px;font-size:13px;color:#666;">60日均线:${q.ma60.toFixed(4)}</div>
    `;
}

5.4 详细布林量化信号面板渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function renderSignal(q,history){
    let tip, cls;
    if(q.pct <= 30 && q.latest <= q.bollDown){
        tip = "深度低估 · 分批加仓信号";
        cls = "status-buy";
    }else if(q.pct >= 70 && q.latest >= q.bollUp){
        tip = "显著高估 · 分批减仓出售信号";
        cls = "status-sell";
    }else{
        tip = "中性估值区间 · 持有观望";
        cls = "status-watch";
    }
    const devDown = ((q.latest - q.bollDown)/q.bollDown*100).toFixed(2);
    const devUp = ((q.bollUp - q.latest)/q.latest*100).toFixed(2);
    const devAvg = ((q.latest - q.avg2y)/q.avg2y*100).toFixed(2);
    const pos = Math.max(0, Math.min(100, ((q.latest - q.min)/(q.max - q.min))*100));
    const p30 = ((quantile(history.map(d=>d.value),0.3)-q.min)/(q.max-q.min))*100;
    const p70 = ((quantile(history.map(d=>d.value),0.7)-q.min)/(q.max-q.min))*100;
    document.getElementById("signalContent").innerHTML = `
        <div class="signal-grid">
            <div class="signal-side">
                <div class="signal-label">布林下轨(加仓参考)</div>
                <div class="signal-price buy">${q.bollDown.toFixed(4)}</div>
                <div class="signal-gap">距下轨 ${devDown>0?"+":""}${devDown}%</div>
            </div>
            <div class="signal-status ${cls}">${tip}</div>
            <div class="signal-side">
                <div class="signal-label">布林上轨(减仓出售参考)</div>
                <div class="signal-price sell">${q.bollUp.toFixed(4)}</div>
                <div class="signal-gap">距上轨 +${devUp}%</div>
            </div>
        </div>
        <div class="signal-mid-info">2年均值:${q.avg2y.toFixed(4)}|中位数:${q.med2y.toFixed(4)}<br/>当前净值相对2年均值偏离:${devAvg>0?"+":""}${devAvg}%|两年历史估值分位:${q.pct.toFixed(1)}%</div>
        <div class="progress-bar">
            <div class="progress-line-mark" style="left:${p30}%"></div>
            <div class="progress-line-mark" style="left:${p70}%"></div>
            <div class="progress-marker" style="left:${pos}%"></div>
        </div>
        <div class="progress-labels"><span>2年最低 ${q.min.toFixed(4)}</span><span>估值30%分位</span><span>估值70%分位</span><span>2年最高 ${q.max.toFixed(4)}</span></div>
        <div class="signal-desc">量化规则:估值分位≤30%且净值跌破布林下轨=加仓;估值分位≥70%且净值突破布林上轨=减仓出售;中间区间持有不动</div>
    `;
}

5.5 资产配置卡片渲染

1
2
3
4
5
6
7
8
9
10
11
function renderAlloc(code){
    const a = FUND_LIST[code].alloc;
    document.getElementById("allocWrap").innerHTML = `
        <div class="alloc-grid">
            <div class="alloc-item stock"><div class="alloc-value">${a.stock}</div><div class="alloc-label">股票占比</div></div>
            <div class="alloc-item bond"><div class="alloc-value">${a.bond}</div><div class="alloc-label">债券占比</div></div>
            <div class="alloc-item cash"><div class="alloc-value">${a.cash}</div><div class="alloc-label">现金占比</div></div>
            <div class="alloc-item"><div class="alloc-value">${a.asset}</div><div class="alloc-label">基金总规模</div></div>
        </div>
    `;
}

模块 6:辅助报错弹窗

1
2
3
4
function showError(msg){
    document.getElementById("navContent").innerHTML = `<div class="error">${msg}<br><button class="retry-btn" onclick="loadCurrentFund()">重新加载</button></div>`;
    document.getElementById("signalContent").innerHTML = `<div class="error">无法生成布林带+分位量化信号</div>`;
}

模块 7:页面加载入口(初始化执行)

1
2
3
4
window.onload = function () {
    refreshAllBoard();
    loadNavData(currentFundCode);
}

模块 8:完整配套 CSS 样式(一次性复制,页面所有元素样式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:system-ui,-apple-system;background:#f0f2f5;color:#333;padding:20px}
.container{max-width:1200px;margin:0 auto}
.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;flex-wrap:wrap;gap:10px}
.header h1{font-size:22px;color:#1a3a6c}
.header .meta{font-size:13px;color:#888}
.btn-group{display:flex;gap:8px;flex-wrap:wrap}
.refresh-btn{background:#1a3a6c;color:#fff;border:none;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:14px}
.refresh-btn.batch{background:#2e7d32}
.refresh-btn:hover{background:#2a5a9c}
.refresh-btn.batch:hover{background:#249b29}
.refresh-btn:disabled{background:#999;cursor:not-allowed}
.tip-auto{font-size:12px;color:#2e7d32;background:#f0fff4;padding:4px 8px;border-radius:4px;margin:6px 0}
.tip-static{font-size:12px;color:#e65100;background:#fff3e0;padding:4px 8px;border-radius:4px;margin:6px 0}
.fund-tabs{display:flex;gap:8px;margin-bottom:16px;flex-wrap:wrap}
.fund-tab{padding:8px 16px;border-radius:6px;border:1px solid #dcdcdc;cursor:pointer;font-size:14px;background:#fff}
.fund-tab.active{background:#1a3a6c;color:#fff;border-color:#1a3a6c}
.fund-tab.loading{opacity:0.6;cursor:not-allowed}
.card{background:#fff;border-radius:12px;padding:24px;margin-bottom:16px;box-shadow:0 2px 8px rgba(0,0,0,0.06)}
.card-title{font-size:14px;color:#888;margin-bottom:12px;display:flex;justify-content:space-between;align-items:center;font-weight:500}
.nav-card{text-align:center}
.nav-value{font-size:48px;font-weight:bold;color:#1a3a6c;margin:10px 0}
.nav-date{font-size:14px;color:#888}
.nav-change{font-size:18px;margin-top:8px}
.nav-change.up{color:#e53935}
.nav-change.down{color:#2e7d32}
.badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:12px;background:#e8f5e9;color:#2e7d32;margin-left:8px}
.star-rating{font-size:20px;letter-spacing:2px;margin:6px 0}
.signal-grid{display:grid;grid-template-columns:1fr auto 1fr;gap:20px;align-items:center}
.signal-side{text-align:center}
.signal-label{font-size:13px;color:#888;margin-bottom:6px}
.signal-price{font-size:24px;font-weight:bold}
.signal-price.buy{color:#2e7d32}
.signal-price.sell{color:#e53935}
.signal-mid-info{color:#1a3a6c;font-size:13px;margin-top:6px;line-height:1.5}
.signal-gap{font-size:12px;color:#888;margin-top:4px}
.signal-status{padding:12px 24px;border-radius:8px;font-size:18px;font-weight:bold;text-align:center;min-width:140px}
.status-watch{background:#fff3e0;color:#e65100}
.status-buy{background:#e8f5e9;color:#2e7d32}
.status-sell{background:#ffebee;color:#c62828}
.signal-desc{margin-top:14px;font-size:12px;color:#666;line-height:1.7;background:#f7f8fa;padding:10px;border-radius:6px}
.progress-bar{margin-top:20px;height:8px;background:linear-gradient(90deg,#2e7d32 30%,#ff9800 30%,#ff9800 70%,#e53935 70%);border-radius:4px;position:relative;overflow:visible}
.progress-marker{position:absolute;top:-6px;width:20px;height:20px;background:#1a3a6c;border-radius:50%;transform:translateX(-50%);border:3px solid #fff;box-shadow:0 2px 4px rgba(0,0,0,0.2)}
.progress-line-mark{position:absolute;width:1px;height:12px;background:#333;top:-2px;transform:translateX(-50%)}
.progress-labels{display:flex;justify-content:space-between;font-size:11px;color:#aaa;margin-top:6px}
.chart-container{width:100%;height:320px}
.chart-tabs{display:flex;gap:8px;margin-bottom:12px}
.chart-tab{padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;background:#f0f0f0;color:#666}
.chart-tab.active{background:#1a3a6c;color:#fff}
.alloc-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;text-align:center}
.alloc-item .alloc-value{font-size:22px;font-weight:bold;color:#1a3a6c}
.alloc-item .alloc-label{font-size:12px;color:#888;margin-top:4px}
.alloc-item.stock .alloc-value{color:#e53935}
.alloc-item.bond .alloc-value{color:#1565c0}
.alloc-item.cash .alloc-value{color:#2e7d32}
.footer{text-align:center;font-size:12px;color:#aaa;margin-top:20px}
.loading{text-align:center;padding:30px;color:#999}
.error{text-align:center;padding:20px;color:#e53935}
.retry-btn{margin-top:10px;padding:6px 16px;border:1px solid #e53935;background:#fff;color:#e53935;border-radius:4px;cursor:pointer}
.board-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-top:10px}
.board-card{border-radius:8px;padding:14px;text-align:center;border:1px solid #eee}
.board-buy{border:2px solid #2e7d32;background:#f0fff4}
.board-watch{border:2px solid #ff9800;background:#fffbf0}
.board-sell{border:2px solid #e53935;background:#fff0f0}
.board-loading{background:#f5f5f5;color:#999}
.board-code{font-weight:bold;font-size:15px;margin-bottom:4px}
.board-name{font-size:12px;color:#666;margin-bottom:8px}
.board-signal{font-size:16px;font-weight:bold;margin-bottom:6px}
.board-pct{font-size:12px;color:#555}
@media (max-width:992px){
    .board-grid{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:768px){
    body{padding:12px}
    .card{padding:16px}
    .nav-value{font-size:36px}
    .signal-grid{grid-template-columns:1fr;gap:12px}
    .signal-status{order:-1}
    .chart-container{height:260px}
    .header h1{font-size:18px}
    .alloc-grid{grid-template-columns:repeat(2,1fr)}
}

模块 9:页面 HTML 骨架(DOM 结构,ID 与 JS 严格对应,不能修改 ID)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>基金量化面板</title>
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<!-- 粘贴上面完整CSS -->
</head>
<body>
<div class="container">
    <div class="header">
        <div>
            <h1>场外基金量化监控面板</h1>
            <div class="meta">最后刷新时间:<span id="updateTime">未执行刷新</span>
                <span class="data-source">净值自动API:东方财富;量化模型:布林轨道+两年历史分位</span>
            </div>
        </div>
        <div class="btn-group">
            <button class="refresh-btn" onclick="refreshAllBoard()">一键刷新全部5只基金信号</button>
            <button class="refresh-btn" id="refreshSingleBtn" onclick="loadCurrentFund()">刷新当前选中基金</button>
        </div>
    </div>
    <div class="tip-auto">✅ 自动拉取净值、自动计算量化信号;绿色=加仓,黄色=持有观望,红色=适合减仓出售</div>
    <div class="tip-static">⚠️ 资产配置为静态基础数据,无持仓、无交易台账</div>
 
    <!-- 五基金总览看板容器 -->
    <div class="card">
        <div class="card-title">五只基金今日量化信号总览(快速判断加仓/持有/减仓出售)</div>
        <div class="board-grid" id="signalBoard"></div>
    </div>
 
    <!-- 基金切换标签 -->
    <div class="fund-tabs" id="fundTabWrap">
        <div class="fund-tab active" data-code="011609" onclick="switchFund('011609')">011609 易方达上证科创50ETF联接C</div>
        <div class="fund-tab" data-code="024481" onclick="switchFund('024481')">024481 财通品质甄选混合C</div>
        <div class="fund-tab" data-code="016873" onclick="switchFund('016873')">016873 广发远见智选混合A</div>
        <div class="fund-tab" data-code="007339" onclick="switchFund('007339')">007339 易方达沪深300ETF联接C</div>
        <div class="fund-tab" data-code="018124" onclick="switchFund('018124')">018124 永赢先进制造智选混合发起A</div>
    </div>
 
    <!-- 单位净值卡片 -->
    <div class="card nav-card">
        <div class="card-title">单位净值 <span id="fundNameTitle">011609 易方达上证科创50ETF联接C</span></div>
        <div id="navContent" class="loading">等待自动加载净值API</div>
    </div>
 
    <!-- 详细布林量化信号 -->
    <div class="card">
        <div class="card-title">单基详细量化买卖信号(布林带 + 2年历史估值分位)</div>
        <div id="signalContent" class="loading">自动计算估值与交易信号</div>
    </div>
 
    <!-- K线图表 -->
    <div class="card">
        <div class="chart-tabs">
            <div class="chart-tab active" data-range="6m" onclick="switchChart('6m')">半年走势</div>
            <div class="chart-tab" data-range="2y" onclick="switchChart('2y')">两年走势</div>
        </div>
        <div class="chart-container" id="chart"></div>
    </div>
 
    <!-- 资产配置 -->
    <div class="card">
        <div class="card-title">基金资产配置比例</div>
        <div id="allocWrap" class="loading">加载配置数据</div>
    </div>
 
    <div class="footer">净值API:东方财富公开接口;量化规则:分位≤30%+跌破布林下轨加仓|分位≥70%+突破布林上轨减仓出售</div>
</div>
<!-- 依次粘贴所有JS模块代码 -->
</body>
</html>

复用搭建新网站操作步骤(下次新建页面直接照做)

  1. 复制 HTML 骨架,粘贴到新 html 文件
  2. 将完整 CSS 粘贴至<head>内
  3. 按顺序粘贴 JS 模块(顺序不能乱)
    1. FUND_LIST 基金配置
    2. 全局数学工具函数
    3. calcQuant 量化核心计算
    4. API 净值拉取函数
    5. 所有渲染函数
    6. 报错函数 showError
    7. window.onload 页面初始化
  4. 修改 FUND_LIST 内基金代码 / 名称 / 资产配置,即可适配自己的基金
  5. 如需拓展功能,可单独复制对应渲染模块,不影响其他功能运行

还有什么 API 没有出现的么?

一、你当前代码只用的唯一接口

1
2
3
// 历史净值K线(仅这个在你文档里用到)
`https://fund.eastmoney.com/pingzhongdata/${code}.js?t=${Date.now()}`
// 只解析其中变量 Data_netWorthTrend 做布林计算

二、未使用、前端可直接引入 JS 的接口(和现有加载逻辑完全一致)

1. 实时盘中估值接口(最实用,盘中看涨跌)

地址:https://fundgz.1234567.com.cn/js/${基金代码}.js

返回全局变量 gzfund,包含:

  • 实时估算净值、实时涨跌幅、今日预估收益、昨日净值、更新时间

    适用场景:顶部总览看板增加盘中实时估值,不用等收盘净值

    加载方式(和 loadNavData 逻辑一模一样):

1
2
3
4
5
6
7
8
9
function loadRealTimeValuation(code, callback) {
  const script = document.createElement('script');
  script.src = `https://fundgz.1234567.com.cn/js/${code}.js?t=${Date.now()}`;
  script.onload = () => {
    callback(window.gzfund);
    delete window.gzfund;
  };
  document.body.appendChild(script);
}

2. 全市场所有基金代码 + 名称列表接口

地址:https://fund.eastmoney.com/js/fundcode_search.js

用途:不用手动写死 FUND_LIST,自动下拉搜索基金、新增基金无需改代码

返回全局数组 r,每条包含 基金代码、基金简称、基金类型

3. 全部基金公司列表接口

地址:https://fund.eastmoney.com/js/jjjz_gs.js

用途:按基金公司筛选基金,拓展筛选面板

4. F10 定期报告系列分页接口(分红、持仓、历史净值区间查询)

基础地址:

https://fundf10.eastmoney.com/F10DataApi.aspx

参数说明:

  • type=lsjz:指定起止日期历史净值(自定义时间区间,不限制只能取近两年)
  • type=fhps:分红送配记录
  • type=cgch:前十大持仓股票
  • code:基金代码
  • sdate/edate:起始 / 结束日期
  • per:每页条数,page 页码

    用途:做分红统计、持仓分析、自定义区间收益计算

5. pingzhongdata 同文件内其他隐藏变量(同一个接口,但你代码没解析)

你现在只取 Data_netWorthTrend,同一个 js 文件里还有大量数据不用额外请求:
  1. Data_ACWorthTrend:累计净值走势
  2. Data_income:各阶段收益率(近 1 月 / 3 月 / 6 月 / 1 年 / 3 年)
  3. Data_assetAllocation:资产配置明细(股票 / 债券 / 现金占比,比你静态 alloc 更实时)
  4. Data_manager:基金经理、任职时间、历史业绩
  5. Data_dividend:历史分红记录
  6. Data_scale:基金最新规模变化曲线

三、第三方商用付费 API(不在东方财富免费 JS 体系,按需选用)

  1. 极速数据基金 API:https://api.jisuapi.com/fund/openquery(需要 appkey,JSON 标准接口)
  2. Twelve Data 海外基金 API:境外公募、ETF 完整数据(英文市场)
  3. iFinD 同花顺专业金融接口:机构级分钟估值、宏观经济数据(付费)

四、可拓展功能对应的缺失 API 总结(对应你现有面板)

  1. 盘中实时估值 → fundgz 实时 JS 接口(缺失)
  2. 自动基金列表搜索 → fundcode_search.js(缺失,目前手动硬编码 5 只)
  3. 实时资产配置、基金经理、阶段收益 → pingzhongdata 内置变量未解析(缺失)
  4. 分红记录、自定义区间净值、持仓明细 → F10DataApi 分页接口(缺失)
  5. 基金公司筛选 → jjjz_gs.js(缺失)

五、最简拓展示例(给顶部看板增加实时估值,直接插入现有代码)

1
2
3
4
5
6
7
8
9
10
11
// 在renderSignalBoard卡片内部增加实时估值一行
function loadRealTimeForBoard(code, domCallback) {
  const s = document.createElement("script");
  s.src = `https://fundgz.1234567.com.cn/js/${code}.js?t=${Date.now()}`;
  s.onload = () => {
    const data = window.gzfund;
    domCallback(data);
    delete window.gzfund;
  };
  document.body.appendChild(s);
}

 

页面

  • 【AI模型】Stable Diffusion3.5
  • 【个人笔记】Adobe-URL-Block-List
  • 【个人笔记】AI 基础底模
  • 【个人笔记】PC电脑安全、维护相关CMD、PowerShell‌命令
  • 【个人笔记】Powershell 相关命令
  • 【个人笔记】基金相关
  • 【个人笔记】基金量化面板 模块化核心代码拆分
  • 【个人笔记】微软自带的电脑修复
  • 【个人笔记】整理一下红色警戒2的一些优秀的mod
  • 【个人笔记】服务器命令
  • 【个人笔记】模拟城市4 秘籍
  • 【个人笔记】游戏秘籍
  • 【个人笔记】红色警戒mod心灵终结自制地图笔记
  • 【个人笔记】自用激活码备份。
  • 【学习笔记】CMD的正确命令
  • 【学习笔记】ipconfig
  • 【学习笔记】网页代码
  • 【批处理软件】压缩包密码批量测试(包含前置软件) 百度网盘下载页面
  • 【摄影学习】日本明星写真
  • Windows 命令
  • 个人用导航
  • 中国特惠!多平台广告屏蔽专家 AdGuard 买断仅需 119 元起
  • 九月开学季,六折起购 EndNote、Typora、BookxNote,部分库存限量抢
  • 公共DNS服务器地址大全
  • 在线的免费病毒扫描服务 – Virustotal.com 官网
  • 归档
  • 隐私政策
  • 鸣谢名单

分类

近期文章

  • 【航拍】2026全国新版无人驾驶航空器适飞空域地图启用 2026年 5月 14日
  • 【胶片相机】Lubitel 166B 120胶卷相机 2026年 3月 31日
  • 【软件推荐】HandBrake 开源免费视频转码工具 2026年 3月 2日
  • 【软件推荐】重复图片整理工具 FirmTools Duplicate Photo Finder 2026年 1月 12日
  • 【AI生图】必备模型 图片放大算法 4x-UltraSharp & 4x-UltraSharp V2 2025年 7月 30日

个人用

  • 【AI模型】Stable Diffusion3.5
  • 【个人笔记】Adobe-URL-Block-List
  • 【个人笔记】AI 基础底模
  • 【个人笔记】PC电脑安全、维护相关CMD、PowerShell‌命令
  • 【个人笔记】Powershell 相关命令
  • 【个人笔记】基金相关
  • 【个人笔记】基金量化面板 模块化核心代码拆分
  • 【个人笔记】微软自带的电脑修复
  • 【个人笔记】整理一下红色警戒2的一些优秀的mod
  • 【个人笔记】服务器命令
  • 【个人笔记】模拟城市4 秘籍
  • 【个人笔记】游戏秘籍
  • 【个人笔记】红色警戒mod心灵终结自制地图笔记
  • 【个人笔记】自用激活码备份。
  • 【学习笔记】CMD的正确命令
  • 【学习笔记】ipconfig
  • 【学习笔记】网页代码
  • 【批处理软件】压缩包密码批量测试(包含前置软件) 百度网盘下载页面
  • 【摄影学习】日本明星写真
  • Windows 命令
  • 个人用导航
  • 中国特惠!多平台广告屏蔽专家 AdGuard 买断仅需 119 元起
  • 九月开学季,六折起购 EndNote、Typora、BookxNote,部分库存限量抢
  • 公共DNS服务器地址大全
  • 在线的免费病毒扫描服务 – Virustotal.com 官网
  • 归档
  • 隐私政策
  • 鸣谢名单

社交平台

Bilibili
WEIBO
小红书:林霖
(小红书号:679622260)
公众号:林霖的网络笔记
© 1994-2024 独立摄影师-林霖 Filmlin.com All rights reserved.
部分图片配图,封面图来源于:摄图网(个人商用授权)
本站服务器由腾讯云提供
闽公网安备 35020602002133号
闽ICP备2021011430号-1