mirror of https://github.com/certusone/oyster.git
feat: Add in fulcrum feature and get it working, but now it's time to really start to make this page look good now that all the pieces are there.
This commit is contained in:
parent
bda95affc2
commit
3b3ad80dd0
|
@ -4311,6 +4311,32 @@
|
||||||
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
|
||||||
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
||||||
},
|
},
|
||||||
|
"chart.js": {
|
||||||
|
"version": "2.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz",
|
||||||
|
"integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==",
|
||||||
|
"requires": {
|
||||||
|
"chartjs-color": "^2.1.0",
|
||||||
|
"moment": "^2.10.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chartjs-color": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==",
|
||||||
|
"requires": {
|
||||||
|
"chartjs-color-string": "^0.6.0",
|
||||||
|
"color-convert": "^1.9.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chartjs-color-string": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==",
|
||||||
|
"requires": {
|
||||||
|
"color-name": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"cheerio": {
|
"cheerio": {
|
||||||
"version": "0.22.0",
|
"version": "0.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
|
||||||
|
@ -12566,6 +12592,15 @@
|
||||||
"whatwg-fetch": "^3.0.0"
|
"whatwg-fetch": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-chartjs-2": {
|
||||||
|
"version": "2.11.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-2.11.1.tgz",
|
||||||
|
"integrity": "sha512-G7cNq/n2Bkh/v4vcI+GKx7Q1xwZexKYhOSj2HmrFXlvNeaURWXun6KlOUpEQwi1cv9Tgs4H3kGywDWMrX2kxfA==",
|
||||||
|
"requires": {
|
||||||
|
"lodash": "^4.17.19",
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-copy-to-clipboard": {
|
"react-copy-to-clipboard": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.2.tgz",
|
||||||
|
|
|
@ -19,12 +19,14 @@
|
||||||
"bn.js": "^5.1.3",
|
"bn.js": "^5.1.3",
|
||||||
"bs58": "^4.0.1",
|
"bs58": "^4.0.1",
|
||||||
"buffer-layout": "^1.2.0",
|
"buffer-layout": "^1.2.0",
|
||||||
|
"chart.js": "^2.9.4",
|
||||||
"craco-less": "^1.17.0",
|
"craco-less": "^1.17.0",
|
||||||
"echarts": "^4.9.0",
|
"echarts": "^4.9.0",
|
||||||
"eventemitter3": "^4.0.7",
|
"eventemitter3": "^4.0.7",
|
||||||
"identicon.js": "^2.3.3",
|
"identicon.js": "^2.3.3",
|
||||||
"jazzicon": "^1.5.0",
|
"jazzicon": "^1.5.0",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
|
"react-chartjs-2": "^2.11.1",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-github-btn": "^1.2.0",
|
"react-github-btn": "^1.2.0",
|
||||||
"react-intl": "^5.10.2",
|
"react-intl": "^5.10.2",
|
||||||
|
|
|
@ -3,6 +3,7 @@ import React, { useState } from 'react';
|
||||||
import { Position } from './interfaces';
|
import { Position } from './interfaces';
|
||||||
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
|
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
|
||||||
import tokens from '../../../config/tokens.json';
|
import tokens from '../../../config/tokens.json';
|
||||||
|
import GainsChart from './GainsChart';
|
||||||
|
|
||||||
export function Breakdown({ item }: { item: Position }) {
|
export function Breakdown({ item }: { item: Position }) {
|
||||||
let myPart = parseFloat(item.asset?.value || '0') / item.leverage;
|
let myPart = parseFloat(item.asset?.value || '0') / item.leverage;
|
||||||
|
@ -89,6 +90,7 @@ export function Breakdown({ item }: { item: Position }) {
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
<GainsChart item={item} priceChange={myGain} />
|
||||||
{progressBar}
|
{progressBar}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { Line } from 'react-chartjs-2';
|
||||||
|
import { Position } from './interfaces';
|
||||||
|
|
||||||
|
// Special thanks to
|
||||||
|
// https://github.com/bZxNetwork/fulcrum_ui/blob/development/packages/fulcrum-website/assets/js/trading.js
|
||||||
|
// For the basis of this code - I copied it directly from there and then modified it for our needs.
|
||||||
|
// You guys are real heroes - that is beautifully done.
|
||||||
|
const baseData = [
|
||||||
|
{ x: 0, y: 65 },
|
||||||
|
{ x: 1, y: 80 },
|
||||||
|
{ x: 2, y: 60 },
|
||||||
|
{ x: 3, y: 30 },
|
||||||
|
{ x: 4, y: 20 },
|
||||||
|
{ x: 5, y: 35 },
|
||||||
|
{ x: 6, y: 25 },
|
||||||
|
{ x: 7, y: 40 },
|
||||||
|
{ x: 8, y: 36 },
|
||||||
|
{ x: 9, y: 34 },
|
||||||
|
{ x: 10, y: 50 },
|
||||||
|
{ x: 11, y: 33 },
|
||||||
|
{ x: 12, y: 37 },
|
||||||
|
{ x: 13, y: 45 },
|
||||||
|
{ x: 14, y: 35 },
|
||||||
|
{ x: 15, y: 37 },
|
||||||
|
{ x: 16, y: 50 },
|
||||||
|
{ x: 17, y: 43 },
|
||||||
|
{ x: 18, y: 50 },
|
||||||
|
{ x: 19, y: 45 },
|
||||||
|
{ x: 20, y: 55 },
|
||||||
|
{ x: 21, y: 50 },
|
||||||
|
{ x: 22, y: 45 },
|
||||||
|
{ x: 23, y: 50 },
|
||||||
|
{ x: 24, y: 45 },
|
||||||
|
{ x: 25, y: 40 },
|
||||||
|
{ x: 26, y: 35 },
|
||||||
|
{ x: 27, y: 40 },
|
||||||
|
{ x: 28, y: 37 },
|
||||||
|
{ x: 29, y: 45 },
|
||||||
|
{ x: 30, y: 50 },
|
||||||
|
{ x: 31, y: 60 },
|
||||||
|
{ x: 32, y: 55 },
|
||||||
|
{ x: 33, y: 50 },
|
||||||
|
{ x: 34, y: 53 },
|
||||||
|
{ x: 35, y: 55 },
|
||||||
|
{ x: 36, y: 50 },
|
||||||
|
{ x: 37, y: 45 },
|
||||||
|
{ x: 38, y: 40 },
|
||||||
|
{ x: 39, y: 45 },
|
||||||
|
{ x: 40, y: 50 },
|
||||||
|
{ x: 41, y: 55 },
|
||||||
|
{ x: 42, y: 65 },
|
||||||
|
{ x: 43, y: 62 },
|
||||||
|
{ x: 44, y: 54 },
|
||||||
|
{ x: 45, y: 65 },
|
||||||
|
{ x: 46, y: 48 },
|
||||||
|
{ x: 47, y: 55 },
|
||||||
|
{ x: 48, y: 60 },
|
||||||
|
{ x: 49, y: 63 },
|
||||||
|
{ x: 50, y: 65 },
|
||||||
|
];
|
||||||
|
|
||||||
|
function getChartData({ item, priceChange }: { item: Position; priceChange: number }) {
|
||||||
|
//the only way to create an immutable copy of array with objects inside.
|
||||||
|
const baseDashed = JSON.parse(JSON.stringify(baseData.slice(Math.floor(baseData.length) / 2)));
|
||||||
|
const baseSolid = JSON.parse(JSON.stringify(baseData.slice(0, Math.floor(baseData.length) / 2 + 1)));
|
||||||
|
|
||||||
|
const leverage = item.leverage;
|
||||||
|
|
||||||
|
baseDashed.forEach((item: { y: number; x: number }, index: number) => {
|
||||||
|
if (index !== 0) item.y += (item.y * priceChange) / 100;
|
||||||
|
});
|
||||||
|
var leverageData = baseDashed.map((item: { x: number; y: number }, index: number) => {
|
||||||
|
if (index === 0) {
|
||||||
|
return { x: item.x, y: item.y };
|
||||||
|
}
|
||||||
|
const gain = (priceChange * leverage) / 100;
|
||||||
|
return { x: item.x, y: item.y * (1 + gain) };
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderColor: 'rgb(39, 107, 251)',
|
||||||
|
borderWidth: 4,
|
||||||
|
radius: 0,
|
||||||
|
data: baseSolid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderColor: priceChange >= 0 ? 'rgb(51, 223, 204)' : 'rgb(255,79,79)',
|
||||||
|
borderWidth: 4,
|
||||||
|
radius: 0,
|
||||||
|
data: leverageData,
|
||||||
|
borderDash: [15, 3],
|
||||||
|
label: 'LEVERAGE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderColor: 'rgb(86, 169, 255)',
|
||||||
|
borderWidth: 2,
|
||||||
|
radius: 0,
|
||||||
|
data: baseDashed,
|
||||||
|
borderDash: [8, 4],
|
||||||
|
label: 'HOLD',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateChartData({
|
||||||
|
item,
|
||||||
|
priceChange,
|
||||||
|
chartRef,
|
||||||
|
}: {
|
||||||
|
item: Position;
|
||||||
|
priceChange: number;
|
||||||
|
chartRef: React.RefObject<any>;
|
||||||
|
}) {
|
||||||
|
const data = getChartData({ item, priceChange });
|
||||||
|
chartRef.current.chartInstance.data = data;
|
||||||
|
chartRef.current.chartInstance.canvas.parentNode.style.width = '100%';
|
||||||
|
chartRef.current.chartInstance.canvas.parentNode.style.height = 'auto';
|
||||||
|
chartRef.current.chartInstance.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawLabels(t: any, ctx: any, leverage: number, priceChange: number) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.font = 'normal normal bold 15px /1.5 Muli';
|
||||||
|
ctx.textBaseline = 'bottom';
|
||||||
|
|
||||||
|
const chartInstance = t.chart;
|
||||||
|
const datasets = chartInstance.config.data.datasets;
|
||||||
|
datasets.forEach(function (ds: { label: any; borderColor: any }, index: number) {
|
||||||
|
const label = ds.label;
|
||||||
|
ctx.fillStyle = ds.borderColor;
|
||||||
|
|
||||||
|
const meta = chartInstance.controller.getDatasetMeta(index);
|
||||||
|
const len = meta.data.length - 1;
|
||||||
|
const pointPostition = Math.floor(len / 2) - Math.floor(0.2 * len);
|
||||||
|
const x = meta.data[pointPostition]._model.x;
|
||||||
|
const xOffset = x;
|
||||||
|
const y = meta.data[pointPostition]._model.y;
|
||||||
|
let yOffset;
|
||||||
|
if (label === 'HOLD') {
|
||||||
|
yOffset = leverage * priceChange > 0 ? y * 1.2 : y * 0.8;
|
||||||
|
} else {
|
||||||
|
yOffset = leverage * priceChange > 0 ? y * 0.8 : y * 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yOffset > chartInstance.canvas.parentNode.offsetHeight) {
|
||||||
|
// yOffset = 295;
|
||||||
|
chartInstance.canvas.parentNode.style.height = `${yOffset * 1.3}px`;
|
||||||
|
}
|
||||||
|
if (yOffset < 0) yOffset = 5;
|
||||||
|
if (label) ctx.fillText(label, xOffset, yOffset);
|
||||||
|
});
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function GainsChart({ item, priceChange }: { item: Position; priceChange: number }) {
|
||||||
|
const chartRef = useRef<any>();
|
||||||
|
useEffect(() => {
|
||||||
|
if (chartRef.current.chartInstance) updateChartData({ item, priceChange, chartRef });
|
||||||
|
}, [priceChange, item.leverage]);
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => (
|
||||||
|
<Line
|
||||||
|
ref={chartRef}
|
||||||
|
data={(canvas: any) => {
|
||||||
|
const originalController = chartRef.current?.chartInstance?.controllers?.line;
|
||||||
|
if (originalController)
|
||||||
|
chartRef.current.chartInstance.controllers.line = chartRef.current.chartInstance.controllers.line.extend({
|
||||||
|
draw: function () {
|
||||||
|
originalController.prototype.draw.call(this, arguments);
|
||||||
|
drawLabels(this, canvas.getContext('2d'), item.leverage, priceChange);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return getChartData({ item, priceChange });
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: true,
|
||||||
|
scaleShowLabels: false,
|
||||||
|
layout: {
|
||||||
|
padding: {
|
||||||
|
top: 30,
|
||||||
|
bottom: 80,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
render: 'title',
|
||||||
|
fontColor: ['green', 'white', 'red'],
|
||||||
|
precision: 2,
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
easing: 'easeOutExpo',
|
||||||
|
duration: 500,
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [
|
||||||
|
{
|
||||||
|
display: false,
|
||||||
|
gridLines: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
type: 'linear',
|
||||||
|
position: 'bottom',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yAxes: [
|
||||||
|
{
|
||||||
|
display: false,
|
||||||
|
gridLines: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue