This is an automated email from the ASF dual-hosted git repository.

ovilia pushed a commit to branch feat-matrix-stock
in repository https://gitbox.apache.org/repos/asf/echarts-examples.git

commit a599a838307245bc1d37fb36b9e3780b4955ac39
Author: Ovilia <[email protected]>
AuthorDate: Thu Oct 16 16:03:38 2025 +0800

    WIP: intraday line series
---
 public/examples/ts/matrix-stock.ts | 193 +++++++++++++++++++++++++++++++++++++
 1 file changed, 193 insertions(+)

diff --git a/public/examples/ts/matrix-stock.ts 
b/public/examples/ts/matrix-stock.ts
new file mode 100644
index 00000000..df1c0e86
--- /dev/null
+++ b/public/examples/ts/matrix-stock.ts
@@ -0,0 +1,193 @@
+/*
+title: Matrix Stock Application
+category: matrix-stock
+titleCN: 简单的矩阵图
+difficulty: 3
+since: 6.0.0
+*/
+
+const lastClose = 50; // Close value of yesterday
+const colorGreen = '#14b143';
+const colorRed = '#ef232a';
+const colorGray = '#888';
+
+const getPriceColor = (price: number) => {
+  return price === lastClose
+    ? colorGray
+    : price > lastClose
+    ? colorRed
+    : colorGreen;
+};
+const priceFormatter = (value: number) => {
+  const result = Math.round(value * 100) / 100 + '';
+  // Adding padding 0 if needed
+  let dotIndex = result.indexOf('.');
+  if (dotIndex < 0) {
+    return result + '.00';
+  } else if (dotIndex === result.length - 2) {
+    return result + '0';
+  }
+  return result;
+};
+
+const priceData = [];
+const volumeData = [];
+const averageData = []; // Average of volume * price
+let sumPrice = 0;
+let sumVolume = 0;
+const sTime = new Date('2025-10-16 09:30:00').getTime();
+const eTime = new Date('2025-10-16 15:00:00').getTime();
+const breakStartTime = new Date('2025-10-16 11:30:00').getTime();
+const breakEndTime = new Date('2025-10-16 13:00:00').getTime();
+
+let time = startTime;
+let price = lastClose;
+let direction = 1; // 1 for up, -1 for down
+let maxAbs = 0;
+while (time < eTime) {
+  const volume = Math.random() * 1000 + 500;
+  volumeData.push([time, volume]);
+  sumVolume += volume;
+
+  if (time === startTime) {
+    // Today open price
+    direction = Math.random() < 0.5 ? 1 : -1;
+    price = lastClose * (1 + (Math.random() - 0.5) * 0.02);
+  } else {
+    // 70% chance to maintain the last direction
+    direction = Math.random() < 0.8 ? direction : -direction;
+    price = Math.round((price + direction * (Math.random() * 0.1)) * 100) / 
100;
+  }
+  priceData.push([time, price]);
+
+  sumPrice += price * volume;
+  averageData.push([time, sumPrice / sumVolume]);
+
+  maxAbs = Math.max(maxAbs, Math.abs(price - lastClose));
+
+  if (time === breakStartTime) {
+    time = breakEndTime;
+  } else {
+    time += 60 * 1000; // increment by 1 minute
+  }
+}
+
+option = {
+  xAxis: [
+    {
+      type: 'time',
+      show: false,
+      breaks: [
+        {
+          start: breakStartTime,
+          end: breakEndTime,
+          gap: 0
+        }
+      ]
+    }
+  ],
+  yAxis: [
+    {
+      type: 'value',
+      show: false,
+      // Value should be symmetric around zero
+      min: lastClose - maxAbs,
+      max: lastClose + maxAbs
+    }
+  ],
+  grid: [
+    {
+      coordinateSystem: 'matrix',
+      coord: [0, 0],
+      top: 0,
+      bottom: 0,
+      left: 0,
+      right: 0
+    }
+  ],
+  series: [
+    {
+      type: 'line',
+      symbolSize: 0,
+      data: priceData,
+      markPoint: {
+        symbolSize: 0,
+        symbol: 'circle',
+        data: [
+          {
+            relativeTo: 'coordinate',
+            x: 0,
+            y: 0,
+            name: 'max',
+            type: 'max',
+            label: {
+              align: 'left',
+              verticalAlign: 'top',
+              formatter: priceFormatter(lastClose + maxAbs),
+              color: getPriceColor(lastClose + maxAbs)
+            }
+          },
+          {
+            relativeTo: 'coordinate',
+            x: 0,
+            y: '100%',
+            name: 'min',
+            type: 'min',
+            label: {
+              align: 'left',
+              verticalAlign: 'bottom',
+              formatter: priceFormatter(lastClose - maxAbs),
+              color: getPriceColor(lastClose - maxAbs)
+            }
+          },
+          {
+            relativeTo: 'coordinate',
+            x: '100%',
+            y: 0,
+            name: priceFormatter((maxAbs / lastClose) * 100) + '%',
+            label: {
+              align: 'right',
+              verticalAlign: 'top',
+              color: colorRed,
+              formatter: (params) => params.name
+            }
+          },
+          {
+            relativeTo: 'coordinate',
+            x: '100%',
+            y: '100%',
+            name: '-' + priceFormatter((maxAbs / lastClose) * 100) + '%',
+            label: {
+              align: 'right',
+              verticalAlign: 'bottom',
+              color: colorGreen,
+              formatter: (params) => params.name
+            }
+          }
+        ]
+      }
+    },
+    {
+      type: 'line',
+      symbolSize: 0,
+      data: averageData,
+      xAxisIndex: 0,
+      yAxisIndex: 0
+    }
+  ],
+  matrix: {
+    x: { data: Array(5).fill(null) },
+    y: { data: Array(6).fill(null) },
+    body: {
+      data: [
+        {
+          coord: [
+            [0, 3],
+            [0, 3]
+          ],
+          mergeCells: true
+        }
+      ]
+    }
+  }
+};


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to