Kaynağa Gözat

Add few more metrics to X axis and corner.

rongmz 5 yıl önce
ebeveyn
işleme
fd8f88d4d6
6 değiştirilmiş dosya ile 68 ekleme ve 63 silme
  1. 22 0
      .npmignore
  2. 0 43
      package-lock.json
  3. 2 3
      package.json
  4. 39 12
      src/index.js
  5. 4 3
      src/utils.js
  6. 1 2
      types/index.d.ts

+ 22 - 0
.npmignore

@@ -0,0 +1,22 @@
+
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
+node_modules
+
+example
+
+# builds
+.rpt2_cache
+
+# misc
+.DS_Store
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*

+ 0 - 43
package-lock.json

@@ -5145,20 +5145,6 @@
       "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz",
       "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ=="
     },
-    "d3-dispatch": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-2.0.0.tgz",
-      "integrity": "sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA=="
-    },
-    "d3-drag": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-2.0.0.tgz",
-      "integrity": "sha512-g9y9WbMnF5uqB9qKqwIIa/921RYWzlUDv9Jl1/yONQwxbOfszAWTCm8u7HOTgJgRDXiRZN56cHT9pd24dmXs8w==",
-      "requires": {
-        "d3-dispatch": "1 - 2",
-        "d3-selection": "2"
-      }
-    },
     "d3-ease": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-2.0.0.tgz",
@@ -5194,11 +5180,6 @@
         "d3-time-format": "2 - 3"
       }
     },
-    "d3-selection": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz",
-      "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA=="
-    },
     "d3-shape": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.0.0.tgz",
@@ -5225,30 +5206,6 @@
       "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-2.0.0.tgz",
       "integrity": "sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA=="
     },
-    "d3-transition": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-2.0.0.tgz",
-      "integrity": "sha512-42ltAGgJesfQE3u9LuuBHNbGrI/AJjNL2OAUdclE70UE6Vy239GCBEYD38uBPoLeNsOhFStGpPI0BAOV+HMxog==",
-      "requires": {
-        "d3-color": "1 - 2",
-        "d3-dispatch": "1 - 2",
-        "d3-ease": "1 - 2",
-        "d3-interpolate": "1 - 2",
-        "d3-timer": "1 - 2"
-      }
-    },
-    "d3-zoom": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-2.0.0.tgz",
-      "integrity": "sha512-fFg7aoaEm9/jf+qfstak0IYpnesZLiMX6GZvXtUSdv8RH2o4E2qeelgdU09eKS6wGuiGMfcnMI0nTIqWzRHGpw==",
-      "requires": {
-        "d3-dispatch": "1 - 2",
-        "d3-drag": "2",
-        "d3-interpolate": "1 - 2",
-        "d3-selection": "2",
-        "d3-transition": "2"
-      }
-    },
     "damerau-levenshtein": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",

+ 2 - 3
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@rongmz/react-stock-heatmap",
-  "version": "1.0.0",
+  "version": "1.0.1",
   "description": "This is a react chart library for genarating "Stock Heatmap" with given market depth data. Stock Heatmap graph useful for day traders.",
   "author": "Rounak Saha",
   "license": "MIT",
@@ -75,7 +75,6 @@
     "d3-interpolate": "^2.0.1",
     "d3-scale": "^3.2.2",
     "d3-shape": "^2.0.0",
-    "d3-timer": "^2.0.0",
-    "d3-zoom": "^2.0.0"
+    "d3-timer": "^2.0.0"
   }
 }

+ 39 - 12
src/index.js

@@ -5,7 +5,6 @@ import * as d3Color from 'd3-color';
 import * as d3Format from 'd3-format';
 import * as d3Interpolate from 'd3-interpolate';
 import * as d3Shape from 'd3-shape';
-import * as d3Zoom from 'd3-zoom';
 import * as d3Timer from 'd3-timer';
 import * as d3Ease from 'd3-ease';
 import { extractBidPrices, extractBidVolumes, extractMaxTradedVolume, extractMaxVolume, zoomTimeFormat } from './utils';
@@ -15,7 +14,7 @@ export const d3 = Object.assign(
     Object.assign({}, d3Scale, d3Array, d3Color)
     , d3Format, d3Interpolate, d3Shape
   )
-  , d3Zoom, d3Ease, d3Timer
+  , d3Ease, d3Timer
 );
 
 /**
@@ -48,6 +47,7 @@ export default class StockHeatmap extends React.Component {
     sellColor: '#d32f2f',
     textOnSellColor: '#ffffff',
     textOnBackground: '#000000',
+    textHighlightOnBackground: '#ff0000',
     tradeColor: '#7434eb',
     axisTickSize: 6,
     axisColor: '#000000',
@@ -55,6 +55,8 @@ export default class StockHeatmap extends React.Component {
     yAxisTextPadding: 6,
     bidAskGraphPaddingLeft: 10,
     bidAskTransitionDuration: 500,
+    volumeCircleMaxRadius: 10,
+    runningRatioSeconds: 5,
     hmWidth: () => (this.props.width - this.defaults.borderPadding[1] - this.defaults.borderPadding[3] - this.defaults.bidAskWidth - this.defaults.axisYWidth),
     hmHeight: () => (this.props.height - this.defaults.borderPadding[0] - this.defaults.borderPadding[2] - this.defaults.axisXHeight),
     clearColor: '#ffffff',
@@ -265,13 +267,26 @@ export default class StockHeatmap extends React.Component {
       const w = this.props.width - x;
       const h = this.props.height - y;
       this.clearCanvas(x, y, w, h, this.defaults.clearColor);
-      let textHeight = (h - 15) / 2;
+      let textHeight = (h - 10) / 2;
       this.drawingContext.save();
       this.drawingContext.textAlign = 'center';
       this.drawingContext.textBaseline = 'middle';
       this.drawingContext.font = `bold ${textHeight}px sans-serif`;
-      this.drawingContext.fillText((d.marketDepth.buyOrderVolume / d.marketDepth.sellOrderVolume).toFixed(2), x + w / 2, y + textHeight / 2);
-      this.drawingContext.fillText('Buy/Sell', x + w / 2, y + textHeight * 1.5 + 5);
+      this.drawingContext.fillText((d.marketDepth.buyOrderVolume / d.marketDepth.sellOrderVolume).toFixed(2), x + w *3/4, y + textHeight / 2);
+      // Runing average ratio
+      if(this.windowedData.length >= this.defaults.runningRatioSeconds) {
+        let sellT20RunningSum = 0;
+        let buyT20RunningSum = 0;
+        for (let i = this.windowedData.length - 1; i >= this.windowedData.length - this.defaults.runningRatioSeconds; i--) {
+          sellT20RunningSum += (this.windowedData[i].marketDepth.sells || []).reduce((vol, s) => vol + s.qty,0);
+          buyT20RunningSum += (this.windowedData[i].marketDepth.buys || []).reduce((vol, s) => vol + s.qty,0);
+        }
+        const newBSTPFactor = (buyT20RunningSum / sellT20RunningSum);
+        this.drawingContext.fillText(newBSTPFactor.toFixed(2), x + w /4, y + textHeight *0.5);
+      }
+      this.drawingContext.font = `bold ${13}px sans-serif`;
+      this.drawingContext.textBaseline = 'bottom';
+      this.drawingContext.fillText('Buy/Sell', x + w / 2, y + textHeight * 2 + 5);
       this.drawingContext.restore();
     }
   }
@@ -297,19 +312,31 @@ export default class StockHeatmap extends React.Component {
     const bandInterval = parseInt(assumedTextWidth / (this.xScale?.bandwidth() || 1)) || 1;
     // console.log('bandInterval=', bandInterval);
     this.windowedData.map((d, i) => {
-      let x = this.xScale(d.ts);
-      this.drawingContext.moveTo(x, 0);
-      this.drawingContext.lineTo(x, this.defaults.axisTickSize);
-      if (i % bandInterval === 0)
+      if (i % bandInterval === 0) {
+        let x = this.xScale(d.ts);
+        this.drawingContext.moveTo(x, 0);
+        this.drawingContext.lineTo(x, this.defaults.axisTickSize);
         this.drawingContext.fillText(d.ts, x, this.defaults.axisTickSize + this.defaults.xAxisTextPadding);
+      }
     });
     this.drawingContext.textAlign = 'left';
+    this.drawingContext.font = '12px Arial';
     this.drawingContext.fillText(`Zoom Level:  ${zoomTimeFormat(this.windowLength)}`, 20, this.defaults.axisTickSize + this.defaults.xAxisTextPadding + 20);
     let w = this.drawingContext.measureText(`Zoom Level:  ${zoomTimeFormat(this.windowLength)}`).width;
-    if (this.windowedData.length > 0)
+    const maxVolumeInWindowData = extractMaxTradedVolume(this.windowedData);
+    this.drawingContext.fillText(`Max Volume in ${zoomTimeFormat(this.windowLength, 1)}:  `, 20 + w + 20, this.defaults.axisTickSize + this.defaults.xAxisTextPadding + 20);
+    this.drawingContext.fillStyle = this.defaults.textHighlightOnBackground;
+    w += this.drawingContext.measureText(`Max Volume in ${zoomTimeFormat(this.windowLength, 1)}:  `).width;
+    this.drawingContext.font = 'bold 12px Arial';
+    this.drawingContext.fillText(`${maxVolumeInWindowData}`, 20 + w + 20, this.defaults.axisTickSize + this.defaults.xAxisTextPadding + 20);
+    w += this.drawingContext.measureText(`${maxVolumeInWindowData}`).width;
+    if (this.windowedData.length > 0) {
+      this.drawingContext.fillStyle = this.defaults.textOnBackground;
       this.drawingContext.fillText(`LTP:  ${this.windowedData[this.windowedData.length - 1].marketDepth.lastTradedPrice
         }     LTQ:  ${this.windowedData[this.windowedData.length - 1].marketDepth.lastTradedQty
-        }`, 20 + w + 20, this.defaults.axisTickSize + this.defaults.xAxisTextPadding + 20);
+        }`, 20 + w + 40, this.defaults.axisTickSize + this.defaults.xAxisTextPadding + 20);
+    }
+    this.drawingContext.fillStyle = this.defaults.textOnBackground;
     this.drawingContext.lineWidth = 1.2;
     this.drawingContext.strokeStyle = this.defaults.axisColor;
     this.drawingContext.stroke();
@@ -460,7 +487,7 @@ export default class StockHeatmap extends React.Component {
         color.opacity = 1;
         this.drawingContext.lineWidth = 1;
         this.drawingContext.fillStyle = color.toString();
-        const r = xh2 * (+marketDepth.lastTradedQty / maxTradedVolume);
+        const r = /*xh2*/ this.defaults.volumeCircleMaxRadius * (+marketDepth.lastTradedQty / maxTradedVolume);
         this.drawingContext.beginPath();
         this.drawingContext.arc(
           this.xScale(ts) /* + xh2*/,

+ 4 - 3
src/utils.js

@@ -60,15 +60,16 @@ export const extractMaxTradedVolume = (data) => {
  * Format zoom scale time
  * @param {number} seconds 
  */
-export const zoomTimeFormat = (seconds) => {
+export const zoomTimeFormat = (seconds, decimal) => {
+  if(!decimal) decimal = 2;
   if(seconds > 59) {
     if(seconds > 3599) {
       let hrs = seconds/3600;
-      return `${hrs.toFixed(2)} hour${hrs>1?'s':''}`;
+      return `${hrs.toFixed(decimal)} hour${hrs>1?'s':''}`;
     } 
     else {
       let mins = seconds/60;
-      return `${mins.toFixed(2)} minute${mins>1?'s':''}`;
+      return `${mins.toFixed(decimal)} minute${mins>1?'s':''}`;
     }
   }
   else return `${seconds} second${seconds>1?'s':''}`;

+ 1 - 2
types/index.d.ts

@@ -5,11 +5,10 @@ import * as d3Color from 'd3-color';
 import * as d3Format from 'd3-format';
 import * as d3Interpolate from 'd3-interpolate';
 import * as d3Shape from 'd3-shape';
-import * as d3Zoom from 'd3-zoom';
 import * as d3Timer from 'd3-timer';
 import * as d3Ease from 'd3-ease';
 
-export const d3: typeof d3Scale & typeof d3Array & typeof d3Color & typeof d3Format & typeof d3Interpolate & typeof d3Shape & typeof d3Zoom & typeof d3Ease & typeof d3Timer;
+export const d3: typeof d3Scale & typeof d3Array & typeof d3Color & typeof d3Format & typeof d3Interpolate & typeof d3Shape & typeof d3Ease & typeof d3Timer;
 /**
  * Stock Heatmap
  * @author Rounak Saha