|
@@ -27,58 +27,63 @@ tickerSelect.addEventListener('change', async () => {
|
|
|
|
|
|
|
|
async function render() {
|
|
async function render() {
|
|
|
charts.innerHTML = '';
|
|
charts.innerHTML = '';
|
|
|
- renderPriceChart(tickerSelect.value, 'NC1');
|
|
|
|
|
- renderPriceChart(tickerSelect.value, 'CI1');
|
|
|
|
|
- renderPriceChart(tickerSelect.value, 'IC1');
|
|
|
|
|
- renderPriceChart(tickerSelect.value, 'AI1');
|
|
|
|
|
|
|
+ const cxpc = await Promise.all([
|
|
|
|
|
+ getCXPC(tickerSelect.value, 'NC1'), getCXPC(tickerSelect.value, 'CI1'),
|
|
|
|
|
+ getCXPC(tickerSelect.value, 'IC1'), getCXPC(tickerSelect.value, 'AI1'),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ const maxPrice = Math.max(...cxpc.flatMap((cxPrices) => cxPrices.map((p) => p.High)));
|
|
|
|
|
+ const maxTraded = Math.max(...cxpc.flatMap((cxPrices) => cxPrices.map((t) => t.Traded)));
|
|
|
|
|
+ charts.append(
|
|
|
|
|
+ renderPriceChart('NC1', maxPrice, maxTraded, cxpc[0]), renderPriceChart('CI1', maxPrice, maxTraded, cxpc[1]),
|
|
|
|
|
+ renderPriceChart('IC1', maxPrice, maxTraded, cxpc[2]), renderPriceChart('AI1', maxPrice, maxTraded, cxpc[3]),
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async function renderPriceChart(ticker: string, cx: string) {
|
|
|
|
|
|
|
+async function getCXPC(ticker: string, cx: string): Promise<PriceChartPoint[]> {
|
|
|
const cxpc: PriceChartPoint[] = await cachedFetchJSON(`https://rest.fnar.net/exchange/cxpc/${ticker}.${cx}`);
|
|
const cxpc: PriceChartPoint[] = await cachedFetchJSON(`https://rest.fnar.net/exchange/cxpc/${ticker}.${cx}`);
|
|
|
- const filtered = cxpc.filter((p) => p.Interval === 'HOUR_TWELVE').map((p) => ({
|
|
|
|
|
- ...p,
|
|
|
|
|
- Date: new Date(p.DateEpochMs)
|
|
|
|
|
- }));
|
|
|
|
|
- const maxPrice = Math.max(...filtered.map((p) => p.High));
|
|
|
|
|
- const maxTraded = Math.max(...filtered.map((t) => t.Traded));
|
|
|
|
|
- charts.appendChild(Plot.plot({
|
|
|
|
|
|
|
+ return cxpc.filter((p) => p.Interval === 'HOUR_TWELVE');
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function renderPriceChart(cx: string, maxPrice: number, maxTraded: number, cxpc: PriceChartPoint[]):
|
|
|
|
|
+ SVGSVGElement | HTMLElement {
|
|
|
|
|
+ return Plot.plot({
|
|
|
grid: true,
|
|
grid: true,
|
|
|
width: charts.getBoundingClientRect().width / 2 - 10,
|
|
width: charts.getBoundingClientRect().width / 2 - 10,
|
|
|
height: 400,
|
|
height: 400,
|
|
|
y: {axis: 'left', label: cx, domain: [0, maxPrice * 1.1]},
|
|
y: {axis: 'left', label: cx, domain: [0, maxPrice * 1.1]},
|
|
|
marks: [
|
|
marks: [
|
|
|
- Plot.axisY(d3.ticks(0, maxTraded * 2, 10), {
|
|
|
|
|
|
|
+ Plot.axisY(d3.ticks(0, maxTraded * 1.5, 10), {
|
|
|
label: 'traded',
|
|
label: 'traded',
|
|
|
anchor: 'right',
|
|
anchor: 'right',
|
|
|
y: (d) => (d / maxTraded) * maxPrice / 3,
|
|
y: (d) => (d / maxTraded) * maxPrice / 3,
|
|
|
}),
|
|
}),
|
|
|
- Plot.rectY(filtered, {
|
|
|
|
|
- x: 'Date',
|
|
|
|
|
- y: (t) => (t.Traded / maxTraded) * maxPrice / 3, // scale traded to price
|
|
|
|
|
|
|
+ Plot.rectY(cxpc, {
|
|
|
|
|
+ x: (p) => new Date(p.DateEpochMs),
|
|
|
|
|
+ y: (p) => (p.Traded / maxTraded) * maxPrice / 3, // scale traded to price
|
|
|
interval: 'day',
|
|
interval: 'day',
|
|
|
fill: '#272',
|
|
fill: '#272',
|
|
|
- fillOpacity: 0.1,
|
|
|
|
|
|
|
+ fillOpacity: 0.2,
|
|
|
}),
|
|
}),
|
|
|
- Plot.ruleX(filtered, {
|
|
|
|
|
- x: 'Date',
|
|
|
|
|
|
|
+ Plot.ruleX(cxpc, {
|
|
|
|
|
+ x: (p) => new Date(p.DateEpochMs),
|
|
|
y1: 'Low',
|
|
y1: 'Low',
|
|
|
y2: 'High',
|
|
y2: 'High',
|
|
|
strokeWidth: 5,
|
|
strokeWidth: 5,
|
|
|
stroke: '#42a',
|
|
stroke: '#42a',
|
|
|
}),
|
|
}),
|
|
|
- Plot.dot(filtered, {
|
|
|
|
|
- x: 'Date',
|
|
|
|
|
|
|
+ Plot.dot(cxpc, {
|
|
|
|
|
+ x: (p) => new Date(p.DateEpochMs),
|
|
|
y: (p) => p.Volume / p.Traded,
|
|
y: (p) => p.Volume / p.Traded,
|
|
|
fill: '#a37',
|
|
fill: '#a37',
|
|
|
r: 2,
|
|
r: 2,
|
|
|
}),
|
|
}),
|
|
|
- Plot.crosshairX(filtered, {
|
|
|
|
|
- x: 'Date',
|
|
|
|
|
|
|
+ Plot.crosshairX(cxpc, {
|
|
|
|
|
+ x: (p) => new Date(p.DateEpochMs),
|
|
|
y: (p) => p.Volume / p.Traded,
|
|
y: (p) => p.Volume / p.Traded,
|
|
|
textStrokeWidth: 0,
|
|
textStrokeWidth: 0,
|
|
|
})
|
|
})
|
|
|
]
|
|
]
|
|
|
- }));
|
|
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
interface Material {
|
|
interface Material {
|