188 lines
5.7 KiB
JavaScript
188 lines
5.7 KiB
JavaScript
import {
|
||
PALETTE, FONT, axisCommon, categoryAxis, titleStyle,
|
||
lineSeries, footnote, watermarks,
|
||
} from '../theme.js';
|
||
|
||
/** 圆角卡片:ECharts 的 graphic.group + rect + 多段文字 */
|
||
function infoCard({ left, top, width, title, lines, fill, stroke, titleColor }) {
|
||
const children = [
|
||
{
|
||
type: 'rect',
|
||
shape: { x: 0, y: 0, width, height: 108, r: 8 },
|
||
style: { fill, stroke, lineWidth: 1 },
|
||
},
|
||
{
|
||
type: 'text',
|
||
style: {
|
||
x: 20, y: 20,
|
||
text: title,
|
||
fill: titleColor,
|
||
fontSize: 15,
|
||
fontWeight: 'bold',
|
||
fontFamily: FONT,
|
||
},
|
||
},
|
||
];
|
||
lines.forEach((line, i) => {
|
||
children.push({
|
||
type: 'text',
|
||
style: {
|
||
x: 20, y: 52 + i * 22,
|
||
text: line,
|
||
fill: '#555',
|
||
fontSize: 12.5,
|
||
fontFamily: FONT,
|
||
},
|
||
});
|
||
});
|
||
return { type: 'group', left, top, silent: true, children };
|
||
}
|
||
|
||
/**
|
||
* 图 5:人民币兑美元两种口径对比
|
||
* 只有两个时间点,重点在文字说明和端点标注,不在曲线本身
|
||
*/
|
||
export function fxChart(rows, { width, height }) {
|
||
const stages = rows.map((r) => r.stage);
|
||
const mid = rows.map((r) => r.mid_price);
|
||
const spot = rows.map((r) => r.onshore_spot);
|
||
|
||
// label.position 不接受函数(写函数会被静默忽略,退化成默认位置),
|
||
// 想让首尾点位置不同只能逐点覆盖:起点标在上方,末点标在右侧
|
||
const endpointLabels = (values) =>
|
||
values.map((v, i) => ({
|
||
value: v,
|
||
label: { position: i === values.length - 1 ? 'right' : 'top' },
|
||
}));
|
||
|
||
// 偏离点数 = (中间价 - 即期)× 10000
|
||
const gapStart = Math.round((mid[0] - spot[0]) * 10000);
|
||
const gapEnd = Math.round((mid[1] - spot[1]) * 10000);
|
||
const midGain = Math.round((mid[0] - mid[1]) * 10000);
|
||
const spotGain = Math.round((spot[0] - spot[1]) * 10000);
|
||
const midPct = (((mid[0] - mid[1]) / mid[0]) * 100).toFixed(1);
|
||
const spotPct = (((spot[0] - spot[1]) / spot[0]) * 100).toFixed(1);
|
||
|
||
const cardWidth = 340;
|
||
const cardTop = 132;
|
||
|
||
return {
|
||
backgroundColor: '#fff',
|
||
title: {
|
||
...titleStyle,
|
||
text: '人民币兑美元的两种「升值」口径:中间价 vs 在岸即期',
|
||
subtext: '口径区别在「谁定的价」:中间价是央行每日公布的锚,即期是市场在锚附近交易出来的成交价',
|
||
top: 20,
|
||
},
|
||
grid: { left: 96, right: 110, top: 340, bottom: 88 },
|
||
tooltip: { trigger: 'axis', textStyle: { fontFamily: FONT } },
|
||
xAxis: {
|
||
...categoryAxis,
|
||
boundaryGap: ['12%', '12%'],
|
||
data: stages,
|
||
},
|
||
yAxis: {
|
||
...axisCommon,
|
||
type: 'value',
|
||
name: 'USDCNY(数值越小=人民币越强)',
|
||
nameLocation: 'middle',
|
||
nameGap: 62,
|
||
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
|
||
min: 6.75,
|
||
max: 7.05,
|
||
interval: 0.1,
|
||
axisLabel: {
|
||
...axisCommon.axisLabel,
|
||
formatter: (v) => v.toFixed(2),
|
||
},
|
||
},
|
||
series: [
|
||
{
|
||
...lineSeries({
|
||
name: '中间价',
|
||
data: mid,
|
||
color: '#3b6ea5',
|
||
formatter: (p) => p.value.toFixed(4),
|
||
}),
|
||
data: endpointLabels(mid),
|
||
symbolSize: 11,
|
||
itemStyle: { color: '#3b6ea5' },
|
||
label: {
|
||
show: true,
|
||
fontSize: 15,
|
||
fontWeight: 'bold',
|
||
color: '#3b6ea5',
|
||
fontFamily: FONT,
|
||
distance: 10,
|
||
formatter: (p) => p.value.toFixed(4),
|
||
},
|
||
},
|
||
{
|
||
...lineSeries({
|
||
name: '在岸即期',
|
||
data: spot,
|
||
color: PALETTE.coral,
|
||
formatter: (p) => p.value.toFixed(4),
|
||
}),
|
||
data: endpointLabels(spot),
|
||
symbolSize: 11,
|
||
itemStyle: { color: PALETTE.coral },
|
||
label: {
|
||
show: true,
|
||
fontSize: 15,
|
||
fontWeight: 'bold',
|
||
color: PALETTE.coral,
|
||
fontFamily: FONT,
|
||
distance: 10,
|
||
formatter: (p) => p.value.toFixed(4),
|
||
},
|
||
},
|
||
],
|
||
graphic: [
|
||
...watermarks(width, height),
|
||
|
||
infoCard({
|
||
left: 88, top: cardTop, width: cardWidth,
|
||
title: '中间价(央行公布的「锚」)',
|
||
lines: [
|
||
'每交易日 9:15 由央行公布:做市商报价',
|
||
'加权+逆周期因子,是当日波动中心与政策信号',
|
||
],
|
||
fill: '#eef4fa', stroke: '#c7dcf0', titleColor: '#2f5f8f',
|
||
}),
|
||
infoCard({
|
||
left: 88 + cardWidth + 24, top: cardTop, width: cardWidth,
|
||
title: '在岸即期(市场成交价)',
|
||
lines: [
|
||
'银行间外汇市场实时成交形成,16:30 收盘价',
|
||
'最常被引用;每日只能在中间价 ±2% 区间内波动',
|
||
],
|
||
fill: '#fdf1ed', stroke: '#f5d3c8', titleColor: '#c05a45',
|
||
}),
|
||
|
||
// 图例改成两行文字说明,信息量比色块图例大
|
||
{
|
||
type: 'text',
|
||
left: 108, top: 272, silent: true,
|
||
style: {
|
||
text: `● 中间价:${mid[0]} → ${mid[1]},累计升值 ${midGain}点(+${midPct}%)`,
|
||
fill: '#3b6ea5', fontSize: 14, fontFamily: FONT,
|
||
},
|
||
},
|
||
{
|
||
type: 'text',
|
||
left: 108, top: 296, silent: true,
|
||
style: {
|
||
text: `● 在岸即期:${spot[0]} → ${spot[1]},累计升值 ${spotGain}点(+${spotPct}%)`,
|
||
fill: PALETTE.coral, fontSize: 14, fontFamily: FONT,
|
||
},
|
||
},
|
||
|
||
footnote(
|
||
`注:涨幅差${midGain - spotGain}点=期初偏离${gapStart}−期末偏离${gapEnd}(即期偏强收窄)|` +
|
||
'汇兑重估用市场汇率而非中间价|数据:新华财经|Joey 谨制'
|
||
),
|
||
],
|
||
};
|
||
}
|