Update scoring and progress
This commit is contained in:
@@ -3,19 +3,20 @@ using static Managing.Common.Enums;
|
|||||||
|
|
||||||
public class BacktestScorer
|
public class BacktestScorer
|
||||||
{
|
{
|
||||||
// Updated weights with more balanced distribution
|
// Updated weights with more emphasis on HODL comparison and risk management
|
||||||
private static readonly Dictionary<string, double> Weights = new Dictionary<string, double>
|
private static readonly Dictionary<string, double> Weights = new Dictionary<string, double>
|
||||||
{
|
{
|
||||||
{ "GrowthPercentage", 0.25 },
|
{ "GrowthPercentage", 0.20 },
|
||||||
{ "SharpeRatio", 0.15 },
|
{ "SharpeRatio", 0.15 },
|
||||||
{ "MaxDrawdownUsd", 0.12 },
|
{ "MaxDrawdownUsd", 0.10 },
|
||||||
{ "HodlComparison", 0.05 },
|
{ "HodlComparison", 0.15 }, // Increased from 0.05 to 0.15
|
||||||
{ "WinRate", 0.15 },
|
{ "WinRate", 0.12 },
|
||||||
{ "ProfitabilityBonus", 0.08 },
|
{ "ProfitabilityBonus", 0.08 },
|
||||||
{ "TradeCount", 0.05 },
|
{ "TradeCount", 0.05 },
|
||||||
{ "RecoveryTime", 0.02 },
|
{ "RecoveryTime", 0.02 },
|
||||||
{ "TestDuration", 0.03 },
|
{ "TestDuration", 0.03 },
|
||||||
{ "FeesImpact", 0.02 }
|
{ "FeesImpact", 0.02 },
|
||||||
|
{ "RiskAdjustedReturn", 0.08 } // New component
|
||||||
};
|
};
|
||||||
|
|
||||||
public static double CalculateTotalScore(BacktestScoringParams p)
|
public static double CalculateTotalScore(BacktestScoringParams p)
|
||||||
@@ -52,7 +53,8 @@ public class BacktestScorer
|
|||||||
{ "TradeCount", CalculateTradeCountScore(p.TradeCount) },
|
{ "TradeCount", CalculateTradeCountScore(p.TradeCount) },
|
||||||
{ "RecoveryTime", CalculateRecoveryScore(p.MaxDrawdownRecoveryTime, p.Timeframe) },
|
{ "RecoveryTime", CalculateRecoveryScore(p.MaxDrawdownRecoveryTime, p.Timeframe) },
|
||||||
{ "TestDuration", CalculateTestDurationScore(p.StartDate, p.EndDate, p.Timeframe) },
|
{ "TestDuration", CalculateTestDurationScore(p.StartDate, p.EndDate, p.Timeframe) },
|
||||||
{ "FeesImpact", CalculateFeesImpactScore(p.FeesPaid, p.InitialBalance, (decimal)p.TotalPnL) }
|
{ "FeesImpact", CalculateFeesImpactScore(p.FeesPaid, p.InitialBalance, (decimal)p.TotalPnL) },
|
||||||
|
{ "RiskAdjustedReturn", CalculateRiskAdjustedReturnScore(p.TotalPnL, p.MaxDrawdown, p.InitialBalance) }
|
||||||
};
|
};
|
||||||
|
|
||||||
return componentScores.Sum(kvp => kvp.Value * Weights[kvp.Key]);
|
return componentScores.Sum(kvp => kvp.Value * Weights[kvp.Key]);
|
||||||
@@ -89,14 +91,25 @@ public class BacktestScorer
|
|||||||
penaltyMultiplier *= Math.Max(0.5, 1 - profitPenalty);
|
penaltyMultiplier *= Math.Max(0.5, 1 - profitPenalty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Drawdown Penalty (Dynamic)
|
// 5. Drawdown Penalty (Dynamic) - Enhanced to consider PnL ratio
|
||||||
if (p.MaxDrawdownPc > 20)
|
if (p.MaxDrawdownPc > 20)
|
||||||
{
|
{
|
||||||
var drawdownPenalty = (p.MaxDrawdownPc - 20) * 0.02; // 2% penalty per 1% above 20%
|
var drawdownPenalty = (p.MaxDrawdownPc - 20) * 0.02; // 2% penalty per 1% above 20%
|
||||||
penaltyMultiplier *= Math.Max(0.3, 1 - drawdownPenalty);
|
penaltyMultiplier *= Math.Max(0.3, 1 - drawdownPenalty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Test Duration Penalty (Dynamic)
|
// 6. Enhanced Drawdown vs PnL Penalty
|
||||||
|
if (p.TotalPnL > 0 && p.MaxDrawdown > 0)
|
||||||
|
{
|
||||||
|
var drawdownToPnLRatio = (double)(p.MaxDrawdown / (decimal)p.TotalPnL);
|
||||||
|
if (drawdownToPnLRatio > 1.5) // If drawdown is more than 150% of PnL
|
||||||
|
{
|
||||||
|
var ratioPenalty = (drawdownToPnLRatio - 1.5) * 0.2; // 20% penalty per 0.5 ratio above 1.5
|
||||||
|
penaltyMultiplier *= Math.Max(0.2, 1 - ratioPenalty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Test Duration Penalty (Dynamic)
|
||||||
var testDurationDays = (p.EndDate - p.StartDate).TotalDays;
|
var testDurationDays = (p.EndDate - p.StartDate).TotalDays;
|
||||||
if (testDurationDays < 30)
|
if (testDurationDays < 30)
|
||||||
{
|
{
|
||||||
@@ -154,8 +167,6 @@ public class BacktestScorer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static double CalculateDrawdownUsdScore(decimal maxDrawdown, decimal initialBalance)
|
private static double CalculateDrawdownUsdScore(decimal maxDrawdown, decimal initialBalance)
|
||||||
{
|
{
|
||||||
if (initialBalance <= 0) return 0;
|
if (initialBalance <= 0) return 0;
|
||||||
@@ -188,10 +199,16 @@ public class BacktestScorer
|
|||||||
private static double CalculateHodlComparisonScore(double strategyGrowth, double hodlGrowth)
|
private static double CalculateHodlComparisonScore(double strategyGrowth, double hodlGrowth)
|
||||||
{
|
{
|
||||||
var difference = strategyGrowth - hodlGrowth;
|
var difference = strategyGrowth - hodlGrowth;
|
||||||
|
|
||||||
|
// Much more aggressive scoring for HODL comparison
|
||||||
return difference switch
|
return difference switch
|
||||||
{
|
{
|
||||||
> 0 => 80 - (80 / (1 + difference / 3)), // Reduced max to 80
|
> 5 => 100, // Significantly outperform HODL (>5% better)
|
||||||
_ => Math.Max(0, 20 + difference * 2) // Reduced base score
|
> 2 => 80 + (difference - 2) * 6.67, // 2-5% better: 80-100 points
|
||||||
|
> 0 => 40 + difference * 20, // 0-2% better: 40-80 points
|
||||||
|
> -2 => 20 + (difference + 2) * 10, // -2-0%: 20-40 points
|
||||||
|
> -5 => Math.Max(0, 10 + (difference + 5) * 3.33), // -5 to -2%: 0-20 points
|
||||||
|
_ => 0 // More than 5% worse than HODL = 0 points
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,4 +290,22 @@ public class BacktestScorer
|
|||||||
|
|
||||||
return feeEfficiency;
|
return feeEfficiency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static double CalculateRiskAdjustedReturnScore(double totalPnL, decimal maxDrawdown, decimal initialBalance)
|
||||||
|
{
|
||||||
|
if (initialBalance <= 0 || maxDrawdown <= 0) return 0;
|
||||||
|
|
||||||
|
var pnlRatio = totalPnL / (double)maxDrawdown;
|
||||||
|
|
||||||
|
// Score based on PnL to drawdown ratio
|
||||||
|
return pnlRatio switch
|
||||||
|
{
|
||||||
|
> 3 => 100, // Excellent risk-adjusted return (>3:1)
|
||||||
|
> 2 => 80 + (pnlRatio - 2) * 20, // 2-3:1: 80-100 points
|
||||||
|
> 1.5 => 60 + (pnlRatio - 1.5) * 40, // 1.5-2:1: 60-80 points
|
||||||
|
> 1 => 40 + (pnlRatio - 1) * 40, // 1-1.5:1: 40-60 points
|
||||||
|
> 0.5 => 20 + (pnlRatio - 0.5) * 40, // 0.5-1:1: 20-40 points
|
||||||
|
_ => Math.Max(0, pnlRatio * 40) // 0-0.5:1: 0-20 points
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import useApiUrlStore from '../../../app/store/apiStore'
|
|||||||
import useBacktestStore from '../../../app/store/backtestStore'
|
import useBacktestStore from '../../../app/store/backtestStore'
|
||||||
import type {Backtest} from '../../../generated/ManagingApi'
|
import type {Backtest} from '../../../generated/ManagingApi'
|
||||||
import {BacktestClient} from '../../../generated/ManagingApi'
|
import {BacktestClient} from '../../../generated/ManagingApi'
|
||||||
import {CardText, ConfigDisplayModal, IndicatorsDisplay, SelectColumnFilter, Table} from '../../mollecules'
|
import {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'
|
||||||
|
|
||||||
@@ -37,6 +37,9 @@ const BacktestTable: React.FC<BacktestTableProps> = ({list, isFetching, displayS
|
|||||||
medianCooldown: 0,
|
medianCooldown: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Summary collapse state
|
||||||
|
const [isSummaryCollapsed, setIsSummaryCollapsed] = useState(true)
|
||||||
|
|
||||||
// Bot configuration modal state
|
// Bot configuration modal state
|
||||||
const [showBotConfigModal, setShowBotConfigModal] = useState(false)
|
const [showBotConfigModal, setShowBotConfigModal] = useState(false)
|
||||||
const [selectedBacktest, setSelectedBacktest] = useState<Backtest | null>(null)
|
const [selectedBacktest, setSelectedBacktest] = useState<Backtest | null>(null)
|
||||||
@@ -471,37 +474,56 @@ const BacktestTable: React.FC<BacktestTableProps> = ({list, isFetching, displayS
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{list && list.length > 0 && displaySummary && (
|
{list && list.length > 0 && displaySummary && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<button
|
||||||
|
onClick={() => setIsSummaryCollapsed(!isSummaryCollapsed)}
|
||||||
|
className="btn btn-sm btn-outline mb-2"
|
||||||
|
>
|
||||||
|
{isSummaryCollapsed ? (
|
||||||
<>
|
<>
|
||||||
<div className="mb-4">
|
<ChevronRightIcon className="w-4 h-4 mr-1" />
|
||||||
<CardText
|
Show Summary
|
||||||
title="Average Optimized Money Management"
|
|
||||||
content={
|
|
||||||
"SL: " + optimizedMoneyManagement.stopLoss.toFixed(2) + "% | TP: " +
|
|
||||||
optimizedMoneyManagement.takeProfit.toFixed(2) + "% | R/R: " +
|
|
||||||
(optimizedMoneyManagement.takeProfit / optimizedMoneyManagement.stopLoss || 0).toFixed(2)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-4">
|
|
||||||
<CardText
|
|
||||||
title="Position Timing Statistics"
|
|
||||||
content={
|
|
||||||
"Avg: " + positionTimingStats.averageOpenTime.toFixed(1) + "h | " +
|
|
||||||
"Median: " + positionTimingStats.medianOpenTime.toFixed(1) + "h | " +
|
|
||||||
"Losing Avg: " + positionTimingStats.losingPositionsAverageOpenTime.toFixed(1) + "h"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-4">
|
|
||||||
<CardText
|
|
||||||
title="Cooldown Recommendations"
|
|
||||||
content={
|
|
||||||
"Avg: " + cooldownRecommendations.averageCooldown + " candles | " +
|
|
||||||
"Median: " + cooldownRecommendations.medianCooldown + " candles"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ChevronDownIcon className="w-4 h-4 mr-1" />
|
||||||
|
Hide Summary
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{!isSummaryCollapsed && (
|
||||||
|
<div className="bg-base-200 p-4 rounded-lg space-y-3">
|
||||||
|
<div className="flex flex-wrap gap-4 text-sm">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="font-semibold text-xs text-gray-600 mb-1">Money Management</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<span className="badge badge-outline">SL: {optimizedMoneyManagement.stopLoss.toFixed(2)}%</span>
|
||||||
|
<span className="badge badge-outline">TP: {optimizedMoneyManagement.takeProfit.toFixed(2)}%</span>
|
||||||
|
<span className="badge badge-outline">R/R: {(optimizedMoneyManagement.takeProfit / optimizedMoneyManagement.stopLoss || 0).toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="font-semibold text-xs text-gray-600 mb-1">Position Timing</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<span className="badge badge-outline">Avg: {positionTimingStats.averageOpenTime.toFixed(1)}h</span>
|
||||||
|
<span className="badge badge-outline">Median: {positionTimingStats.medianOpenTime.toFixed(1)}h</span>
|
||||||
|
<span className="badge badge-outline">Losing: {positionTimingStats.losingPositionsAverageOpenTime.toFixed(1)}h</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="font-semibold text-xs text-gray-600 mb-1">Cooldown</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<span className="badge badge-outline">Avg: {cooldownRecommendations.averageCooldown} candles</span>
|
||||||
|
<span className="badge badge-outline">Median: {cooldownRecommendations.medianCooldown} candles</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Plot from 'react-plotly.js';
|
import Plot from 'react-plotly.js';
|
||||||
import {Backtest} from '../../../generated/ManagingApi';
|
import {Backtest} from '../../../generated/ManagingApi';
|
||||||
|
import useTheme from '../../../hooks/useTheme';
|
||||||
|
|
||||||
interface Fitness3DPlotProps {
|
interface Fitness3DPlotProps {
|
||||||
backtests: Backtest[];
|
backtests: Backtest[];
|
||||||
theme: { secondary: string };
|
theme: { secondary: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
const Fitness3DPlot: React.FC<Fitness3DPlotProps> = ({ backtests, theme }) => {
|
const Fitness3DPlot: React.FC<Fitness3DPlotProps> = ({ backtests }) => {
|
||||||
|
const theme = useTheme().themeProperty()
|
||||||
|
|
||||||
const LOOPBACK_CONFIG = {
|
const LOOPBACK_CONFIG = {
|
||||||
singleIndicator: 1,
|
singleIndicator: 1,
|
||||||
multipleIndicators: { min: 5, max: 15 },
|
multipleIndicators: { min: 5, max: 15 },
|
||||||
@@ -113,9 +116,9 @@ const Fitness3DPlot: React.FC<Fitness3DPlotProps> = ({ backtests, theme }) => {
|
|||||||
layout={{
|
layout={{
|
||||||
title: {text: 'Fitness Score vs Score vs Win Rate'},
|
title: {text: 'Fitness Score vs Score vs Win Rate'},
|
||||||
scene: {
|
scene: {
|
||||||
xaxis: {title: {text: 'Fitness Score'}},
|
xaxis: {title: {text: 'Fitness Score'}, zerolinecolor: theme.secondary, color: theme.secondary},
|
||||||
yaxis: {title: {text: 'Score'}},
|
yaxis: {title: {text: 'Score'}, zerolinecolor: theme.secondary, color: theme.secondary},
|
||||||
zaxis: {title: {text: 'Win Rate (%)'}},
|
zaxis: {title: {text: 'Win Rate (%)'}, zerolinecolor: theme.secondary, color: theme.secondary},
|
||||||
},
|
},
|
||||||
width: 750,
|
width: 750,
|
||||||
height: 600,
|
height: 600,
|
||||||
@@ -126,8 +129,8 @@ const Fitness3DPlot: React.FC<Fitness3DPlotProps> = ({ backtests, theme }) => {
|
|||||||
r: 0,
|
r: 0,
|
||||||
t: 0,
|
t: 0,
|
||||||
},
|
},
|
||||||
paper_bgcolor: '#121212',
|
paper_bgcolor: "rgba(0,0,0,0)",
|
||||||
plot_bgcolor: theme.secondary,
|
plot_bgcolor: "rgba(0,0,0,0)",
|
||||||
}}
|
}}
|
||||||
config={{displayModeBar: false}}
|
config={{displayModeBar: false}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Plot from 'react-plotly.js'
|
import Plot from 'react-plotly.js'
|
||||||
import {Backtest} from '../../../generated/ManagingApi'
|
import {Backtest} from '../../../generated/ManagingApi'
|
||||||
|
import useTheme from '../../../hooks/useTheme'
|
||||||
|
|
||||||
interface ScoreVsGenerationProps {
|
interface ScoreVsGenerationProps {
|
||||||
backtests: Backtest[]
|
backtests: Backtest[]
|
||||||
theme: { secondary: string }
|
theme: { secondary: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
const ScoreVsGeneration: React.FC<ScoreVsGenerationProps> = ({ backtests, theme }) => {
|
const ScoreVsGeneration: React.FC<ScoreVsGenerationProps> = ({ backtests }) => {
|
||||||
|
const theme = useTheme().themeProperty()
|
||||||
|
|
||||||
// Helper function to get generation from metadata
|
// Helper function to get generation from metadata
|
||||||
const getGeneration = (backtest: Backtest): number => {
|
const getGeneration = (backtest: Backtest): number => {
|
||||||
if (backtest.metadata && typeof backtest.metadata === 'object' && 'generation' in backtest.metadata) {
|
if (backtest.metadata && typeof backtest.metadata === 'object' && 'generation' in backtest.metadata) {
|
||||||
@@ -115,7 +118,7 @@ const ScoreVsGeneration: React.FC<ScoreVsGenerationProps> = ({ backtests, theme
|
|||||||
color: theme.secondary
|
color: theme.secondary
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
gridcolor: theme.secondary,
|
gridcolor: theme.neutral,
|
||||||
zerolinecolor: theme.secondary,
|
zerolinecolor: theme.secondary,
|
||||||
color: theme.secondary
|
color: theme.secondary
|
||||||
},
|
},
|
||||||
@@ -126,7 +129,7 @@ const ScoreVsGeneration: React.FC<ScoreVsGenerationProps> = ({ backtests, theme
|
|||||||
color: theme.secondary
|
color: theme.secondary
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
gridcolor: theme.secondary,
|
gridcolor: theme.neutral,
|
||||||
zerolinecolor: theme.secondary,
|
zerolinecolor: theme.secondary,
|
||||||
color: theme.secondary
|
color: theme.secondary
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ const TPvsSLvsPnL3DPlot: React.FC<TPvsSLvsPnL3DPlotProps> = ({ backtests, theme
|
|||||||
layout={{
|
layout={{
|
||||||
title: {text: 'Take Profit % vs Stop Loss % vs PnL'},
|
title: {text: 'Take Profit % vs Stop Loss % vs PnL'},
|
||||||
scene: {
|
scene: {
|
||||||
xaxis: {title: {text: 'Take Profit (%)'}},
|
xaxis: {title: {text: 'Take Profit (%)'}, zerolinecolor: theme.secondary, color: theme.secondary},
|
||||||
yaxis: {title: {text: 'Stop Loss (%)'}},
|
yaxis: {title: {text: 'Stop Loss (%)'}, zerolinecolor: theme.secondary, color: theme.secondary},
|
||||||
zaxis: {title: {text: 'PnL ($)'}},
|
zaxis: {title: {text: 'PnL ($)'}, zerolinecolor: theme.secondary, color: theme.secondary},
|
||||||
},
|
},
|
||||||
width: 750,
|
width: 750,
|
||||||
height: 600,
|
height: 600,
|
||||||
@@ -99,8 +99,8 @@ const TPvsSLvsPnL3DPlot: React.FC<TPvsSLvsPnL3DPlotProps> = ({ backtests, theme
|
|||||||
r: 0,
|
r: 0,
|
||||||
t: 0,
|
t: 0,
|
||||||
},
|
},
|
||||||
paper_bgcolor: '#121212',
|
paper_bgcolor: "rgba(0,0,0,0)",
|
||||||
plot_bgcolor: theme.secondary,
|
plot_bgcolor: "rgba(0,0,0,0)",
|
||||||
}}
|
}}
|
||||||
config={{displayModeBar: false}}
|
config={{displayModeBar: false}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -248,20 +248,34 @@ const BacktestGeneticBundle: React.FC = () => {
|
|||||||
accessor: 'timeframe',
|
accessor: 'timeframe',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Progress',
|
Header: 'Progress & Status',
|
||||||
accessor: 'currentGeneration',
|
accessor: 'currentGeneration',
|
||||||
Cell: ({value, row}: { value: number, row: any }) => {
|
Cell: ({value, row}: { value: number, row: any }) => {
|
||||||
const generations = row.original.generations
|
const generations = row.original.generations
|
||||||
const currentGen = value || 0
|
const currentGen = value || 0
|
||||||
const percentage = generations > 0 ? Math.round((currentGen / generations) * 100) : 0
|
const percentage = generations > 0 ? Math.round((currentGen / generations) * 100) : 0
|
||||||
|
const status = row.original.status
|
||||||
|
|
||||||
|
// Calculate color based on percentage (red to green)
|
||||||
|
const getProgressColor = (percent: number) => {
|
||||||
|
if (percent <= 25) return 'progress-error'
|
||||||
|
if (percent <= 50) return 'progress-warning'
|
||||||
|
if (percent <= 75) return 'progress-info'
|
||||||
|
return 'progress-success'
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className={`badge badge-sm ${getStatusBadgeColor(status)}`}>
|
||||||
|
{status}
|
||||||
|
</span>
|
||||||
<div className="text-xs">
|
<div className="text-xs">
|
||||||
{currentGen} / {generations}
|
{currentGen} / {generations}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<progress
|
<progress
|
||||||
className="progress progress-primary w-full h-2"
|
className={`progress w-56 ${getProgressColor(percentage)}`}
|
||||||
value={percentage}
|
value={percentage}
|
||||||
max="100"
|
max="100"
|
||||||
/>
|
/>
|
||||||
@@ -290,15 +304,7 @@ const BacktestGeneticBundle: React.FC = () => {
|
|||||||
<span className="text-xs">{value}</span>
|
<span className="text-xs">{value}</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Header: 'Status',
|
|
||||||
accessor: 'status',
|
|
||||||
Cell: ({value}: { value: string }) => (
|
|
||||||
<span className={`badge ${getStatusBadgeColor(value)}`}>
|
|
||||||
{value}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Header: 'Actions',
|
Header: 'Actions',
|
||||||
accessor: 'actions',
|
accessor: 'actions',
|
||||||
|
|||||||
Reference in New Issue
Block a user