diff --git a/package-lock.json b/package-lock.json
index de08d99..aa053fc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4311,6 +4311,32 @@
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
"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": {
"version": "0.22.0",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
@@ -12566,6 +12592,15 @@
"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": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.2.tgz",
diff --git a/package.json b/package.json
index 7545483..53be587 100644
--- a/package.json
+++ b/package.json
@@ -19,12 +19,14 @@
"bn.js": "^5.1.3",
"bs58": "^4.0.1",
"buffer-layout": "^1.2.0",
+ "chart.js": "^2.9.4",
"craco-less": "^1.17.0",
"echarts": "^4.9.0",
"eventemitter3": "^4.0.7",
"identicon.js": "^2.3.3",
"jazzicon": "^1.5.0",
"react": "^16.13.1",
+ "react-chartjs-2": "^2.11.1",
"react-dom": "^16.13.1",
"react-github-btn": "^1.2.0",
"react-intl": "^5.10.2",
diff --git a/src/views/marginTrading/newPosition/Breakdown.tsx b/src/views/marginTrading/newPosition/Breakdown.tsx
index a497cf7..c3a8b30 100644
--- a/src/views/marginTrading/newPosition/Breakdown.tsx
+++ b/src/views/marginTrading/newPosition/Breakdown.tsx
@@ -3,6 +3,7 @@ import React, { useState } from 'react';
import { Position } from './interfaces';
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
import tokens from '../../../config/tokens.json';
+import GainsChart from './GainsChart';
export function Breakdown({ item }: { item: Position }) {
let myPart = parseFloat(item.asset?.value || '0') / item.leverage;
@@ -89,6 +90,7 @@ export function Breakdown({ item }: { item: Position }) {
/>
+
{progressBar}
);
diff --git a/src/views/marginTrading/newPosition/GainsChart.tsx b/src/views/marginTrading/newPosition/GainsChart.tsx
new file mode 100644
index 0000000..f98f463
--- /dev/null
+++ b/src/views/marginTrading/newPosition/GainsChart.tsx
@@ -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;
+}) {
+ 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();
+ useEffect(() => {
+ if (chartRef.current.chartInstance) updateChartData({ item, priceChange, chartRef });
+ }, [priceChange, item.leverage]);
+
+ return useMemo(
+ () => (
+ {
+ 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,
+ },
+ }}
+ />
+ ),
+ []
+ );
+}