Update trade visualization in chart

This commit is contained in:
Matthias 2022-04-25 17:50:04 +02:00
parent e87e073357
commit 743d5e17bc
2 changed files with 86 additions and 99 deletions

View File

@ -5,10 +5,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, computed, onMounted, watch } from '@vue/composition-api';
import { Trade, PairHistory, PlotConfig } from '@/types'; import { Trade, PairHistory, PlotConfig } from '@/types';
import randomColor from '@/shared/randomColor'; import randomColor from '@/shared/randomColor';
import { roundTimeframe } from '@/shared/timemath';
import heikinashi from '@/shared/heikinashi'; import heikinashi from '@/shared/heikinashi';
import { getTradeEntries } from '@/shared/charts/tradeChartData';
import ECharts from 'vue-echarts'; import ECharts from 'vue-echarts';
import { use } from 'echarts/core'; import { use } from 'echarts/core';
@ -63,14 +64,10 @@ const upBorderColor = '#26A69A';
const downColor = '#EF5350'; const downColor = '#EF5350';
const downBorderColor = '#EF5350'; const downBorderColor = '#EF5350';
const buySignalColor = '#00ff26'; const buySignalColor = '#31e04b';
const shortEntrySignalColor = '#00ff26'; const shortEntrySignalColor = '#bf8613';
const sellSignalColor = '#faba25'; const sellSignalColor = '#faba25';
const shortexitSignalColor = '#faba25'; const shortexitSignalColor = '#faba25';
const tradeBuyColor = 'cyan';
const tradeSellColor = 'pink';
import { defineComponent, ref, computed, onMounted, watch } from '@vue/composition-api';
export default defineComponent({ export default defineComponent({
name: 'CandleChart', name: 'CandleChart',
@ -101,10 +98,6 @@ export default defineComponent({
return props.dataset ? props.dataset.timeframe : ''; return props.dataset ? props.dataset.timeframe : '';
}); });
const timeframems = computed(() => {
return props.dataset ? props.dataset.timeframe_ms : 0;
});
const datasetColumns = computed(() => { const datasetColumns = computed(() => {
return props.dataset ? props.dataset.columns : []; return props.dataset ? props.dataset.columns : [];
}); });
@ -121,41 +114,6 @@ export default defineComponent({
return `${strategy.value} - ${pair.value} - ${timeframe.value}`; return `${strategy.value} - ${pair.value} - ${timeframe.value}`;
}); });
/** Return trade entries for charting */
const getTradeEntries = () => {
const tradeEntries: number[][] = [];
const tradesClose: number[][] = [];
for (let i = 0, len = filteredTrades.value.length; i < len; i += 1) {
const trade: Trade = filteredTrades.value[i];
if (
trade.open_timestamp >= props.dataset.data_start_ts &&
trade.open_timestamp <= props.dataset.data_stop_ts
) {
tradeEntries.push([
roundTimeframe(timeframems.value, trade.open_timestamp),
trade.open_rate,
trade.profit_abs,
trade.is_short ? 1 : 0,
]);
}
if (
trade.close_timestamp !== undefined &&
trade.close_timestamp < props.dataset.data_stop_ts &&
trade.close_timestamp > props.dataset.data_start_ts
) {
if (trade.close_date !== undefined && trade.close_rate !== undefined) {
tradesClose.push([
roundTimeframe(timeframems.value, trade.close_timestamp),
trade.close_rate,
trade.profit_abs,
trade.is_short ? 1 : 0,
]);
}
}
}
return { tradeEntries, tradesClose };
};
const updateChart = (initial = false) => { const updateChart = (initial = false) => {
if (!hasData.value) { if (!hasData.value) {
return; return;
@ -316,19 +274,16 @@ export default defineComponent({
if (hasShorts) { if (hasShorts) {
// Add short support // Add short support
if (!Array.isArray(chartOptions.value?.legend) && chartOptions.value?.legend?.data) { if (!Array.isArray(chartOptions.value?.legend) && chartOptions.value?.legend?.data) {
if (colShortEntryData >= 0) { // chartOptions.value.legend.data.push('Short');
chartOptions.value.legend.data.push('Short'); chartOptions.value.legend.data.push('Short exit');
}
if (colShortExitData >= 0) {
chartOptions.value.legend.data.push('Short exit');
}
} }
if (Array.isArray(options.series)) { if (Array.isArray(options.series)) {
if (colShortEntryData >= 0) { if (colShortEntryData >= 0) {
options.series.push({ options.series.push({
name: 'Short', name: 'Long',
type: 'scatter', type: 'scatter',
symbol: 'pin', symbol: 'triangle',
symbolRotate: 180,
symbolSize: 10, symbolSize: 10,
xAxisIndex: 0, xAxisIndex: 0,
yAxisIndex: 0, yAxisIndex: 0,
@ -494,64 +449,39 @@ export default defineComponent({
chartOptions.value.grid[chartOptions.value.grid.length - 1].bottom = '50px'; chartOptions.value.grid[chartOptions.value.grid.length - 1].bottom = '50px';
delete chartOptions.value.grid[chartOptions.value.grid.length - 1].top; delete chartOptions.value.grid[chartOptions.value.grid.length - 1].top;
} }
const { tradeEntries, tradesClose } = getTradeEntries(); const { tradeData } = getTradeEntries(props.dataset, filteredTrades.value);
const name = 'Trades';
const nameClose = 'Trades Close'; const nameTrades = 'Trades';
if (!Array.isArray(chartOptions.value.legend) && chartOptions.value.legend?.data) { if (!Array.isArray(chartOptions.value.legend) && chartOptions.value.legend?.data) {
chartOptions.value.legend.data.push(name); chartOptions.value.legend.data.push(nameTrades);
} }
const sp: ScatterSeriesOption = { if (!Array.isArray(chartOptions.value.legend) && chartOptions.value.legend?.data) {
name, chartOptions.value.legend.data.push(nameTrades);
type: 'scatter', }
xAxisIndex: 0, const tradesSeries: ScatterSeriesOption = {
yAxisIndex: 0, name: nameTrades,
encode: {
x: 0,
y: 1,
},
itemStyle: {
color: tradeBuyColor,
},
symbol: (v, params) => {
// TODO: finish me
// console.log('v', v, params);
// 1 == short, 0 == long
return v[3] === 0 ? 'circle' : 'pin';
}, // Symbol per long/short wins??
data: tradeEntries,
};
if (Array.isArray(chartOptions.value?.series)) {
chartOptions.value.series.push(sp);
}
if (!Array.isArray(chartOptions.value.legend) && chartOptions.value.legend?.data) {
chartOptions.value.legend.data.push(nameClose);
}
const closeSeries: ScatterSeriesOption = {
name: nameClose,
type: 'scatter', type: 'scatter',
xAxisIndex: 0, xAxisIndex: 0,
yAxisIndex: 0, yAxisIndex: 0,
encode: { encode: {
x: 0, x: 0,
y: 1, y: 1,
label: 5,
tooltip: 6,
}, },
label: { show: true, fontSize: 10, color: props.theme === 'dark' ? '#fff' : '#000' },
labelLayout: { rotate: 90, align: 'left', dx: 10 },
itemStyle: { itemStyle: {
// color: tradeSellColor, // color: tradeSellColor,
color: (v) => { color: (v) => v.data[4],
// TODO: finish me
// console.log('vc', v);
return v.data[2] > 0 ? 'green' : tradeSellColor;
},
}, },
symbol: (v, params) => { symbol: (v) => v[2],
// TODO: finish me symbolRotate: (v) => v[3],
// console.log('v', v, params); symbolSize: 13,
return v[2] > 0 ? 'rect' : 'triangleup'; data: tradeData,
}, // Symbol per long/short wins??
data: tradesClose,
}; };
if (chartOptions.value.series && Array.isArray(chartOptions.value.series)) { if (chartOptions.value.series && Array.isArray(chartOptions.value.series)) {
chartOptions.value.series.push(closeSeries); chartOptions.value.series.push(tradesSeries);
} }
console.log('chartOptions', chartOptions.value); console.log('chartOptions', chartOptions.value);
@ -745,7 +675,6 @@ export default defineComponent({
strategy, strategy,
pair, pair,
timeframe, timeframe,
timeframems,
datasetColumns, datasetColumns,
hasData, hasData,
filteredTrades, filteredTrades,

View File

@ -0,0 +1,58 @@
import { formatPercent } from '@/shared/formatters';
import { roundTimeframe } from '@/shared/timemath';
import { PairHistory, Trade } from '@/types';
/** Return trade entries for charting */
export function getTradeEntries(dataset: PairHistory, filteredTrades: Trade[]) {
const tradeData: (number | string)[][] = [];
// Return schema:
// 0: Timeframe
// 1: rate
// 2: symbol
// 3: symbol rotate
// 4: color
// 5: label
// 6: tooltip
for (let i = 0, len = filteredTrades.length; i < len; i += 1) {
const trade: Trade = filteredTrades[i];
if (
trade.open_timestamp >= dataset.data_start_ts &&
trade.open_timestamp <= dataset.data_stop_ts
) {
tradeData.push([
roundTimeframe(dataset.timeframe_ms ?? 0, trade.open_timestamp),
trade.open_rate,
'triangle', // TODO: use better symbol
trade.is_short ? 180 : 0,
trade.is_short ? '#ffaf0d' : '#00ff26',
'',
// trade.profit_abs,
`${trade.is_short ? 'short' : 'long'} entry ${formatPercent(trade.profit_ratio)} ${
trade.enter_tag ?? ''
}`,
]);
}
if (
trade.close_timestamp !== undefined &&
trade.close_timestamp < dataset.data_stop_ts &&
trade.close_timestamp > dataset.data_start_ts
) {
if (trade.close_date !== undefined && trade.close_rate !== undefined) {
tradeData.push([
roundTimeframe(dataset.timeframe_ms ?? 0, trade.close_timestamp),
trade.close_rate,
'rect',
0,
trade.is_short ? '#ffaf0d' : '#00ff26',
// 'blue',
formatPercent(trade.profit_ratio, 2),
`${trade.is_short ? 'short' : 'long'} exit ${formatPercent(trade.profit_ratio)}% ${
trade.enter_tag ?? ''
}`,
]);
}
}
}
return { tradeData };
}