|
@@ -368,8 +368,8 @@ export default class StockHeatmap extends React.Component {
|
|
|
// 绘制方块内数据
|
|
// 绘制方块内数据
|
|
|
drawPending.map((d, index) => {
|
|
drawPending.map((d, index) => {
|
|
|
let depth = d.marketDepth;
|
|
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(`${d.ts}`, x + 10, y + 7 + 15 + index * 45);
|
|
|
context.fillText(text, x + 10, y + 7 + 30 + index * 45);
|
|
context.fillText(text, x + 10, y + 7 + 30 + index * 45);
|
|
|
if (index < drawPending.length - 1) {
|
|
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);
|
|
this.drawingContext.fillText(`${maxVolumeInWindowData}`, 20 + w + 20, this.defaults.axisTickSize + this.defaults.xAxisTextPadding + 20);
|
|
|
w += this.drawingContext.measureText(`${maxVolumeInWindowData}`).width;
|
|
w += this.drawingContext.measureText(`${maxVolumeInWindowData}`).width;
|
|
|
|
|
|
|
|
|
|
+ let latested = this.windowedData[this.windowedData.length - 1]
|
|
|
if (this.windowedData.length > 0) {
|
|
if (this.windowedData.length > 0) {
|
|
|
this.drawingContext.fillStyle = this.defaults.textOnBackground;
|
|
this.drawingContext.fillStyle = this.defaults.textOnBackground;
|
|
|
this.drawingContext.font = '12px Arial';
|
|
this.drawingContext.font = '12px Arial';
|
|
|
this.drawingContext.fillText(`
|
|
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);
|
|
`, 20 + w + 40, this.defaults.axisTickSize + this.defaults.xAxisTextPadding + 20);
|
|
|
}
|
|
}
|
|
|
this.drawingContext.fillStyle = this.defaults.textOnBackground;
|
|
this.drawingContext.fillStyle = this.defaults.textOnBackground;
|
|
@@ -664,40 +665,80 @@ export default class StockHeatmap extends React.Component {
|
|
|
this.circles = []
|
|
this.circles = []
|
|
|
this.windowedData.map(d => {
|
|
this.windowedData.map(d => {
|
|
|
const marketDepth = d.marketDepth;
|
|
const marketDepth = d.marketDepth;
|
|
|
- if (+marketDepth.lastTradedQty === 0) {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const maxBidAskVol = extractMaxVolume(d);
|
|
|
|
|
+ const ts = d.ts;
|
|
|
const ask1 = marketDepth.sells[0];
|
|
const ask1 = marketDepth.sells[0];
|
|
|
const bid1 = marketDepth.buys[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
|
|
// draw buy line path
|
|
@@ -820,8 +861,10 @@ export default class StockHeatmap extends React.Component {
|
|
|
avgPrice: 0,
|
|
avgPrice: 0,
|
|
|
buyOrderVolume: 0,
|
|
buyOrderVolume: 0,
|
|
|
sellOrderVolume: 0,
|
|
sellOrderVolume: 0,
|
|
|
- lastTradedPrice: 0,
|
|
|
|
|
- lastTradedQty: 0,
|
|
|
|
|
|
|
+ lastBuyPrice: 0,
|
|
|
|
|
+ lastBuyQty: 0,
|
|
|
|
|
+ lastSellPrice: 0,
|
|
|
|
|
+ lastSellQty: 0,
|
|
|
lastTradedTS: 0,
|
|
lastTradedTS: 0,
|
|
|
open: 0,
|
|
open: 0,
|
|
|
high: 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;
|
|
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;
|
|
return merged;
|
|
|
}
|
|
}
|
|
@@ -964,7 +1005,7 @@ export default class StockHeatmap extends React.Component {
|
|
|
// move position only if within valid range
|
|
// move position only if within valid range
|
|
|
this.windowedData = this.data.slice(position, position + this.windowLength + 1);
|
|
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.windowedData = this.mergeWindowedData();
|
|
|
this.isMerged = true;
|
|
this.isMerged = true;
|
|
|
} else {
|
|
} else {
|