Skip to content

Commit

Permalink
convert performance chart data to standard time intervals
Browse files Browse the repository at this point in the history
  • Loading branch information
kenkunz committed Oct 24, 2023
1 parent fc0ab60 commit 64ce402
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 97 deletions.
63 changes: 8 additions & 55 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@testing-library/jest-dom": "^6.1.2",
"@testing-library/svelte": "^4.0.3",
"@testing-library/user-event": "^14.4.3",
"@types/d3-time": "^3.0.2",
"@types/plotly.js": "^2.12.21",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
Expand Down Expand Up @@ -61,8 +62,7 @@
"bignumber.js": "^9.1.2",
"cheerio": "^1.0.0-rc.12",
"cookie": "^0.5.0",
"d3-array": "^3.2.3",
"d3-scale": "^4.0.2",
"d3-time": "^3.1.0",
"date-fns": "^2.29.3",
"dd-trace": "^4.15.0",
"devalue": "^4.3.2",
Expand Down
8 changes: 7 additions & 1 deletion src/lib/chart/ChartIQ.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Dynamically ChartIQ modules (if available) and render chart element.
<script lang="ts">
import { fade } from 'svelte/transition';
import { lightFormat as formatDate } from 'date-fns';
import { floorUTCDate, addUTCDays } from '$lib/helpers/date';
import { type ChartLinker, type QuoteFeed, ChartActivityTracker } from '$lib/chart';
import { Alert } from '$lib/components';
import Spinner from 'svelte-spinner';
Expand Down Expand Up @@ -125,7 +126,12 @@ Dynamically ChartIQ modules (if available) and render chart element.
// ChartIQ doesn't preserve original UTC date; restore it from tz-adjusted DT value
if (data) {
data.originalDate = new Date(data.DT.getTime() - data.DT.getTimezoneOffset() * 60000);
const { timeUnit } = chartEngine.getPeriodicity();
if (timeUnit === 'day') {
data.adjustedDate = addUTCDays(floorUTCDate(data.DT), 1);
} else {
data.adjustedDate = new Date(data.DT);
}
}
cursor = { position, data };
Expand Down
12 changes: 3 additions & 9 deletions src/routes/strategies/ChartThumbnail.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,8 @@
update() {
chartEngine.loadChart('strategy-thumbnail', {
periodicity: { period: 1, timeUnit: 'day' },
masterData: data.map(([date, value]) => ({
// passing ISO date (w/out time) to prevent buggy ChartIQ tz conversions
DT: date.toISOString().slice(0, 10),
Value: value,
// add baseline data for drawing a simple 0 basline series (see above)
Baseline: 0
})),
span: { base: 'day', multiplier: 90 }
span: { base: 'day', multiplier: 90 },
masterData: data.map(([DT, Value]) => ({ DT, Value, Baseline: 0 }))
});
const { yAxis } = chartEngine.chart;
Expand All @@ -62,7 +56,7 @@
<Marker x={position.DateX} y={position.CloseY} size={4} />
<div class="chart-hover-info" style:--x="{position.cx}px" style:--y="{position.CloseY}px">
<UpDownCell value={data.Close - data.iqPrevClose}>
<Timestamp date={data.originalDate} />
<Timestamp date={data.adjustedDate} />
<div class="value">{formatPercent(data.Close, 2)}</div>
</UpDownCell>
</div>
Expand Down
13 changes: 1 addition & 12 deletions src/routes/strategies/[strategy]/(nav)/performance/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<script lang="ts">
import { getPortfolioLatestStats } from 'trade-executor/state/stats';
import SummaryStatistics from './SummaryStatistics.svelte';
import WebChart from '../../WebChart.svelte';
import PortfolioPerformanceChart from './PortfolioPerformanceChart.svelte';
export let data;
Expand All @@ -18,17 +17,7 @@

<section class="performance">
{#if profitabilityChart}
<!-- We cannot do fill here, because protability chart goes below zero -->
<WebChart
name="Profitability"
description="Compounded profitability of realised trading positions."
webChart={profitabilityChart}
yType="percent"
yAxisTitle="Realised profit"
fillMode="none"
/>

<PortfolioPerformanceChart data={profitabilityChart.data} />
<PortfolioPerformanceChart data={profitabilityChart.data} helpLink={profitabilityChart.help_link} />
{/if}

<SummaryStatistics {oldLatestStats} {summaryStatistics} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,58 @@ Render the portfolio performance chart using ChartIQ.
- Y-axis: portfolio value
-->
<script lang="ts">
import { utcHour, utcDay } from 'd3-time';
import { formatPercent } from '$lib/helpers/formatters';
import { determinePriceChangeClass } from '$lib/helpers/price';
import { SegmentedControl, Timestamp, UpDownCell } from '$lib/components';
import { ChartIQ, Marker } from '$lib/chart';
export let data: [number, number][];
export let helpLink: string;
type ChartTick = [Date, number];
const rawData: ChartTick[] = data.map(([ts, val]) => [new Date(ts * 1000), val]);
let chartWrapper: HTMLElement;
const timeSpanOptions = {
'1W': { hours: 1, spanDays: 7, withTime: true },
'1M': { hours: 4, spanDays: 30, withTime: true },
'3M': { hours: 24, spanDays: 90, withTime: false }
'1W': {
spanDays: 7,
interval: utcHour.every(1),
periodicity: { period: 1, interval: 60, timeUnit: 'minute' }
},
'1M': {
spanDays: 30,
interval: utcHour.every(4),
periodicity: { period: 1, interval: 4 * 60, timeUnit: 'minute' }
},
'3M': {
spanDays: 90,
interval: utcDay.every(1),
periodicity: { period: 1, interval: 1, timeUnit: 'day' }
}
} as const;
let timeSpan: keyof typeof timeSpanOptions = '3M';
$: ({ hours, spanDays, withTime } = timeSpanOptions[timeSpan]);
$: ({ spanDays, interval, periodicity } = timeSpanOptions[timeSpan]);
function getNormalizedIntervalData() {
return rawData.reduce((acc: ChartTick[], [date, value]) => {
const normalizedDate = interval!.floor(date);
const replace = Number(normalizedDate.valueOf() === acc.at(-1)?.[0].valueOf());
acc.splice(acc.length - replace, replace, [normalizedDate, value]);
return acc;
}, []);
}
const options = {
layout: { chartType: 'mountain' },
controls: { chartControls: null },
dontRoll: true,
chart: {
tension: 1,
xAxis: { displayGridLines: false },
yAxis: {
displayGridLines: false,
Expand Down Expand Up @@ -56,12 +84,9 @@ Render the portfolio performance chart using ChartIQ.
return {
update() {
chartEngine.loadChart('strategy-profitability', {
periodicity: { period: hours, interval: 60, timeUnit: 'minute' },
periodicity,
span: { base: 'day', multiplier: spanDays },
masterData: data.map(([ts, Value]) => ({
DT: ts * 1000,
Value
}))
masterData: getNormalizedIntervalData().map(([DT, Value]) => ({ DT, Value }))
});
}
};
Expand All @@ -70,18 +95,20 @@ Render the portfolio performance chart using ChartIQ.

<div class="portfolio-performance-chart">
<header>
<h2>Total Equity</h2>
<h2>Profitability</h2>
<SegmentedControl options={Object.keys(timeSpanOptions)} bind:selected={timeSpan} />
</header>
<p>Cash and market valued tokens in the strategy (USD)</p>
<p>
Compounded <a class="body-link" href={helpLink}>profitability</a> of realised trading positions.
</p>
<div bind:this={chartWrapper}>
<ChartIQ {init} {options} invalidate={[timeSpan]} let:cursor>
{@const { position, data } = cursor}
{#if data}
<Marker x={position.DateX} y={position.CloseY} size={4.5} />
<div class="chart-hover-info" style:--x="{position.cx}px" style:--y="{position.CloseY}px">
<UpDownCell value={data.Close - data.iqPrevClose}>
<Timestamp date={data.originalDate} {withTime} />
<Timestamp date={data.adjustedDate} withTime={periodicity.timeUnit === 'minute'} />
<div class="value">{formatPercent(data.Close, 2)}</div>
</UpDownCell>
</div>
Expand Down Expand Up @@ -120,15 +147,10 @@ Render the portfolio performance chart using ChartIQ.
h2 {
font: var(--f-heading-md-medium);
letter-spacing: var(--f-heading-md-spacing, normal);
@media (--viewport-sm-down) {
font: var(--f-heading-sm-medium);
letter-spacing: var(--f-heading-sm-spacing, normal);
}
}
p {
color: hsla(var(--hsl-text-extra-light));
color: hsla(var(--hsl-text-light));
font: var(--f-ui-md-medium);
letter-spacing: var(--f-ui-md-spacing, normal);
Expand Down

0 comments on commit 64ce402

Please sign in to comment.