Update dashboard

This commit is contained in:
2025-07-08 13:36:48 +07:00
parent 2551dddc23
commit 7a74c44739
6 changed files with 124 additions and 93 deletions

View File

@@ -0,0 +1,80 @@
import React from 'react'
import type {Indicator, IndicatorType} from '../../../generated/ManagingApi'
import {SignalType} from '../../../generated/ManagingApi'
interface IndicatorsDisplayProps {
indicators: Indicator[]
className?: string
}
// Helper function to get indicator parameters from scenario indicators
const getIndicatorParameters = (indicator: Indicator) => {
if (!indicator) return null;
return {
name: indicator.name || 'Unnamed Indicator',
type: indicator.type,
signalType: indicator.signalType,
period: indicator.period,
fastPeriods: indicator.fastPeriods,
slowPeriods: indicator.slowPeriods,
signalPeriods: indicator.signalPeriods,
multiplier: indicator.multiplier,
smoothPeriods: indicator.smoothPeriods,
stochPeriods: indicator.stochPeriods,
cyclePeriods: indicator.cyclePeriods
};
};
// Helper function to get badge color based on signal type
const getBadgeColor = (signalType: SignalType) => {
switch (signalType) {
case SignalType.Signal:
return 'badge-primary';
case SignalType.Trend:
return 'badge-secondary';
case SignalType.Context:
return 'badge-accent';
default:
return 'badge-neutral';
}
};
// Helper function to format indicator type for display
const formatIndicatorType = (type: IndicatorType) => {
return type.replace(/([A-Z])/g, ' $1').trim();
};
const IndicatorsDisplay: React.FC<IndicatorsDisplayProps> = ({ indicators, className = "" }) => {
const processedIndicators = indicators.map(getIndicatorParameters).filter(Boolean);
return (
<div className={`flex flex-wrap gap-1 ${className}`}>
{processedIndicators.map((indicator: any, index: number) => {
if (!indicator) return null;
// Build tooltip content with indicator parameters
const tooltipContent = `
${indicator.period ? `Period: ${indicator.period}` : ''}
${indicator.fastPeriods ? `Fast: ${indicator.fastPeriods}` : ''}
${indicator.slowPeriods ? `Slow: ${indicator.slowPeriods}` : ''}
${indicator.signalPeriods ? `Signal: ${indicator.signalPeriods}` : ''}
${indicator.multiplier ? `Multiplier: ${indicator.multiplier}` : ''}
${indicator.smoothPeriods ? `Smooth: ${indicator.smoothPeriods}` : ''}
${indicator.stochPeriods ? `Stoch: ${indicator.stochPeriods}` : ''}
${indicator.cyclePeriods ? `Cycle: ${indicator.cyclePeriods}` : ''}
`.trim().replace(/\n\s+/g, '\n');
return (
<div key={index} className="tooltip" data-tip={tooltipContent}>
<div className={`badge ${getBadgeColor(indicator.signalType)} badge-sm`}>
{formatIndicatorType(indicator.type)}
</div>
</div>
);
})}
</div>
);
};
export default IndicatorsDisplay;

View File

@@ -13,3 +13,4 @@ export { default as GridTile } from './GridTile/GridTile'
export { default as SelectColumnFilter } from './Table/SelectColumnFilter' export { default as SelectColumnFilter } from './Table/SelectColumnFilter'
export { default as Card } from './Card/Card' export { default as Card } from './Card/Card'
export { default as ConfigDisplayModal } from './ConfigDisplayModal/ConfigDisplayModal' export { default as ConfigDisplayModal } from './ConfigDisplayModal/ConfigDisplayModal'
export { default as IndicatorsDisplay } from './IndicatorsDisplay/IndicatorsDisplay'

View File

@@ -3,15 +3,15 @@ import React, {useEffect, useState} from 'react'
import {Hub} from '../../../app/providers/Hubs' import {Hub} from '../../../app/providers/Hubs'
import useApiUrlStore from '../../../app/store/apiStore' import useApiUrlStore from '../../../app/store/apiStore'
import type {Account, TradingBot} from '../../../generated/ManagingApi' import type {Account, TradingBotResponse} from '../../../generated/ManagingApi'
import {AccountClient, BotClient, TradeDirection, TradeStatus,} from '../../../generated/ManagingApi' import {AccountClient, BotClient, TradeDirection, TradeStatus,} from '../../../generated/ManagingApi'
import {SelectColumnFilter, Table} from '../../mollecules' import {IndicatorsDisplay, SelectColumnFilter, Table} from '../../mollecules'
import StatusBadge from '../StatusBadge/StatusBadge' import StatusBadge from '../StatusBadge/StatusBadge'
import Summary from '../Trading/Summary' import Summary from '../Trading/Summary'
import BotRowDetails from './botRowDetails' import BotRowDetails from './botRowDetails'
export default function ActiveBots() { export default function ActiveBots() {
const [bots, setBots] = useState<TradingBot[]>([]) const [bots, setBots] = useState<TradingBotResponse[]>([])
const [accounts, setAccounts] = useState<Account[]>([]) const [accounts, setAccounts] = useState<Account[]>([])
const { apiUrl } = useApiUrlStore() const { apiUrl } = useApiUrlStore()
@@ -64,23 +64,31 @@ export default function ActiveBots() {
{ {
Filter: SelectColumnFilter, Filter: SelectColumnFilter,
Header: 'Ticker', Header: 'Ticker',
accessor: 'ticker', accessor: 'config.ticker',
disableSortBy: true, disableSortBy: true,
}, },
{ {
Header: 'Account', Header: 'Name',
accessor: 'accountName', accessor: 'config.name',
}, },
{ {
Filter: SelectColumnFilter, Filter: SelectColumnFilter,
Header: 'Timeframe', Header: 'Timeframe',
accessor: 'timeframe', accessor: 'config.timeframe',
disableSortBy: true, disableSortBy: true,
}, },
{ {
Filter: SelectColumnFilter, Header: 'Indicators',
Header: 'Scenario', accessor: 'config.scenario.indicators',
accessor: 'scenario', Cell: ({cell}: any) => {
const bot = cell.row.original as TradingBotResponse;
const indicators = bot.config?.scenario?.indicators || [];
return (
<IndicatorsDisplay indicators={indicators} />
);
},
disableFilters: true,
disableSortBy: true, disableSortBy: true,
}, },
{ {
@@ -161,12 +169,12 @@ export default function ActiveBots() {
const setupHubConnection = async () => { const setupHubConnection = async () => {
const hub = new Hub('bothub', apiUrl).hub const hub = new Hub('bothub', apiUrl).hub
hub.on('BotsSubscription', (data: TradingBot[]) => { hub.on('BotsSubscription', (data: TradingBotResponse[]) => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log( console.log(
'bot List', 'bot List',
bots.map((bot: TradingBot) => { bots.map((bot: TradingBotResponse) => {
return bot.name return bot.config.name
}) })
) )
setBots(data) setBots(data)

View File

@@ -1,9 +1,9 @@
import { TradeChart, CardPositionItem } from '..' import {CardPositionItem, TradeChart} from '..'
import { Backtest, MoneyManagement, TradingBot } from '../../../generated/ManagingApi' import {TradingBotResponse} from '../../../generated/ManagingApi'
import {CardPosition, CardText} from '../../mollecules' import {CardPosition, CardText} from '../../mollecules'
interface IBotRowDetailsProps { interface IBotRowDetailsProps {
bot: TradingBot; bot: TradingBotResponse;
} }
const BotRowDetails: React.FC<IBotRowDetailsProps> = ({ const BotRowDetails: React.FC<IBotRowDetailsProps> = ({
@@ -18,7 +18,8 @@ const BotRowDetails: React.FC<IBotRowDetailsProps> = ({
candles, candles,
positions, positions,
signals, signals,
moneyManagement config,
agentName
} = bot; } = bot;
return ( return (
@@ -43,8 +44,8 @@ const BotRowDetails: React.FC<IBotRowDetailsProps> = ({
<CardText <CardText
title="Money Management" title="Money Management"
content={ content={
"SL: " +(moneyManagement?.stopLoss * 100).toFixed(2) + "% TP: " + "SL: " +(config.moneyManagement?.stopLoss * 100).toFixed(2) + "% TP: " +
(moneyManagement?.takeProfit * 100).toFixed(2) + "%" (config.moneyManagement?.takeProfit * 100).toFixed(2) + "%"
} }
></CardText> ></CardText>

View File

@@ -3,9 +3,9 @@ import React, {useEffect, useState} from 'react'
import useApiUrlStore from '../../../app/store/apiStore' import useApiUrlStore from '../../../app/store/apiStore'
import useBacktestStore from '../../../app/store/backtestStore' import useBacktestStore from '../../../app/store/backtestStore'
import type {Backtest, Indicator, IndicatorType} from '../../../generated/ManagingApi' import type {Backtest} from '../../../generated/ManagingApi'
import {BacktestClient, SignalType} from '../../../generated/ManagingApi' import {BacktestClient} from '../../../generated/ManagingApi'
import {CardText, ConfigDisplayModal, SelectColumnFilter, Table} from '../../mollecules' import {CardText, ConfigDisplayModal, IndicatorsDisplay, SelectColumnFilter, Table} from '../../mollecules'
import {UnifiedTradingModal} from '../index' import {UnifiedTradingModal} from '../index'
import Toast from '../../mollecules/Toast/Toast' import Toast from '../../mollecules/Toast/Toast'
@@ -16,49 +16,7 @@ interface BacktestTableProps {
isFetching?: boolean isFetching?: boolean
} }
// Helper function to get indicator parameters from scenario indicators
const getIndicatorParameters = (indicator: Indicator) => {
if (!indicator) return null;
return {
name: indicator.name || 'Unnamed Indicator',
type: indicator.type,
signalType: indicator.signalType,
period: indicator.period,
fastPeriods: indicator.fastPeriods,
slowPeriods: indicator.slowPeriods,
signalPeriods: indicator.signalPeriods,
multiplier: indicator.multiplier,
smoothPeriods: indicator.smoothPeriods,
stochPeriods: indicator.stochPeriods,
cyclePeriods: indicator.cyclePeriods
};
};
// Helper function to get indicators from scenario
const getScenarioIndicators = (backtest: Backtest) => {
if (!backtest.config?.scenario?.indicators) return [];
return backtest.config.scenario.indicators.map(getIndicatorParameters).filter(Boolean);
};
// Helper function to get badge color based on signal type
const getBadgeColor = (signalType: SignalType) => {
switch (signalType) {
case SignalType.Signal:
return 'badge-primary';
case SignalType.Trend:
return 'badge-secondary';
case SignalType.Context:
return 'badge-accent';
default:
return 'badge-neutral';
}
};
// Helper function to format indicator type for display
const formatIndicatorType = (type: IndicatorType) => {
return type.replace(/([A-Z])/g, ' $1').trim();
};
const BacktestTable: React.FC<BacktestTableProps> = ({list, isFetching}) => { const BacktestTable: React.FC<BacktestTableProps> = ({list, isFetching}) => {
const [rows, setRows] = useState<Backtest[]>([]) const [rows, setRows] = useState<Backtest[]>([])
@@ -207,34 +165,10 @@ const BacktestTable: React.FC<BacktestTableProps> = ({list, isFetching}) => {
accessor: 'config.scenario.indicators', accessor: 'config.scenario.indicators',
Cell: ({cell}: any) => { Cell: ({cell}: any) => {
const backtest = cell.row.original as Backtest; const backtest = cell.row.original as Backtest;
const indicators = getScenarioIndicators(backtest); const indicators = backtest.config?.scenario?.indicators || [];
return ( return (
<div className="flex flex-wrap gap-1"> <IndicatorsDisplay indicators={indicators} />
{indicators.map((indicator: any, index: number) => {
if (!indicator) return null;
// Build tooltip content with indicator parameters
const tooltipContent = `
${indicator.period ? `Period: ${indicator.period}` : ''}
${indicator.fastPeriods ? `Fast: ${indicator.fastPeriods}` : ''}
${indicator.slowPeriods ? `Slow: ${indicator.slowPeriods}` : ''}
${indicator.signalPeriods ? `Signal: ${indicator.signalPeriods}` : ''}
${indicator.multiplier ? `Multiplier: ${indicator.multiplier}` : ''}
${indicator.smoothPeriods ? `Smooth: ${indicator.smoothPeriods}` : ''}
${indicator.stochPeriods ? `Stoch: ${indicator.stochPeriods}` : ''}
${indicator.cyclePeriods ? `Cycle: ${indicator.cyclePeriods}` : ''}
`.trim().replace(/\n\s+/g, '\n');
return (
<div key={index} className="tooltip" data-tip={tooltipContent}>
<div className={`badge ${getBadgeColor(indicator.signalType)} badge-sm`}>
{formatIndicatorType(indicator.type)}
</div>
</div>
);
})}
</div>
); );
}, },
disableFilters: true, disableFilters: true,

View File

@@ -258,7 +258,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
<div className="mb-4"> <div className="mb-4">
{/* Bot Name - Always on its own line */} {/* Bot Name - Always on its own line */}
<h2 className="card-title text-sm mb-3"> <h2 className="card-title text-sm mb-3">
{bot.config.ticker} {bot.config.name}
</h2> </h2>
{/* Badge Container - Responsive */} {/* Badge Container - Responsive */}
@@ -282,6 +282,10 @@ const BotList: React.FC<IBotList> = ({ list }) => {
<div className="columns-2"> <div className="columns-2">
<div> <div>
<div> <div>
<CardText
title="Ticker"
content={bot.config.ticker}
></CardText>
<CardText <CardText
title="Scenario" title="Scenario"
content={bot.config.scenarioName ?? bot.config.scenario?.name} content={bot.config.scenarioName ?? bot.config.scenario?.name}
@@ -290,8 +294,11 @@ const BotList: React.FC<IBotList> = ({ list }) => {
</div> </div>
</div> </div>
<div className="columns-2"> <div className="columns-2">
<CardText
title="Agent"
content={bot.agentName}
></CardText>
<CardSignal signals={bot.signals}></CardSignal> <CardSignal signals={bot.signals}></CardSignal>
<CardText title="Type" content={bot.config.flipPosition ? 'Flipping' : 'Simple'}></CardText>
</div> </div>
<div className="columns-2"> <div className="columns-2">
<CardPosition <CardPosition