Convert CAndleStickChart to typescript

This commit is contained in:
Matthias 2020-06-23 21:48:37 +02:00
parent 0d1ab76ba0
commit 6912fb4905
2 changed files with 322 additions and 334 deletions

View File

@ -6,365 +6,349 @@
</div>
</template>
<script>
import { timestampms } from '@/shared/formatters';
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
import ECharts from 'vue-echarts';
import { timestampms } from '../../shared/formatters';
import { PlotConfig } from '../../store/types';
import randomColor from '../../shared/randomColor';
import 'echarts';
const MARGINLEFT = '5%';
const MARGINRIGHT = '4%';
export default {
name: 'CandleChart',
@Component({
components: { 'v-chart': ECharts },
props: {
pair: {
type: String,
required: true,
},
timeframe: {
type: String,
required: true,
},
dataset: {
type: Object,
required: true,
},
plotConfig: {
type: Object,
required: false,
default() {
return {};
},
},
},
})
export default class CandleChart extends Vue {
@Prop({ required: true }) readonly pair: string = '';
data() {
return {};
},
computed: {
hasData() {
return this.dataset !== null && typeof this.dataset === 'object';
},
@Prop({ required: true }) readonly timeframe: string = '';
chartOptions() {
if (!this.hasData) {
return {};
}
const upColor = '#00da3c';
const upBorderColor = '#008F28';
const downColor = '#ec0000';
const downBorderColor = '#8A0000';
console.log(`Available Columns: ${this.dataset.columns}`);
// Find default columns (sequence might be different, depending on the strategy)
const colDate = this.dataset.columns.findIndex((el) => el === 'date');
const colOpen = this.dataset.columns.findIndex((el) => el === 'open');
const colHigh = this.dataset.columns.findIndex((el) => el === 'high');
const colLow = this.dataset.columns.findIndex((el) => el === 'low');
const colClose = this.dataset.columns.findIndex((el) => el === 'close');
const colVolume = this.dataset.columns.findIndex((el) => el === 'volume');
const colBuy = this.dataset.columns.findIndex((el) => el === 'buy');
const colSell = this.dataset.columns.findIndex((el) => el === 'sell');
// Plot data
const buyData = [];
const sellData = [];
@Prop({ required: true }) readonly dataset: Record<string, any> = {};
const subPlots = {
legend: [],
grid: [],
yaxis: [],
xaxis: [],
xaxisIndex: [],
series: [],
};
@Prop({ required: false }) readonly plotConfig!: PlotConfig;
if ('main_plot' in this.plotConfig) {
Object.entries(this.plotConfig.main_plot).forEach(([key, value]) => {
const col = this.dataset.columns.findIndex((el) => el === key);
subPlots.legend.push(key);
const sp = {
name: key,
type: 'line',
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
color: value.color,
},
encode: {
x: colDate,
y: col,
},
showSymbol: false,
};
subPlots.series.push(sp);
});
}
get hasData() {
return this.dataset !== null && typeof this.dataset === 'object';
}
if ('subplots' in this.plotConfig) {
let plotIndex = 2;
Object.entries(this.plotConfig.subplots).forEach(([key, value]) => {
// define yaxis
subPlots.yaxis.push({
scale: true,
gridIndex: plotIndex,
name: key,
nameLocation: 'middle',
nameGap: 60,
axisLabel: { show: true },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { show: false },
});
subPlots.xaxis.push({
scale: true,
gridIndex: plotIndex,
boundaryGap: false,
axisLine: { onZero: false },
axisTick: { show: false },
splitLine: { show: false },
axisLabel: { show: false },
splitNumber: 20,
});
subPlots.xaxisIndex.push(plotIndex);
subPlots.grid.push({
left: MARGINLEFT,
right: MARGINRIGHT,
bottom: `${(plotIndex - 1) * 50}px`,
height: '8%',
});
Object.entries(value).forEach(([sk, sv]) => {
subPlots.legend.push(sk);
// entries per subplot
const col = this.dataset.columns.findIndex((el) => el === sk);
if (col) {
const sp = {
name: sk,
type: 'line',
xAxisIndex: plotIndex,
yAxisIndex: plotIndex,
itemStyle: {
color: sv.color || randomColor(),
},
encode: {
x: colDate,
y: col,
},
showSymbol: false,
};
subPlots.series.push(sp);
console.log(subPlots);
} else {
console.log(`element ${sk} was not found in the columns.`);
}
});
get chartOptions() {
if (!this.hasData) {
return {};
}
const upColor = '#00da3c';
const upBorderColor = '#008F28';
const downColor = '#ec0000';
const downBorderColor = '#8A0000';
console.log(`Available Columns: ${this.dataset.columns}`);
// Find default columns (sequence might be different, depending on the strategy)
const colDate = this.dataset.columns.findIndex((el) => el === 'date');
const colOpen = this.dataset.columns.findIndex((el) => el === 'open');
const colHigh = this.dataset.columns.findIndex((el) => el === 'high');
const colLow = this.dataset.columns.findIndex((el) => el === 'low');
const colClose = this.dataset.columns.findIndex((el) => el === 'close');
const colVolume = this.dataset.columns.findIndex((el) => el === 'volume');
const colBuy = this.dataset.columns.findIndex((el) => el === 'buy');
const colSell = this.dataset.columns.findIndex((el) => el === 'sell');
// Plot data
const buyData = [] as Array<number>[];
const sellData = [] as Array<number>[];
plotIndex += 1;
});
}
// Generate Buy and sell array (using open rate to display marker)
for (let i = 0, len = this.dataset.data.length; i < len; i += 1) {
if (this.dataset.data[i][colBuy] === 1) {
buyData.push([this.dataset.data[i][colDate], this.dataset.data[i][colOpen]]);
}
if (this.dataset.data[i][colSell] === 1) {
sellData.push([this.dataset.data[i][colDate], this.dataset.data[i][colOpen]]);
}
}
// console.log(this.dataset.data);
const subPlots = {
legend: [] as string[],
grid: [] as object[],
yaxis: [] as object[],
xaxis: [] as object[],
xaxisIndex: [] as number[],
series: [] as object[],
};
return {
title: {
text: `${this.pair} - ${this.timeframe}`,
show: true,
},
backgroundColor: '#231202D',
dataset: {
source: this.dataset.data,
},
animation: false,
legend: {
data: ['Candles', 'Volume', 'Buy', 'Sell', ...subPlots.legend],
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
lineStyle: {
color: '#cccccc',
width: 1,
opacity: 1,
},
if ('main_plot' in this.plotConfig) {
Object.entries(this.plotConfig.main_plot).forEach(([key, value]) => {
const col = this.dataset.columns.findIndex((el) => el === key);
subPlots.legend.push(key);
const sp = {
name: key,
type: 'line',
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
color: value.color,
},
},
axisPointer: {
link: { xAxisIndex: 'all' },
label: {
backgroundColor: '#777',
encode: {
x: colDate,
y: col,
},
},
xAxis: [
{
type: 'category',
scale: true,
boundaryGap: false,
axisLine: { onZero: false },
splitLine: { show: false },
splitNumber: 20,
min: 'dataMin',
max: 'dataMax',
axisLabel: {
formatter: (value) => {
return timestampms(value);
showSymbol: false,
};
subPlots.series.push(sp);
});
}
if ('subplots' in this.plotConfig) {
let plotIndex = 2;
Object.entries(this.plotConfig.subplots).forEach(([key, value]) => {
// define yaxis
subPlots.yaxis.push({
scale: true,
gridIndex: plotIndex,
name: key,
nameLocation: 'middle',
nameGap: 60,
axisLabel: { show: true },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { show: false },
});
subPlots.xaxis.push({
scale: true,
gridIndex: plotIndex,
boundaryGap: false,
axisLine: { onZero: false },
axisTick: { show: false },
splitLine: { show: false },
axisLabel: { show: false },
splitNumber: 20,
});
subPlots.xaxisIndex.push(plotIndex);
subPlots.grid.push({
left: MARGINLEFT,
right: MARGINRIGHT,
bottom: `${(plotIndex - 1) * 50}px`,
height: '8%',
});
Object.entries(value).forEach(([sk, sv]) => {
subPlots.legend.push(sk);
// entries per subplot
const col = this.dataset.columns.findIndex((el) => el === sk);
if (col) {
const sp = {
name: sk,
type: 'line',
xAxisIndex: plotIndex,
yAxisIndex: plotIndex,
itemStyle: {
color: sv.color || randomColor(),
},
encode: {
x: colDate,
y: col,
},
showSymbol: false,
};
subPlots.series.push(sp);
console.log(subPlots);
} else {
console.log(`element ${sk} was not found in the columns.`);
}
});
plotIndex += 1;
});
}
// Generate Buy and sell array (using open rate to display marker)
for (let i = 0, len = this.dataset.data.length; i < len; i += 1) {
if (this.dataset.data[i][colBuy] === 1) {
buyData.push([this.dataset.data[i][colDate], this.dataset.data[i][colOpen]]);
}
if (this.dataset.data[i][colSell] === 1) {
sellData.push([this.dataset.data[i][colDate], this.dataset.data[i][colOpen]]);
}
}
// console.log(this.dataset.data);
return {
title: {
text: `${this.pair} - ${this.timeframe}`,
show: true,
},
backgroundColor: '#231202D',
dataset: {
source: this.dataset.data,
},
animation: false,
legend: {
data: ['Candles', 'Volume', 'Buy', 'Sell', ...subPlots.legend],
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
lineStyle: {
color: '#cccccc',
width: 1,
opacity: 1,
},
},
},
axisPointer: {
link: { xAxisIndex: 'all' },
label: {
backgroundColor: '#777',
},
},
xAxis: [
{
type: 'category',
scale: true,
boundaryGap: false,
axisLine: { onZero: false },
splitLine: { show: false },
splitNumber: 20,
min: 'dataMin',
max: 'dataMax',
axisLabel: {
formatter: (value) => {
return timestampms(value);
},
},
{
type: 'category',
gridIndex: 1,
scale: true,
boundaryGap: false,
axisLine: { onZero: false },
axisTick: { show: false },
splitLine: { show: false },
axisLabel: { show: false },
splitNumber: 20,
min: 'dataMin',
max: 'dataMax',
},
{
type: 'category',
gridIndex: 1,
scale: true,
boundaryGap: false,
axisLine: { onZero: false },
axisTick: { show: false },
splitLine: { show: false },
axisLabel: { show: false },
splitNumber: 20,
min: 'dataMin',
max: 'dataMax',
},
...subPlots.xaxis,
],
yAxis: [
{
scale: true,
},
{
scale: true,
gridIndex: 1,
splitNumber: 2,
axisLabel: { show: false },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { show: false },
},
...subPlots.yaxis,
],
grid: [
{
left: MARGINLEFT,
right: MARGINRIGHT,
height: '60%',
// top: '0px',
// bottom: '150px',
},
{
// Volume
left: MARGINLEFT,
right: MARGINRIGHT,
bottom: '20%',
height: '80px',
},
...subPlots.grid,
],
dataZoom: [
{
type: 'inside',
xAxisIndex: [0, 1, ...subPlots.xaxisIndex],
start: 50,
end: 100,
},
{
show: true,
xAxisIndex: [0, 1, ...subPlots.xaxisIndex],
type: 'slider',
bottom: 10,
start: 10,
end: 100,
},
],
// visualMap: {
// // TODO: this would allow to colorize volume bars (if we'd want this)
// // Needs green / red indicator column in data.
// show: true,
// seriesIndex: 1,
// dimension: 5,
// pieces: [
// {
// max: 500000.0,
// color: downColor,
// },
// {
// min: 500000.0,
// color: upColor,
// },
// ],
// },
series: [
{
name: 'Candles',
type: 'candlestick',
itemStyle: {
color: upColor,
color0: downColor,
borderColor: upBorderColor,
borderColor0: downBorderColor,
},
...subPlots.xaxis,
],
yAxis: [
{
scale: true,
encode: {
x: 0,
// open, close, low, high
y: [colOpen, colClose, colLow, colHigh],
},
{
scale: true,
gridIndex: 1,
splitNumber: 2,
axisLabel: { show: false },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { show: false },
},
{
name: 'Volume',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 1,
itemStyle: {
color: '#777777',
},
...subPlots.yaxis,
],
grid: [
{
left: MARGINLEFT,
right: MARGINRIGHT,
height: '60%',
// top: '0px',
// bottom: '150px',
large: true,
encode: {
x: 0,
y: colVolume,
},
{
// Volume
left: MARGINLEFT,
right: MARGINRIGHT,
bottom: '20%',
height: '80px',
},
{
name: 'Buy',
type: 'scatter',
symbol: 'triangle',
data: buyData,
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
color: '#00FF00',
},
...subPlots.grid,
],
dataZoom: [
{
type: 'inside',
xAxisIndex: [0, 1, ...subPlots.xaxisIndex],
start: 50,
end: 100,
encode: {
x: 0,
y: 1,
},
{
show: true,
xAxisIndex: [0, 1, ...subPlots.xaxisIndex],
type: 'slider',
bottom: 10,
start: 10,
end: 100,
},
{
name: 'Sell',
type: 'scatter',
data: sellData,
symbol: 'diamond',
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
color: '#FF0000',
},
],
// visualMap: {
// // TODO: this would allow to colorize volume bars (if we'd want this)
// // Needs green / red indicator column in data.
// show: true,
// seriesIndex: 1,
// dimension: 5,
// pieces: [
// {
// max: 500000.0,
// color: downColor,
// },
// {
// min: 500000.0,
// color: upColor,
// },
// ],
// },
series: [
{
name: 'Candles',
type: 'candlestick',
itemStyle: {
color: upColor,
color0: downColor,
borderColor: upBorderColor,
borderColor0: downBorderColor,
},
encode: {
x: 0,
// open, close, low, high
y: [colOpen, colClose, colLow, colHigh],
},
encode: {
x: 0,
y: 1,
},
{
name: 'Volume',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 1,
itemStyle: {
color: '#777777',
},
large: true,
encode: {
x: 0,
y: colVolume,
},
},
{
name: 'Buy',
type: 'scatter',
symbol: 'triangle',
data: buyData,
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
color: '#00FF00',
},
encode: {
x: 0,
y: 1,
},
},
{
name: 'Sell',
type: 'scatter',
data: sellData,
symbol: 'diamond',
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
color: '#FF0000',
},
encode: {
x: 0,
y: 1,
},
},
...subPlots.series,
],
};
},
},
};
},
...subPlots.series,
],
};
}
}
</script>
<style scoped>

View File

@ -144,7 +144,11 @@ export interface PairHistoryPayload {
limit: number;
}
export interface PlotConfig {
main_plot?: object;
subplots?: object;
export interface IndicatorConfig {
color?: string;
}
export interface PlotConfig {
main_plot: Record<string, IndicatorConfig>;
subplots: Record<string, Record<string, IndicatorConfig>>;
}