diff --git a/src/pages/visualizer/Chart.tsx b/src/pages/visualizer/Chart.tsx
index 8652a6a..3d8b1df 100644
--- a/src/pages/visualizer/Chart.tsx
+++ b/src/pages/visualizer/Chart.tsx
@@ -41,15 +41,16 @@ function getThemeOptions(theme: (highcharts: typeof Highcharts) => void): Highch
interface ChartProps {
title: string;
+ options?: Highcharts.Options;
series: Highcharts.SeriesOptionsType[];
min?: number;
max?: number;
}
-export function Chart({ title, series, min, max }: ChartProps): ReactNode {
+export function Chart({ title, options, series, min, max }: ChartProps): ReactNode {
const colorScheme = useActualColorScheme();
- const options = useMemo((): Highcharts.Options => {
+ const fullOptions = useMemo((): Highcharts.Options => {
const themeOptions = colorScheme === 'light' ? {} : getThemeOptions(HighchartsHighContrastDarkTheme);
const chartOptions: Highcharts.Options = {
@@ -148,14 +149,15 @@ export function Chart({ title, series, min, max }: ChartProps): ReactNode {
enabled: false,
},
series,
+ ...options,
};
return merge(themeOptions, chartOptions);
- }, [colorScheme, title, series, min, max]);
+ }, [colorScheme, title, options, series, min, max]);
return (
-
+
);
}
diff --git a/src/pages/visualizer/ConversionObservationsTable.tsx b/src/pages/visualizer/ConversionObservationsTable.tsx
index 03b92f9..bf7d843 100644
--- a/src/pages/visualizer/ConversionObservationsTable.tsx
+++ b/src/pages/visualizer/ConversionObservationsTable.tsx
@@ -14,13 +14,13 @@ export function ConversionObservationsTable({ conversionObservations }: Conversi
rows.push(
{product}
- {formatNumber(observation.bidPrice)}
- {formatNumber(observation.askPrice)}
- {formatNumber(observation.transportFees)}
- {formatNumber(observation.exportTariff)}
- {formatNumber(observation.importTariff)}
- {formatNumber(observation.sunlight)}
- {formatNumber(observation.humidity)}
+ {formatNumber(observation.bidPrice, 2)}
+ {formatNumber(observation.askPrice, 2)}
+ {formatNumber(observation.transportFees, 2)}
+ {formatNumber(observation.exportTariff, 2)}
+ {formatNumber(observation.importTariff, 2)}
+ {formatNumber(observation.sunlight, 2)}
+ {formatNumber(observation.humidity, 2)}
,
);
}
diff --git a/src/pages/visualizer/ConversionPriceChart.tsx b/src/pages/visualizer/ConversionPriceChart.tsx
new file mode 100644
index 0000000..f7ac6ef
--- /dev/null
+++ b/src/pages/visualizer/ConversionPriceChart.tsx
@@ -0,0 +1,41 @@
+import Highcharts from 'highcharts';
+import { ReactNode } from 'react';
+import { ProsperitySymbol } from '../../models.ts';
+import { useStore } from '../../store.ts';
+import { getAskColor, getBidColor } from '../../utils/colors.ts';
+import { Chart } from './Chart.tsx';
+
+export interface ConversionPriceChartProps {
+ symbol: ProsperitySymbol;
+}
+
+export function ConversionPriceChart({ symbol }: ConversionPriceChartProps): ReactNode {
+ const algorithm = useStore(state => state.algorithm)!;
+
+ const bidPriceData = [];
+ const askPriceData = [];
+
+ for (const row of algorithm.data) {
+ const observation = row.state.observations.conversionObservations[symbol];
+ if (observation === undefined) {
+ continue;
+ }
+
+ bidPriceData.push([row.state.timestamp, observation.bidPrice]);
+ askPriceData.push([row.state.timestamp, observation.askPrice]);
+ }
+
+ const options: Highcharts.Options = {
+ yAxis: {
+ opposite: true,
+ allowDecimals: true,
+ },
+ };
+
+ const series: Highcharts.SeriesOptionsType[] = [
+ { type: 'line', name: 'Bid', color: getBidColor(1.0), marker: { symbol: 'triangle' }, data: bidPriceData },
+ { type: 'line', name: 'Ask', color: getAskColor(1.0), marker: { symbol: 'triangle-down' }, data: askPriceData },
+ ];
+
+ return ;
+}
diff --git a/src/pages/visualizer/EnvironmentChart.tsx b/src/pages/visualizer/EnvironmentChart.tsx
new file mode 100644
index 0000000..41b34a2
--- /dev/null
+++ b/src/pages/visualizer/EnvironmentChart.tsx
@@ -0,0 +1,37 @@
+import Highcharts from 'highcharts';
+import { ReactNode } from 'react';
+import { ProsperitySymbol } from '../../models.ts';
+import { useStore } from '../../store.ts';
+import { Chart } from './Chart.tsx';
+
+export interface EnvironmentChartProps {
+ symbol: ProsperitySymbol;
+}
+
+export function EnvironmentChart({ symbol }: EnvironmentChartProps): ReactNode {
+ const algorithm = useStore(state => state.algorithm)!;
+
+ const sunlightData = [];
+ const humidityData = [];
+
+ for (const row of algorithm.data) {
+ const observation = row.state.observations.conversionObservations[symbol];
+ if (observation === undefined) {
+ continue;
+ }
+
+ sunlightData.push([row.state.timestamp, observation.sunlight]);
+ humidityData.push([row.state.timestamp, observation.humidity]);
+ }
+
+ const series: Highcharts.SeriesOptionsType[] = [
+ { type: 'line', name: 'Sunlight', marker: { symbol: 'square' }, yAxis: 0, data: sunlightData },
+ { type: 'line', name: 'Humidity', marker: { symbol: 'circle' }, yAxis: 1, data: humidityData },
+ ];
+
+ const options: Highcharts.Options = {
+ yAxis: [{}, { opposite: true }],
+ };
+
+ return ;
+}
diff --git a/src/pages/visualizer/PositionChart.tsx b/src/pages/visualizer/PositionChart.tsx
index a67703f..e613ce0 100644
--- a/src/pages/visualizer/PositionChart.tsx
+++ b/src/pages/visualizer/PositionChart.tsx
@@ -8,6 +8,7 @@ function getLimit(algorithm: Algorithm, symbol: ProsperitySymbol): number {
const knownLimits: Record = {
AMETHYSTS: 20,
STARFRUIT: 20,
+ ORCHIDS: 100,
};
if (knownLimits[symbol] !== undefined) {
diff --git a/src/pages/visualizer/PriceChart.tsx b/src/pages/visualizer/ProductPriceChart.tsx
similarity index 93%
rename from src/pages/visualizer/PriceChart.tsx
rename to src/pages/visualizer/ProductPriceChart.tsx
index 4914226..6cc6340 100644
--- a/src/pages/visualizer/PriceChart.tsx
+++ b/src/pages/visualizer/ProductPriceChart.tsx
@@ -5,11 +5,11 @@ import { useStore } from '../../store.ts';
import { getAskColor, getBidColor } from '../../utils/colors.ts';
import { Chart } from './Chart.tsx';
-export interface PriceChartProps {
+export interface ProductPriceChartProps {
symbol: ProsperitySymbol;
}
-export function PriceChart({ symbol }: PriceChartProps): ReactNode {
+export function ProductPriceChart({ symbol }: ProductPriceChartProps): ReactNode {
const algorithm = useStore(state => state.algorithm)!;
const series: Highcharts.SeriesOptionsType[] = [
diff --git a/src/pages/visualizer/TransportChart.tsx b/src/pages/visualizer/TransportChart.tsx
new file mode 100644
index 0000000..c42635c
--- /dev/null
+++ b/src/pages/visualizer/TransportChart.tsx
@@ -0,0 +1,36 @@
+import Highcharts from 'highcharts';
+import { ReactNode } from 'react';
+import { ProsperitySymbol } from '../../models.ts';
+import { useStore } from '../../store.ts';
+import { Chart } from './Chart.tsx';
+
+export interface TransportChartProps {
+ symbol: ProsperitySymbol;
+}
+
+export function TransportChart({ symbol }: TransportChartProps): ReactNode {
+ const algorithm = useStore(state => state.algorithm)!;
+
+ const transportFeesData = [];
+ const importTariffData = [];
+ const exportTariffData = [];
+
+ for (const row of algorithm.data) {
+ const observation = row.state.observations.conversionObservations[symbol];
+ if (observation === undefined) {
+ continue;
+ }
+
+ transportFeesData.push([row.state.timestamp, observation.transportFees]);
+ importTariffData.push([row.state.timestamp, observation.importTariff]);
+ exportTariffData.push([row.state.timestamp, observation.exportTariff]);
+ }
+
+ const series: Highcharts.SeriesOptionsType[] = [
+ { type: 'line', name: 'Transport fees', data: transportFeesData },
+ { type: 'line', name: 'Import tariff', marker: { symbol: 'triangle' }, data: importTariffData },
+ { type: 'line', name: 'Export tariff', marker: { symbol: 'triangle-down' }, data: exportTariffData },
+ ];
+
+ return ;
+}
diff --git a/src/pages/visualizer/VisualizerPage.tsx b/src/pages/visualizer/VisualizerPage.tsx
index 2c3f5c5..681af7b 100644
--- a/src/pages/visualizer/VisualizerPage.tsx
+++ b/src/pages/visualizer/VisualizerPage.tsx
@@ -4,12 +4,15 @@ import { Navigate, useLocation } from 'react-router-dom';
import { useStore } from '../../store.ts';
import { formatNumber } from '../../utils/format.ts';
import { AlgorithmSummaryCard } from './AlgorithmSummaryCard.tsx';
+import { EnvironmentChart } from './EnvironmentChart.tsx';
import { PositionChart } from './PositionChart.tsx';
-import { PriceChart } from './PriceChart.tsx';
+import { ProductPriceChart } from './ProductPriceChart.tsx';
import { ProfitLossChart } from './ProfitLossChart.tsx';
import { TimestampsCard } from './TimestampsCard.tsx';
+import { TransportChart } from './TransportChart.tsx';
import { VisualizerCard } from './VisualizerCard.tsx';
import { VolumeChart } from './VolumeChart.tsx';
+import { ConversionPriceChart } from './ConversionPriceChart.tsx';
export function VisualizerPage(): ReactNode {
const algorithm = useStore(state => state.algorithm);
@@ -20,6 +23,13 @@ export function VisualizerPage(): ReactNode {
return ;
}
+ const conversionProducts = new Set();
+ for (const row of algorithm.data) {
+ for (const product of Object.keys(row.state.observations.conversionObservations)) {
+ conversionProducts.add(product);
+ }
+ }
+
let profitLoss = 0;
const lastTimestamp = algorithm.activityLogs[algorithm.activityLogs.length - 1].timestamp;
for (let i = algorithm.activityLogs.length - 1; i >= 0 && algorithm.activityLogs[i].timestamp == lastTimestamp; i--) {
@@ -29,18 +39,42 @@ export function VisualizerPage(): ReactNode {
const symbolColumns: ReactNode[] = [];
Object.keys(algorithm.data[0].state.listings)
.sort((a, b) => a.localeCompare(b))
- .forEach((symbol, i) => {
+ .forEach(symbol => {
symbolColumns.push(
-
-
+
+
,
);
symbolColumns.push(
-
+
,
);
+
+ if (!conversionProducts.has(symbol)) {
+ return;
+ }
+
+ symbolColumns.push(
+
+
+ ,
+ );
+
+ symbolColumns.push(
+
+
+ ,
+ );
+
+ symbolColumns.push(
+
+
+ ,
+ );
+
+ symbolColumns.push();
});
return (