mirror of
https://github.com/freqtrade/frequi.git
synced 2024-11-23 03:25:15 +00:00
Convert CAndleStickChart to typescript
This commit is contained in:
parent
0d1ab76ba0
commit
6912fb4905
|
@ -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>
|
||||
|
|
|
@ -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>>;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user