diff --git a/src/lib/explorer/TradingDescription.svelte b/src/lib/explorer/TradingDescription.svelte
index bf07780ec..fcee437f8 100644
--- a/src/lib/explorer/TradingDescription.svelte
+++ b/src/lib/explorer/TradingDescription.svelte
@@ -7,7 +7,7 @@ primary label, a modifier (shown in lighter text), and a testing indicator
Used in DataTable context (vs. standard svelte component context).
#### Usage:
-```ts
+```tsx
table.column({
header: 'Position',
id: 'description',
diff --git a/src/lib/trade-executor/components/StrategyError.svelte b/src/lib/trade-executor/components/StrategyError.svelte
new file mode 100644
index 000000000..51706a672
--- /dev/null
+++ b/src/lib/trade-executor/components/StrategyError.svelte
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+{#if !strategy.connected}
+ Trade executor offline. Cannot display the strategy statistics.
+{:else if !strategy.executor_running}
+ Strategy execution is currently paused due to an error. The trade execution engine is waiting for a manual action.
+ {#if strategy.crashed_at}
+
+ Strategy executor halted {relative}.
+
+ {/if}
+ See instance status page for more information .
+{:else if strategy.frozen_positions > 0}
+ Strategy has currently frozen trading positions that require manual intervention.
+ See frozen positions page for more information.
+{/if}
diff --git a/src/lib/trade-executor/components/index.ts b/src/lib/trade-executor/components/index.ts
index 9cb883533..087677004 100644
--- a/src/lib/trade-executor/components/index.ts
+++ b/src/lib/trade-executor/components/index.ts
@@ -1,3 +1,5 @@
export { default as KeyMetric } from './KeyMetric.svelte';
export { default as KeyMetricDescription } from './KeyMetricDescription.svelte';
export { default as StrategyIcon } from './StrategyIcon.svelte';
+export { default as StrategyError } from './StrategyError.svelte';
+export * from './StrategyError.svelte';
diff --git a/src/lib/trade-executor/strategy/error.ts b/src/lib/trade-executor/strategy/error.ts
deleted file mode 100644
index 45cace6b6..000000000
--- a/src/lib/trade-executor/strategy/error.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Strategy error handling.
- *
- * There can be multiple failure modes for a trade executor
- *
- * - No connection - the webhook server is down / Internet issue
- *
- * - The trade executor has halted, the main loop aborted with an exception
- *
- * - The trade executor is not halted, but there is capital tied at
- * frozen positions that need manual interventino
- */
-
-import type { StrategyRuntimeState } from './runtime-state';
-import { fromUnixTime } from 'date-fns';
-import { formatDatetime, formatDuration } from '$lib/helpers/formatters';
-
-/**
- * Get the HTML error message and help link in the case a trade executor is facing an issue.
- */
-export function getTradeExecutorErrorHtml(state: StrategyRuntimeState): string | null {
- const tradeExecutorId = state.id;
-
- if (!state.connected) {
- return `Trade executor offline. Cannot display the strategy statistics.`;
- } else if (!state.executor_running) {
- // Add a timestamp and relative timestamp for quick diagnostics
- let crashedAtMsg;
- if (state.crashed_at) {
- const diffSeconds = Date.now() / 1000 - state.crashed_at;
- crashedAtMsg = `Strategy executor halted ${formatDatetime(fromUnixTime(state.crashed_at))}, ${formatDuration(
- diffSeconds
- )} ago.`;
- } else {
- crashedAtMsg = '';
- }
-
- return `Strategy execution is currently paused due to an error. The trade execution engine is waiting for a manual action.
- See instance status page for more information. ${crashedAtMsg}`;
- } else if (state.frozen_positions > 0) {
- return `Strategy has currently frozen trading positions that needing manual actions.
- See frozen positions page for more information.`;
- }
-
- return null;
-}
diff --git a/src/routes/strategies/StrategyTile.svelte b/src/routes/strategies/StrategyTile.svelte
index 35437e722..acb4250eb 100644
--- a/src/routes/strategies/StrategyTile.svelte
+++ b/src/routes/strategies/StrategyTile.svelte
@@ -5,13 +5,13 @@
import { utcDay } from 'd3-time';
import { normalizeDataForInterval } from '$lib/chart';
import { getChain } from '$lib/helpers/chain.js';
- import { Button, DataBadge, EntitySymbol, Tooltip } from '$lib/components';
- import { StrategyIcon } from 'trade-executor/components';
+ import { Button, DataBadge, Tooltip } from '$lib/components';
+ import { StrategyIcon, StrategyError, shouldDisplayError, adminOnlyError } from 'trade-executor/components';
import StrategyBadges from './StrategyBadges.svelte';
import ChartThumbnail from './ChartThumbnail.svelte';
import StrategyDataSummary from './StrategyDataSummary.svelte';
- import { getTradeExecutorErrorHtml } from 'trade-executor/strategy/error';
import { getLogoUrl } from '$lib/helpers/assets';
+ import Alert from '$lib/components/Alert.svelte';
export let admin = false;
export let simplified = false;
@@ -21,7 +21,6 @@
const chain = getChain(strategy.on_chain_data?.chain_id);
const href = `/strategies/${strategy.id}`;
- const errorHtml = getTradeExecutorErrorHtml(strategy);
const chartData = normalizeDataForInterval(
strategy.summary_statistics?.compounding_unrealised_trading_profitability ?? [],
@@ -53,10 +52,17 @@
{#if !simplified}
- {#if errorHtml}
+ {#if shouldDisplayError(strategy, admin)}
Error
- {@html errorHtml}
+
+ {#if adminOnlyError(strategy)}
+
+ This error is only displayed to admin users.
+
+ {/if}
+
+
{/if}
diff --git a/src/routes/strategies/[strategy]/+layout.svelte b/src/routes/strategies/[strategy]/+layout.svelte
index 3b5414f6e..6b7079da7 100644
--- a/src/routes/strategies/[strategy]/+layout.svelte
+++ b/src/routes/strategies/[strategy]/+layout.svelte
@@ -2,19 +2,18 @@
import { page } from '$app/stores';
import Breadcrumbs from '$lib/breadcrumb/Breadcrumbs.svelte';
import { AlertList, PageHeading } from '$lib/components';
- import { StrategyIcon } from 'trade-executor/components';
+ import { StrategyIcon, StrategyError, shouldDisplayError, adminOnlyError } from 'trade-executor/components';
import { menuOptions, default as StrategyNav } from './StrategyNav.svelte';
import StrategyBadges from '../StrategyBadges.svelte';
import { WalletWidget } from '$lib/wallet';
- import { getTradeExecutorErrorHtml } from 'trade-executor/strategy/error';
export let data;
- $: ({ strategy, deferred } = data);
+ $: ({ admin, strategy, deferred } = data);
$: isOverviewPage = $page.url.pathname.endsWith(strategy.id);
- $: errorHtml = getTradeExecutorErrorHtml(strategy);
+ $: hasStrategyError = shouldDisplayError(strategy, admin);
$: isOutdated = Boolean(strategy.new_version_id);
@@ -42,7 +41,7 @@
- {#if isOverviewPage && (errorHtml || isOutdated)}
+ {#if isOverviewPage && (hasStrategyError || isOutdated)}
@@ -51,8 +50,11 @@
participants should consider tranfering deposits to the latest version (though there is no guarantee of
better performance).
-
- {@html errorHtml}
+
+
+ {#if adminOnlyError(strategy)}
+ Note: this error is only displayed to admin users.
+ {/if}
@@ -82,6 +84,11 @@
transform: translate(0, -0.375em);
}
+ .admin-error {
+ margin-top: 1rem;
+ opacity: 0.5;
+ }
+
.subpage {
display: grid;
gap: var(--space-ls);