Kaynağa Gözat

成交量堆叠,单独计算。

skyffire 1 yıl önce
ebeveyn
işleme
522c86c854
4 değiştirilmiş dosya ile 99 ekleme ve 54 silme
  1. 4 2
      example/src/App.js
  2. 89 48
      src/index.js
  3. 2 2
      src/utils.js
  4. 4 2
      types/index.d.ts

+ 4 - 2
example/src/App.js

@@ -48,8 +48,10 @@ function parseStockData(data) {
   // Construct the final object
   return {
     marketDepth: {
-      lastTradedPrice: last_price,
-      lastTradedQty: last_qty,
+      lastBuyPrice: side === 'buy' ? last_price : 0,
+      lastBuyQty: side === 'buy' ? last_qty : 0,
+      lastSellPrice: side === 'sell' ? last_price : 0,
+      lastSellQty: side === 'sell' ? last_qty : 0,
       priceChangeAmt: last_price - open,  // Simplified price change amount
       priceChangePct: ((last_price - open) / open * 100).toFixed(2),
       lastTradedTS: Date.now(),

+ 89 - 48
src/index.js

@@ -368,8 +368,8 @@ export default class StockHeatmap extends React.Component {
       // 绘制方块内数据
       drawPending.map((d, index) => {
         let depth = d.marketDepth;
-        let text = `${depth.side === 'sell' ? '卖出' : '买入'} ${depth.lastTradedQty} 在 ${depth.lastTradedPrice} `;
-        context.fillStyle = d3.color(depth.side == 'sell' ? '#222' : '#222').rgb();
+        let text = depth.lastSellQty !== 0 ? `卖出 ${depth.lastSellQty} 在 ${depth.lastSellPrice}` : `买入 ${depth.lastBuyQty} 在 ${depth.lastBuyPrice}`
+        context.fillStyle = d3.color(depth.side === 'sell' ? '#222' : '#222').rgb();
         context.fillText(`${d.ts}`, x + 10, y + 7 + 15 + index * 45);
         context.fillText(text, x + 10, y + 7 + 30 + index * 45);
         if (index < drawPending.length - 1) {
@@ -475,13 +475,14 @@ export default class StockHeatmap extends React.Component {
     this.drawingContext.fillText(`${maxVolumeInWindowData}`, 20 + w + 20, this.defaults.axisTickSize + this.defaults.xAxisTextPadding + 20);
     w += this.drawingContext.measureText(`${maxVolumeInWindowData}`).width;
 
+    let latested = this.windowedData[this.windowedData.length - 1]
     if (this.windowedData.length > 0) {
       this.drawingContext.fillStyle = this.defaults.textOnBackground;
       this.drawingContext.font = '12px Arial';
       this.drawingContext.fillText(`
-        最后交易价格:  ${this.windowedData[this.windowedData.length - 1].marketDepth.lastTradedPrice}
-        最后交易数量:  ${this.windowedData[this.windowedData.length - 1].marketDepth.lastTradedQty}
-        最后交易时间:  ${this.windowedData[this.windowedData.length - 1].ts}
+        最后交易价格:  ${latested.marketDepth.side === 'buy' ? latested.marketDepth.lastBuyPrice : latested.marketDepth.lastSellPrice}
+        最后交易数量:  ${latested.marketDepth.side === 'buy' ? latested.marketDepth.lastBuyQty : latested.marketDepth.lastSellQty}
+        最后交易时间:  ${latested.ts}
         `, 20 + w + 40, this.defaults.axisTickSize + this.defaults.xAxisTextPadding + 20);
     }
     this.drawingContext.fillStyle = this.defaults.textOnBackground;
@@ -664,40 +665,80 @@ export default class StockHeatmap extends React.Component {
       this.circles = []
       this.windowedData.map(d => {
         const marketDepth = d.marketDepth;
-        if (+marketDepth.lastTradedQty === 0) {
-          return
-        }
-
+        const maxBidAskVol = extractMaxVolume(d);
+        const ts = d.ts;
         const ask1 = marketDepth.sells[0];
         const bid1 = marketDepth.buys[0];
-        const ts = d.ts;
-        const maxBidAskVol = extractMaxVolume(d);
 
-        let trade_color = d3.color(marketDepth.side == 'sell' ? '#cc5040' : '#44c98b').rgb();
-        trade_color.opacity = 0.7;
-        this.drawingContext.fillStyle = trade_color.toString();
-        const r = 50 - 45 * (2.71 ** (-0.01 * (+marketDepth.lastTradedQty / avgTradedVolume)))
-        this.drawingContext.beginPath();
-        this.drawingContext.arc(
-          this.xScale(ts) + xh2,
-          this.yScale(+marketDepth.lastTradedPrice) + yh2,
-          r, 0, 2 * Math.PI
-        );
-        this.drawingContext.strokeStyle = trade_color;
-        this.drawingContext.fill();
-
-        // 为球添加白色边框
-        this.drawingContext.lineWidth = 1; // 设置线宽,可以根据需要调整
-        this.drawingContext.strokeStyle = 'white'; // 设置描边颜色为白色
-        this.drawingContext.stroke(); // 执行描边操作
-
-        // 事件
-        this.circles.push({
-          x: this.xScale(ts) + xh2,
-          y: this.yScale(+marketDepth.lastTradedPrice) + yh2,
-          radius: r,
-          data: d  // 存储与圆相关的数据,便于在事件处理时使用
-        });
+        // 绘制买入的圆圈
+        if (marketDepth.lastBuyQty !== 0) {
+          let trade_color = d3.color("#44c98b").rgb();
+          trade_color.opacity = 0.7;
+          this.drawingContext.fillStyle = trade_color.toString();
+          const r = 50 - 45 * (2.71 ** (-0.01 * (+marketDepth.lastBuyQty / avgTradedVolume)));
+          this.drawingContext.beginPath();
+          this.drawingContext.arc(
+            this.xScale(ts) + xh2,
+            this.yScale(+marketDepth.lastBuyPrice) + yh2,
+            r,
+            0,
+            2 * Math.PI
+          );
+          this.drawingContext.strokeStyle = trade_color;
+          this.drawingContext.fill();
+
+          // 为球添加白色边框
+          this.drawingContext.lineWidth = 1; // 设置线宽,可以根据需要调整
+          this.drawingContext.strokeStyle = "white"; // 设置描边颜色为白色
+          this.drawingContext.stroke(); // 执行描边操作
+
+          // 事件触发用
+          let d_copy = JSON.parse(JSON.stringify(d))
+          // 只保存需要展示的那边
+          d_copy.marketDepth.lastSellQty = 0
+          d_copy.marketDepth.lastSellPrice = 0
+          this.circles.push({
+            x: this.xScale(ts) + xh2,
+            y: this.yScale(+marketDepth.lastBuyPrice) + yh2,
+            radius: r,
+            data: d_copy  // 存储与圆相关的数据,便于在事件处理时使用
+          });
+        }
+
+        // 绘制卖出的圆圈
+        if (marketDepth.lastSellQty !== 0) {
+          let trade_color = d3.color("#cc5040").rgb();
+          trade_color.opacity = 0.7;
+          this.drawingContext.fillStyle = trade_color.toString();
+          const r = 50 - 45 * (2.71 ** (-0.01 * (+marketDepth.lastSellQty / avgTradedVolume)));
+          this.drawingContext.beginPath();
+          this.drawingContext.arc(
+            this.xScale(ts) + xh2,
+            this.yScale(+marketDepth.lastSellPrice) + yh2,
+            r,
+            0,
+            2 * Math.PI
+          );
+          this.drawingContext.strokeStyle = trade_color;
+          this.drawingContext.fill();
+
+          // 为球添加白色边框
+          this.drawingContext.lineWidth = 1; // 设置线宽,可以根据需要调整
+          this.drawingContext.strokeStyle = "white"; // 设置描边颜色为白色
+          this.drawingContext.stroke(); // 执行描边操作
+
+          // 事件触发用
+          let d_copy = JSON.parse(JSON.stringify(d))
+          // 只保存需要展示的那边
+          d_copy.marketDepth.lastBuyQty = 0
+          d_copy.marketDepth.lastBuyPrice = 0
+          this.circles.push({
+            x: this.xScale(ts) + xh2,
+            y: this.yScale(+marketDepth.lastSellPrice) + yh2,
+            radius: r,
+            data: d_copy  // 存储与圆相关的数据,便于在事件处理时使用
+          });
+        }
       })
 
       // draw buy line path
@@ -820,8 +861,10 @@ export default class StockHeatmap extends React.Component {
         avgPrice: 0,
         buyOrderVolume: 0,
         sellOrderVolume: 0,
-        lastTradedPrice: 0,
-        lastTradedQty: 0,
+        lastBuyPrice: 0,
+        lastBuyQty: 0,
+        lastSellPrice: 0,
+        lastSellQty: 0,
         lastTradedTS: 0,
         open: 0,
         high: 0,
@@ -882,11 +925,8 @@ export default class StockHeatmap extends React.Component {
       });
 
       // 合并最后交易的数量和价格
-      if (snapshot.marketDepth.side === 'buy') {
-        totalLastTradeQtyBuy += snapshot.marketDepth.lastTradedQty;
-      } else if (snapshot.marketDepth.side === 'sell') {
-        totalLastTradeQtySell += snapshot.marketDepth.lastTradedQty;
-      }
+      totalLastTradeQtyBuy += snapshot.marketDepth.lastBuyQty;
+      totalLastTradeQtySell += snapshot.marketDepth.lastSellQty;
 
       // 合并其他字段
       merged.marketDepth.close = snapshot.marketDepth.close;
@@ -916,10 +956,11 @@ export default class StockHeatmap extends React.Component {
     }));
 
     // 计算最终的最后交易的数量和价格
-    let lastTradeQtyDiff = totalLastTradeQtyBuy - totalLastTradeQtySell;
-    merged.marketDepth.lastTradedQty = Math.abs(lastTradeQtyDiff);
-    merged.marketDepth.side = lastTradeQtyDiff > 0 ? 'buy' : 'sell';
-    merged.marketDepth.lastTradedPrice = lastTradeQtyDiff > 0 ? merged.marketDepth.sells[0].rate : merged.marketDepth.buys[0].rate;
+    merged.marketDepth.lastBuyPrice = merged.marketDepth.sells[0].rate;
+    merged.marketDepth.lastBuyQty = totalLastTradeQtyBuy;
+    merged.marketDepth.lastSellPrice = merged.marketDepth.buys[0].rate;
+    merged.marketDepth.lastSellQty = totalLastTradeQtySell;
+    merged.marketDepth.side = 'both';
 
     return merged;
   }
@@ -964,7 +1005,7 @@ export default class StockHeatmap extends React.Component {
       // move position only if within valid range
       this.windowedData = this.data.slice(position, position + this.windowLength + 1);
 
-      if (this.windowedData.length > 1000) {
+      if (this.windowedData.length > 200) {
         this.windowedData = this.mergeWindowedData();
         this.isMerged = true;
       } else {

+ 2 - 2
src/utils.js

@@ -9,7 +9,7 @@ export const extractBidPrices = (data) => {
     if (marketDepth) {
       let buys = marketDepth.buys.map(b => +b.rate);
       let sells = marketDepth.sells.map(b => +b.rate);
-      return buys.concat(sells, [+marketDepth.lastTradedPrice]);
+      return buys.concat(sells, [marketDepth.lastBuyQty !== 0 ? +marketDepth.lastBuyPrice : +marketDepth.lastSellPrice]);
     }
     return [];
   }).reduce((acc, val) => acc.concat(val), []));
@@ -80,7 +80,7 @@ export const extractMaxAskBidVolume = (data) => {
 
 export const extractAvgTradedVolume = (data) => {
   let vols = data.map(d => {
-    if (d.marketDepth) return +d.marketDepth.lastTradedQty;
+    if (d.marketDepth) return +d.marketDepth.lastSellQty + +d.marketDepth.lastBuyQty;
     else return 0;
   });
   if (vols.length > 0) return vols.reduce((acc, curr) => acc + curr, 0) / vols.length;

+ 4 - 2
types/index.d.ts

@@ -60,8 +60,10 @@ export interface StockHeatmapOptions {
 export interface StockData {
   /** Market depth data at this data point */
   marketDepth: {
-    lastTradedPrice: number,
-    lastTradedQty: number,
+    lastBuyPrice: number,
+    lastBuyQty: number,
+    lastSellPrice: number,
+    lastSellQty: number,
     priceChangeAmt: number,
     priceChangePct: number,
     lastTradedTS: number,