Add backtest light
This commit is contained in:
@@ -179,7 +179,21 @@ public class BacktestController : BaseController
|
|||||||
|
|
||||||
var response = new PaginatedBacktestsResponse
|
var response = new PaginatedBacktestsResponse
|
||||||
{
|
{
|
||||||
Backtests = backtests,
|
Backtests = backtests.Select(b => new LightBacktestResponse
|
||||||
|
{
|
||||||
|
Id = b.Id,
|
||||||
|
Config = b.Config,
|
||||||
|
FinalPnl = b.FinalPnl,
|
||||||
|
WinRate = b.WinRate,
|
||||||
|
GrowthPercentage = b.GrowthPercentage,
|
||||||
|
HodlPercentage = b.HodlPercentage,
|
||||||
|
StartDate = b.StartDate,
|
||||||
|
EndDate = b.EndDate,
|
||||||
|
MaxDrawdown = b.MaxDrawdown,
|
||||||
|
Fees = b.Fees,
|
||||||
|
SharpeRatio = b.SharpeRatio,
|
||||||
|
Score = b.Score
|
||||||
|
}),
|
||||||
TotalCount = totalCount,
|
TotalCount = totalCount,
|
||||||
CurrentPage = page,
|
CurrentPage = page,
|
||||||
PageSize = pageSize,
|
PageSize = pageSize,
|
||||||
@@ -229,7 +243,21 @@ public class BacktestController : BaseController
|
|||||||
|
|
||||||
var response = new PaginatedBacktestsResponse
|
var response = new PaginatedBacktestsResponse
|
||||||
{
|
{
|
||||||
Backtests = backtests,
|
Backtests = backtests.Select(b => new LightBacktestResponse
|
||||||
|
{
|
||||||
|
Id = b.Id,
|
||||||
|
Config = b.Config,
|
||||||
|
FinalPnl = b.FinalPnl,
|
||||||
|
WinRate = b.WinRate,
|
||||||
|
GrowthPercentage = b.GrowthPercentage,
|
||||||
|
HodlPercentage = b.HodlPercentage,
|
||||||
|
StartDate = b.StartDate,
|
||||||
|
EndDate = b.EndDate,
|
||||||
|
MaxDrawdown = b.MaxDrawdown,
|
||||||
|
Fees = b.Fees,
|
||||||
|
SharpeRatio = b.SharpeRatio,
|
||||||
|
Score = b.Score
|
||||||
|
}),
|
||||||
TotalCount = totalCount,
|
TotalCount = totalCount,
|
||||||
CurrentPage = page,
|
CurrentPage = page,
|
||||||
PageSize = pageSize,
|
PageSize = pageSize,
|
||||||
|
|||||||
19
src/Managing.Api/Models/Requests/LightBacktestResponse.cs
Normal file
19
src/Managing.Api/Models/Requests/LightBacktestResponse.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Managing.Domain.Bots;
|
||||||
|
|
||||||
|
namespace Managing.Api.Models.Requests;
|
||||||
|
|
||||||
|
public class LightBacktestResponse
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
public TradingBotConfig Config { get; set; } = new();
|
||||||
|
public decimal FinalPnl { get; set; }
|
||||||
|
public int WinRate { get; set; }
|
||||||
|
public decimal GrowthPercentage { get; set; }
|
||||||
|
public decimal HodlPercentage { get; set; }
|
||||||
|
public DateTime StartDate { get; set; }
|
||||||
|
public DateTime EndDate { get; set; }
|
||||||
|
public decimal? MaxDrawdown { get; set; }
|
||||||
|
public decimal Fees { get; set; }
|
||||||
|
public double? SharpeRatio { get; set; }
|
||||||
|
public double Score { get; set; }
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
using Managing.Domain.Backtests;
|
|
||||||
|
|
||||||
namespace Managing.Api.Models.Requests;
|
namespace Managing.Api.Models.Requests;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -10,7 +8,7 @@ public class PaginatedBacktestsResponse
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The list of backtests for the current page
|
/// The list of backtests for the current page
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<Backtest> Backtests { get; set; } = new List<Backtest>();
|
public IEnumerable<LightBacktestResponse> Backtests { get; set; } = new List<LightBacktestResponse>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total number of backtests across all pages
|
/// Total number of backtests across all pages
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ public interface IBacktestRepository
|
|||||||
void InsertBacktestForUser(User user, Backtest result);
|
void InsertBacktestForUser(User user, Backtest result);
|
||||||
IEnumerable<Backtest> GetBacktestsByUser(User user);
|
IEnumerable<Backtest> GetBacktestsByUser(User user);
|
||||||
IEnumerable<Backtest> GetBacktestsByRequestId(string requestId);
|
IEnumerable<Backtest> GetBacktestsByRequestId(string requestId);
|
||||||
(IEnumerable<Backtest> Backtests, int TotalCount) GetBacktestsByRequestIdPaginated(string requestId, int page, int pageSize, string sortBy = "score", string sortOrder = "desc");
|
(IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByRequestIdPaginated(string requestId, int page, int pageSize, string sortBy = "score", string sortOrder = "desc");
|
||||||
(IEnumerable<Backtest> Backtests, int TotalCount) GetBacktestsByUserPaginated(User user, int page, int pageSize, string sortBy = "score", string sortOrder = "desc");
|
(IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByUserPaginated(User user, int page, int pageSize, string sortBy = "score", string sortOrder = "desc");
|
||||||
Backtest GetBacktestByIdForUser(User user, string id);
|
Backtest GetBacktestByIdForUser(User user, string id);
|
||||||
void DeleteBacktestByIdForUser(User user, string id);
|
void DeleteBacktestByIdForUser(User user, string id);
|
||||||
void DeleteBacktestsByIdsForUser(User user, IEnumerable<string> ids);
|
void DeleteBacktestsByIdsForUser(User user, IEnumerable<string> ids);
|
||||||
|
|||||||
@@ -54,12 +54,12 @@ namespace Managing.Application.Abstractions.Services
|
|||||||
bool DeleteBacktests();
|
bool DeleteBacktests();
|
||||||
IEnumerable<Backtest> GetBacktestsByUser(User user);
|
IEnumerable<Backtest> GetBacktestsByUser(User user);
|
||||||
IEnumerable<Backtest> GetBacktestsByRequestId(string requestId);
|
IEnumerable<Backtest> GetBacktestsByRequestId(string requestId);
|
||||||
(IEnumerable<Backtest> Backtests, int TotalCount) GetBacktestsByRequestIdPaginated(string requestId, int page, int pageSize, string sortBy = "score", string sortOrder = "desc");
|
(IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByRequestIdPaginated(string requestId, int page, int pageSize, string sortBy = "score", string sortOrder = "desc");
|
||||||
Backtest GetBacktestByIdForUser(User user, string id);
|
Backtest GetBacktestByIdForUser(User user, string id);
|
||||||
bool DeleteBacktestByUser(User user, string id);
|
bool DeleteBacktestByUser(User user, string id);
|
||||||
bool DeleteBacktestsByIdsForUser(User user, IEnumerable<string> ids);
|
bool DeleteBacktestsByIdsForUser(User user, IEnumerable<string> ids);
|
||||||
bool DeleteBacktestsByUser(User user);
|
bool DeleteBacktestsByUser(User user);
|
||||||
(IEnumerable<Backtest> Backtests, int TotalCount) GetBacktestsByUserPaginated(User user, int page, int pageSize, string sortBy = "score", string sortOrder = "desc");
|
(IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByUserPaginated(User user, int page, int pageSize, string sortBy = "score", string sortOrder = "desc");
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -448,7 +448,7 @@ namespace Managing.Application.Backtesting
|
|||||||
return backtests;
|
return backtests;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (IEnumerable<Backtest> Backtests, int TotalCount) GetBacktestsByRequestIdPaginated(string requestId, int page, int pageSize, string sortBy = "score", string sortOrder = "desc")
|
public (IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByRequestIdPaginated(string requestId, int page, int pageSize, string sortBy = "score", string sortOrder = "desc")
|
||||||
{
|
{
|
||||||
var (backtests, totalCount) = _backtestRepository.GetBacktestsByRequestIdPaginated(requestId, page, pageSize, sortBy, sortOrder);
|
var (backtests, totalCount) = _backtestRepository.GetBacktestsByRequestIdPaginated(requestId, page, pageSize, sortBy, sortOrder);
|
||||||
return (backtests, totalCount);
|
return (backtests, totalCount);
|
||||||
@@ -531,7 +531,7 @@ namespace Managing.Application.Backtesting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public (IEnumerable<Backtest> Backtests, int TotalCount) GetBacktestsByUserPaginated(User user, int page, int pageSize, string sortBy = "score", string sortOrder = "desc")
|
public (IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByUserPaginated(User user, int page, int pageSize, string sortBy = "score", string sortOrder = "desc")
|
||||||
{
|
{
|
||||||
var (backtests, totalCount) = _backtestRepository.GetBacktestsByUserPaginated(user, page, pageSize, sortBy, sortOrder);
|
var (backtests, totalCount) = _backtestRepository.GetBacktestsByUserPaginated(user, page, pageSize, sortBy, sortOrder);
|
||||||
return (backtests, totalCount);
|
return (backtests, totalCount);
|
||||||
|
|||||||
19
src/Managing.Domain/Backtests/LightBacktest.cs
Normal file
19
src/Managing.Domain/Backtests/LightBacktest.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Managing.Domain.Bots;
|
||||||
|
|
||||||
|
namespace Managing.Domain.Backtests;
|
||||||
|
|
||||||
|
public class LightBacktest
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
public TradingBotConfig Config { get; set; } = new();
|
||||||
|
public decimal FinalPnl { get; set; }
|
||||||
|
public int WinRate { get; set; }
|
||||||
|
public decimal GrowthPercentage { get; set; }
|
||||||
|
public decimal HodlPercentage { get; set; }
|
||||||
|
public DateTime StartDate { get; set; }
|
||||||
|
public DateTime EndDate { get; set; }
|
||||||
|
public decimal? MaxDrawdown { get; set; }
|
||||||
|
public decimal Fees { get; set; }
|
||||||
|
public double? SharpeRatio { get; set; }
|
||||||
|
public double Score { get; set; }
|
||||||
|
}
|
||||||
@@ -83,7 +83,7 @@ public class BacktestRepository : IBacktestRepository
|
|||||||
return backtests.Select(b => MongoMappers.Map(b));
|
return backtests.Select(b => MongoMappers.Map(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
public (IEnumerable<Backtest> Backtests, int TotalCount) GetBacktestsByRequestIdPaginated(string requestId,
|
public (IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByRequestIdPaginated(string requestId,
|
||||||
int page, int pageSize, string sortBy = "score", string sortOrder = "desc")
|
int page, int pageSize, string sortBy = "score", string sortOrder = "desc")
|
||||||
{
|
{
|
||||||
var stopwatch = Stopwatch.StartNew();
|
var stopwatch = Stopwatch.StartNew();
|
||||||
@@ -101,14 +101,12 @@ public class BacktestRepository : IBacktestRepository
|
|||||||
.Include(b => b.WinRate)
|
.Include(b => b.WinRate)
|
||||||
.Include(b => b.GrowthPercentage)
|
.Include(b => b.GrowthPercentage)
|
||||||
.Include(b => b.HodlPercentage)
|
.Include(b => b.HodlPercentage)
|
||||||
.Include(b => b.User)
|
|
||||||
.Include(b => b.Statistics)
|
|
||||||
.Include(b => b.StartDate)
|
.Include(b => b.StartDate)
|
||||||
.Include(b => b.EndDate)
|
.Include(b => b.EndDate)
|
||||||
.Include(b => b.Score)
|
.Include(b => b.Score)
|
||||||
.Include(b => b.RequestId)
|
.Include(b => b.Config)
|
||||||
.Include(b => b.Metadata)
|
.Include(b => b.Fees)
|
||||||
.Include(b => b.Config);
|
.Include(b => b.Statistics);
|
||||||
|
|
||||||
// Build sort definition
|
// Build sort definition
|
||||||
var sortDefinition = sortBy.ToLower() switch
|
var sortDefinition = sortBy.ToLower() switch
|
||||||
@@ -146,7 +144,21 @@ public class BacktestRepository : IBacktestRepository
|
|||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"[BacktestRepo] Query: {afterQueryMs}ms, Count: {afterCountMs - afterQueryMs}ms, Projection: {afterProjectionMs - afterCountMs}ms, ToList: {afterToListMs - afterProjectionMs}ms, Total: {afterToListMs}ms");
|
$"[BacktestRepo] Query: {afterQueryMs}ms, Count: {afterCountMs - afterQueryMs}ms, Projection: {afterProjectionMs - afterCountMs}ms, ToList: {afterToListMs - afterProjectionMs}ms, Total: {afterToListMs}ms");
|
||||||
|
|
||||||
var mappedBacktests = backtests.Select(b => MongoMappers.Map(b));
|
var mappedBacktests = backtests.Select(b => new LightBacktest
|
||||||
|
{
|
||||||
|
Id = b.Identifier,
|
||||||
|
Config = MongoMappers.Map(b.Config),
|
||||||
|
FinalPnl = b.FinalPnl,
|
||||||
|
WinRate = b.WinRate,
|
||||||
|
GrowthPercentage = b.GrowthPercentage,
|
||||||
|
HodlPercentage = b.HodlPercentage,
|
||||||
|
StartDate = b.StartDate,
|
||||||
|
EndDate = b.EndDate,
|
||||||
|
MaxDrawdown = b.Statistics?.MaxDrawdown,
|
||||||
|
Fees = b.Fees,
|
||||||
|
SharpeRatio = b.Statistics?.SharpeRatio != null ? (double)b.Statistics.SharpeRatio : null,
|
||||||
|
Score = b.Score
|
||||||
|
});
|
||||||
|
|
||||||
return (mappedBacktests, (int)totalCount);
|
return (mappedBacktests, (int)totalCount);
|
||||||
}
|
}
|
||||||
@@ -198,7 +210,7 @@ public class BacktestRepository : IBacktestRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public (IEnumerable<Backtest> Backtests, int TotalCount) GetBacktestsByUserPaginated(User user, int page,
|
public (IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByUserPaginated(User user, int page,
|
||||||
int pageSize, string sortBy = "score", string sortOrder = "desc")
|
int pageSize, string sortBy = "score", string sortOrder = "desc")
|
||||||
{
|
{
|
||||||
var stopwatch = Stopwatch.StartNew();
|
var stopwatch = Stopwatch.StartNew();
|
||||||
@@ -216,14 +228,12 @@ public class BacktestRepository : IBacktestRepository
|
|||||||
.Include(b => b.WinRate)
|
.Include(b => b.WinRate)
|
||||||
.Include(b => b.GrowthPercentage)
|
.Include(b => b.GrowthPercentage)
|
||||||
.Include(b => b.HodlPercentage)
|
.Include(b => b.HodlPercentage)
|
||||||
.Include(b => b.User)
|
|
||||||
.Include(b => b.Statistics)
|
|
||||||
.Include(b => b.StartDate)
|
.Include(b => b.StartDate)
|
||||||
.Include(b => b.EndDate)
|
.Include(b => b.EndDate)
|
||||||
.Include(b => b.Score)
|
.Include(b => b.Score)
|
||||||
.Include(b => b.RequestId)
|
.Include(b => b.Config)
|
||||||
.Include(b => b.Metadata)
|
.Include(b => b.Fees)
|
||||||
.Include(b => b.Config);
|
.Include(b => b.Statistics);
|
||||||
|
|
||||||
// Build sort definition
|
// Build sort definition
|
||||||
var sortDefinition = sortBy.ToLower() switch
|
var sortDefinition = sortBy.ToLower() switch
|
||||||
@@ -261,7 +271,21 @@ public class BacktestRepository : IBacktestRepository
|
|||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"[BacktestRepo] User Query: {afterQueryMs}ms, Count: {afterCountMs - afterQueryMs}ms, Projection: {afterProjectionMs - afterCountMs}ms, ToList: {afterToListMs - afterProjectionMs}ms, Total: {afterToListMs}ms");
|
$"[BacktestRepo] User Query: {afterQueryMs}ms, Count: {afterCountMs - afterQueryMs}ms, Projection: {afterProjectionMs - afterCountMs}ms, ToList: {afterToListMs - afterProjectionMs}ms, Total: {afterToListMs}ms");
|
||||||
|
|
||||||
var mappedBacktests = backtests.Select(b => MongoMappers.Map(b));
|
var mappedBacktests = backtests.Select(b => new LightBacktest
|
||||||
|
{
|
||||||
|
Id = b.Identifier,
|
||||||
|
Config = MongoMappers.Map(b.Config),
|
||||||
|
FinalPnl = b.FinalPnl,
|
||||||
|
WinRate = b.WinRate,
|
||||||
|
GrowthPercentage = b.GrowthPercentage,
|
||||||
|
HodlPercentage = b.HodlPercentage,
|
||||||
|
StartDate = b.StartDate,
|
||||||
|
EndDate = b.EndDate,
|
||||||
|
MaxDrawdown = b.Statistics?.MaxDrawdown,
|
||||||
|
Fees = b.Fees,
|
||||||
|
SharpeRatio = b.Statistics?.SharpeRatio != null ? (double)b.Statistics.SharpeRatio : null,
|
||||||
|
Score = b.Score
|
||||||
|
});
|
||||||
|
|
||||||
return (mappedBacktests, (int)totalCount);
|
return (mappedBacktests, (int)totalCount);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
|||||||
public MoneyManagementDto OptimizedMoneyManagement { get; internal set; }
|
public MoneyManagementDto OptimizedMoneyManagement { get; internal set; }
|
||||||
public UserDto User { get; set; }
|
public UserDto User { get; set; }
|
||||||
public PerformanceMetrics Statistics { get; set; }
|
public PerformanceMetrics Statistics { get; set; }
|
||||||
|
[BsonRepresentation(BsonType.Decimal128)]
|
||||||
|
public decimal Fees { get; set; }
|
||||||
public double Score { get; set; }
|
public double Score { get; set; }
|
||||||
public string Identifier { get; set; }
|
public string Identifier { get; set; }
|
||||||
public string RequestId { get; set; }
|
public string RequestId { get; set; }
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import {create} from 'zustand'
|
import {create} from 'zustand'
|
||||||
import type {Backtest} from '../../generated/ManagingApi'
|
import type {LightBacktestResponse} from '../../generated/ManagingApi'
|
||||||
|
|
||||||
interface BacktestStore {
|
interface BacktestStore {
|
||||||
backtests: Backtest[]
|
backtests: LightBacktestResponse[]
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
setBacktests: (backtests: Backtest[]) => void
|
setBacktests: (backtests: LightBacktestResponse[]) => void
|
||||||
addBacktest: (backtest: Backtest) => void
|
addBacktest: (backtest: LightBacktestResponse) => void
|
||||||
removeBacktest: (id: string) => void
|
removeBacktest: (id: string) => void
|
||||||
setLoading: (loading: boolean) => void
|
setLoading: (loading: boolean) => void
|
||||||
clearBacktests: () => void
|
clearBacktests: () => void
|
||||||
@@ -15,10 +15,10 @@ const useBacktestStore = create<BacktestStore>((set, get) => ({
|
|||||||
backtests: [],
|
backtests: [],
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
|
||||||
setBacktests: (backtests: Backtest[]) =>
|
setBacktests: (backtests: LightBacktestResponse[]) =>
|
||||||
set({ backtests }),
|
set({ backtests }),
|
||||||
|
|
||||||
addBacktest: (backtest: Backtest) =>
|
addBacktest: (backtest: LightBacktestResponse) =>
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
backtests: [...state.backtests, backtest]
|
backtests: [...state.backtests, backtest]
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -84,13 +84,12 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
const candles = candlesData?.candles || currentBacktest.candles || [];
|
const candles = candlesData?.candles || currentBacktest.candles || [];
|
||||||
const indicatorsValues = candlesData?.indicatorsValues || currentBacktest.indicatorsValues || {};
|
const indicatorsValues = candlesData?.indicatorsValues || currentBacktest.indicatorsValues || {};
|
||||||
|
|
||||||
const {
|
// Only destructure these properties if we have full backtest data
|
||||||
positions,
|
const positions = fullBacktestData?.positions || [];
|
||||||
walletBalances,
|
const walletBalances = fullBacktestData?.walletBalances || [];
|
||||||
signals,
|
const signals = fullBacktestData?.signals || [];
|
||||||
statistics,
|
const statistics = fullBacktestData?.statistics;
|
||||||
config
|
const config = currentBacktest.config;
|
||||||
} = currentBacktest;
|
|
||||||
|
|
||||||
// Helper function to calculate position open time in hours
|
// Helper function to calculate position open time in hours
|
||||||
const calculateOpenTimeInHours = (position: any) => {
|
const calculateOpenTimeInHours = (position: any) => {
|
||||||
@@ -219,7 +218,7 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
|
|
||||||
// Calculate recommended cooldown based on positions that fail after a win
|
// Calculate recommended cooldown based on positions that fail after a win
|
||||||
const getCooldownRecommendations = () => {
|
const getCooldownRecommendations = () => {
|
||||||
if (positions.length < 2 || !candles || candles.length < 2) {
|
if (positions?.length < 2 || !candles || candles?.length < 2) {
|
||||||
return { percentile75: "0", average: "0", median: "0" };
|
return { percentile75: "0", average: "0", median: "0" };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,14 +342,14 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
<CardText
|
<CardText
|
||||||
title="Max Drowdown"
|
title="Max Drowdown"
|
||||||
content={
|
content={
|
||||||
statistics.maxDrawdown?.toFixed(4).toString() +
|
(statistics?.maxDrawdown?.toFixed(4) || '0.0000') +
|
||||||
'$'
|
'$'
|
||||||
}
|
}
|
||||||
></CardText>
|
></CardText>
|
||||||
<CardText
|
<CardText
|
||||||
title="Sharpe Ratio"
|
title="Sharpe Ratio"
|
||||||
content={
|
content={
|
||||||
(statistics.sharpeRatio
|
(statistics?.sharpeRatio
|
||||||
? statistics.sharpeRatio * 100
|
? statistics.sharpeRatio * 100
|
||||||
: 0
|
: 0
|
||||||
)
|
)
|
||||||
@@ -361,7 +360,7 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
<CardText
|
<CardText
|
||||||
title="Max Drawdown Recovery Time"
|
title="Max Drawdown Recovery Time"
|
||||||
content={
|
content={
|
||||||
statistics.maxDrawdownRecoveryTime?.toString() +
|
(statistics?.maxDrawdownRecoveryTime?.toString() || '0') +
|
||||||
' days'
|
' days'
|
||||||
}
|
}
|
||||||
></CardText>
|
></CardText>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {useExpanded, useFilters, usePagination, useSortBy, useTable,} from 'reac
|
|||||||
|
|
||||||
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} from '../../../generated/ManagingApi'
|
import type {Backtest, LightBacktestResponse} from '../../../generated/ManagingApi'
|
||||||
import {BacktestClient} from '../../../generated/ManagingApi'
|
import {BacktestClient} from '../../../generated/ManagingApi'
|
||||||
import {ConfigDisplayModal, IndicatorsDisplay, SelectColumnFilter} from '../../mollecules'
|
import {ConfigDisplayModal, IndicatorsDisplay, SelectColumnFilter} from '../../mollecules'
|
||||||
import {UnifiedTradingModal} from '../index'
|
import {UnifiedTradingModal} from '../index'
|
||||||
@@ -130,7 +130,7 @@ const ServerSortableTable = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface BacktestTableProps {
|
interface BacktestTableProps {
|
||||||
list: Backtest[] | undefined
|
list: LightBacktestResponse[] | undefined
|
||||||
isFetching?: boolean
|
isFetching?: boolean
|
||||||
displaySummary?: boolean
|
displaySummary?: boolean
|
||||||
onSortChange?: (sortBy: string, sortOrder: 'asc' | 'desc') => void
|
onSortChange?: (sortBy: string, sortOrder: 'asc' | 'desc') => void
|
||||||
@@ -139,26 +139,10 @@ interface BacktestTableProps {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const BacktestTable: React.FC<BacktestTableProps> = ({list, isFetching, displaySummary = true, onSortChange, currentSort}) => {
|
const BacktestTable: React.FC<BacktestTableProps> = ({list, isFetching, onSortChange, currentSort}) => {
|
||||||
const [rows, setRows] = useState<Backtest[]>([])
|
const [rows, setRows] = useState<LightBacktestResponse[]>([])
|
||||||
const {apiUrl} = useApiUrlStore()
|
const {apiUrl} = useApiUrlStore()
|
||||||
const {removeBacktest} = useBacktestStore()
|
const {removeBacktest} = useBacktestStore()
|
||||||
const [optimizedMoneyManagement, setOptimizedMoneyManagement] = useState({
|
|
||||||
stopLoss: 0,
|
|
||||||
takeProfit: 0,
|
|
||||||
})
|
|
||||||
const [positionTimingStats, setPositionTimingStats] = useState({
|
|
||||||
averageOpenTime: 0,
|
|
||||||
medianOpenTime: 0,
|
|
||||||
losingPositionsAverageOpenTime: 0,
|
|
||||||
})
|
|
||||||
const [cooldownRecommendations, setCooldownRecommendations] = useState({
|
|
||||||
averageCooldown: 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)
|
||||||
@@ -466,146 +450,6 @@ const BacktestTable: React.FC<BacktestTableProps> = ({list, isFetching, displayS
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (list) {
|
if (list) {
|
||||||
setRows(list)
|
setRows(list)
|
||||||
|
|
||||||
// Calculate average optimized money management
|
|
||||||
if (list.length > 0) {
|
|
||||||
const optimized = list.map((b) => b.optimizedMoneyManagement);
|
|
||||||
const stopLoss = optimized.reduce((acc, curr) => acc + (curr?.stopLoss ?? 0), 0);
|
|
||||||
const takeProfit = optimized.reduce((acc, curr) => acc + (curr?.takeProfit ?? 0), 0);
|
|
||||||
|
|
||||||
setOptimizedMoneyManagement({
|
|
||||||
stopLoss: stopLoss / optimized.length,
|
|
||||||
takeProfit: takeProfit / optimized.length,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Calculate position timing statistics
|
|
||||||
const allPositions = list.flatMap(backtest => backtest.positions);
|
|
||||||
const finishedPositions = allPositions.filter(p => p.status === 'Finished');
|
|
||||||
|
|
||||||
if (finishedPositions.length > 0) {
|
|
||||||
// Calculate position open times in hours
|
|
||||||
const openTimes = finishedPositions.map(position => {
|
|
||||||
const openTime = new Date(position.open.date);
|
|
||||||
// Find the closing trade (either stopLoss or takeProfit that was filled)
|
|
||||||
let closeTime = new Date();
|
|
||||||
|
|
||||||
if (position.stopLoss.status === 'Filled') {
|
|
||||||
closeTime = new Date(position.stopLoss.date);
|
|
||||||
} else if (position.takeProfit1.status === 'Filled') {
|
|
||||||
closeTime = new Date(position.takeProfit1.date);
|
|
||||||
} else if (position.takeProfit2?.status === 'Filled') {
|
|
||||||
closeTime = new Date(position.takeProfit2.date);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return time difference in hours
|
|
||||||
return (closeTime.getTime() - openTime.getTime()) / (1000 * 60 * 60);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Calculate average
|
|
||||||
const averageOpenTime = openTimes.reduce((sum, time) => sum + time, 0) / openTimes.length;
|
|
||||||
|
|
||||||
// Calculate median
|
|
||||||
const sortedTimes = [...openTimes].sort((a, b) => a - b);
|
|
||||||
const medianOpenTime = sortedTimes.length % 2 === 0
|
|
||||||
? (sortedTimes[sortedTimes.length / 2 - 1] + sortedTimes[sortedTimes.length / 2]) / 2
|
|
||||||
: sortedTimes[Math.floor(sortedTimes.length / 2)];
|
|
||||||
|
|
||||||
// Calculate average for losing positions
|
|
||||||
const losingPositions = finishedPositions.filter(p => (p.profitAndLoss?.realized ?? 0) < 0);
|
|
||||||
let losingPositionsAverageOpenTime = 0;
|
|
||||||
|
|
||||||
if (losingPositions.length > 0) {
|
|
||||||
const losingOpenTimes = losingPositions.map(position => {
|
|
||||||
const openTime = new Date(position.open.date);
|
|
||||||
let closeTime = new Date();
|
|
||||||
|
|
||||||
if (position.stopLoss.status === 'Filled') {
|
|
||||||
closeTime = new Date(position.stopLoss.date);
|
|
||||||
} else if (position.takeProfit1.status === 'Filled') {
|
|
||||||
closeTime = new Date(position.takeProfit1.date);
|
|
||||||
} else if (position.takeProfit2?.status === 'Filled') {
|
|
||||||
closeTime = new Date(position.takeProfit2.date);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (closeTime.getTime() - openTime.getTime()) / (1000 * 60 * 60);
|
|
||||||
});
|
|
||||||
|
|
||||||
losingPositionsAverageOpenTime = losingOpenTimes.reduce((sum, time) => sum + time, 0) / losingOpenTimes.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
setPositionTimingStats({
|
|
||||||
averageOpenTime,
|
|
||||||
medianOpenTime,
|
|
||||||
losingPositionsAverageOpenTime,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate cooldown recommendations across all backtests
|
|
||||||
const allCooldownValues: number[] = [];
|
|
||||||
|
|
||||||
list.forEach(backtest => {
|
|
||||||
if (backtest.positions.length < 2 || !backtest.candles || backtest.candles.length < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine candle timeframe in milliseconds
|
|
||||||
const candleTimeframeMs = new Date(backtest.candles[1].date).getTime() - new Date(backtest.candles[0].date).getTime();
|
|
||||||
|
|
||||||
const sortedPositions = [...backtest.positions].sort((a, b) => {
|
|
||||||
const dateA = new Date(a.open.date).getTime();
|
|
||||||
const dateB = new Date(b.open.date).getTime();
|
|
||||||
return dateA - dateB;
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let i = 0; i < sortedPositions.length - 1; i++) {
|
|
||||||
const currentPosition = sortedPositions[i];
|
|
||||||
const nextPosition = sortedPositions[i + 1];
|
|
||||||
|
|
||||||
const currentRealized = currentPosition.profitAndLoss?.realized ?? 0;
|
|
||||||
const nextRealized = nextPosition.profitAndLoss?.realized ?? 0;
|
|
||||||
|
|
||||||
// Check if current position is winning and next position is losing
|
|
||||||
if (currentRealized > 0 && nextRealized <= 0) {
|
|
||||||
// Calculate the close time of the current (winning) position
|
|
||||||
let currentCloseDate: Date | null = null;
|
|
||||||
if (currentPosition.profitAndLoss?.realized != null) {
|
|
||||||
if (currentPosition.profitAndLoss.realized > 0) {
|
|
||||||
currentCloseDate = new Date(currentPosition.takeProfit1.date);
|
|
||||||
} else {
|
|
||||||
currentCloseDate = new Date(currentPosition.stopLoss.date);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentCloseDate) {
|
|
||||||
const nextOpenDate = new Date(nextPosition.open.date);
|
|
||||||
const gapInMs = nextOpenDate.getTime() - currentCloseDate.getTime();
|
|
||||||
|
|
||||||
if (gapInMs >= 0) { // Only consider positive gaps
|
|
||||||
// Convert milliseconds to number of candles
|
|
||||||
const gapInCandles = Math.floor(gapInMs / candleTimeframeMs);
|
|
||||||
allCooldownValues.push(gapInCandles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (allCooldownValues.length > 0) {
|
|
||||||
// Calculate average cooldown
|
|
||||||
const averageCooldown = allCooldownValues.reduce((sum, value) => sum + value, 0) / allCooldownValues.length;
|
|
||||||
|
|
||||||
// Calculate median cooldown
|
|
||||||
const sortedCooldowns = [...allCooldownValues].sort((a, b) => a - b);
|
|
||||||
const medianCooldown = sortedCooldowns.length % 2 === 0
|
|
||||||
? (sortedCooldowns[sortedCooldowns.length / 2 - 1] + sortedCooldowns[sortedCooldowns.length / 2]) / 2
|
|
||||||
: sortedCooldowns[Math.floor(sortedCooldowns.length / 2)];
|
|
||||||
|
|
||||||
setCooldownRecommendations({
|
|
||||||
averageCooldown: Math.ceil(averageCooldown),
|
|
||||||
medianCooldown: Math.ceil(medianCooldown),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [list])
|
}, [list])
|
||||||
|
|
||||||
@@ -617,58 +461,6 @@ const BacktestTable: React.FC<BacktestTableProps> = ({list, isFetching, displayS
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{list && list.length > 0 && displaySummary && (
|
|
||||||
<div className="mb-4">
|
|
||||||
<button
|
|
||||||
onClick={() => setIsSummaryCollapsed(!isSummaryCollapsed)}
|
|
||||||
className="btn btn-sm btn-outline mb-2"
|
|
||||||
>
|
|
||||||
{isSummaryCollapsed ? (
|
|
||||||
<>
|
|
||||||
<ChevronRightIcon className="w-4 h-4 mr-1" />
|
|
||||||
Show Summary
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<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>
|
|
||||||
)}
|
|
||||||
<ServerSortableTable
|
<ServerSortableTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={rows}
|
data={rows}
|
||||||
|
|||||||
@@ -3794,7 +3794,7 @@ export interface DeleteBacktestsRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PaginatedBacktestsResponse {
|
export interface PaginatedBacktestsResponse {
|
||||||
backtests?: Backtest[] | null;
|
backtests?: LightBacktestResponse[] | null;
|
||||||
totalCount?: number;
|
totalCount?: number;
|
||||||
currentPage?: number;
|
currentPage?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
@@ -3803,6 +3803,21 @@ export interface PaginatedBacktestsResponse {
|
|||||||
hasPreviousPage?: boolean;
|
hasPreviousPage?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LightBacktestResponse {
|
||||||
|
id?: string | null;
|
||||||
|
config?: TradingBotConfig | null;
|
||||||
|
finalPnl?: number;
|
||||||
|
winRate?: number;
|
||||||
|
growthPercentage?: number;
|
||||||
|
hodlPercentage?: number;
|
||||||
|
startDate?: Date;
|
||||||
|
endDate?: Date;
|
||||||
|
maxDrawdown?: number | null;
|
||||||
|
fees?: number;
|
||||||
|
sharpeRatio?: number | null;
|
||||||
|
score?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface RunBacktestRequest {
|
export interface RunBacktestRequest {
|
||||||
config?: TradingBotConfigRequest | null;
|
config?: TradingBotConfigRequest | null;
|
||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
|
|||||||
@@ -600,7 +600,7 @@ export interface DeleteBacktestsRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PaginatedBacktestsResponse {
|
export interface PaginatedBacktestsResponse {
|
||||||
backtests?: Backtest[] | null;
|
backtests?: LightBacktestResponse[] | null;
|
||||||
totalCount?: number;
|
totalCount?: number;
|
||||||
currentPage?: number;
|
currentPage?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
@@ -609,6 +609,21 @@ export interface PaginatedBacktestsResponse {
|
|||||||
hasPreviousPage?: boolean;
|
hasPreviousPage?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LightBacktestResponse {
|
||||||
|
id?: string | null;
|
||||||
|
config?: TradingBotConfig | null;
|
||||||
|
finalPnl?: number;
|
||||||
|
winRate?: number;
|
||||||
|
growthPercentage?: number;
|
||||||
|
hodlPercentage?: number;
|
||||||
|
startDate?: Date;
|
||||||
|
endDate?: Date;
|
||||||
|
maxDrawdown?: number | null;
|
||||||
|
fees?: number;
|
||||||
|
sharpeRatio?: number | null;
|
||||||
|
score?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface RunBacktestRequest {
|
export interface RunBacktestRequest {
|
||||||
config?: TradingBotConfigRequest | null;
|
config?: TradingBotConfigRequest | null;
|
||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import {useQuery} from '@tanstack/react-query'
|
|||||||
|
|
||||||
import useApiUrlStore from '../../app/store/apiStore'
|
import useApiUrlStore from '../../app/store/apiStore'
|
||||||
import {
|
import {
|
||||||
type Backtest,
|
|
||||||
BacktestClient,
|
BacktestClient,
|
||||||
GeneticCrossoverMethod,
|
GeneticCrossoverMethod,
|
||||||
GeneticMutationMethod,
|
GeneticMutationMethod,
|
||||||
type GeneticRequest,
|
type GeneticRequest,
|
||||||
GeneticSelectionMethod,
|
GeneticSelectionMethod,
|
||||||
IndicatorType,
|
IndicatorType,
|
||||||
|
type LightBacktestResponse,
|
||||||
type PaginatedBacktestsResponse,
|
type PaginatedBacktestsResponse,
|
||||||
type RunGeneticRequest,
|
type RunGeneticRequest,
|
||||||
Ticker,
|
Ticker,
|
||||||
@@ -73,7 +73,7 @@ const BacktestGeneticBundle: React.FC = () => {
|
|||||||
const [geneticRequests, setGeneticRequests] = useState<GeneticRequest[]>([])
|
const [geneticRequests, setGeneticRequests] = useState<GeneticRequest[]>([])
|
||||||
const [selectedRequest, setSelectedRequest] = useState<GeneticRequest | null>(null)
|
const [selectedRequest, setSelectedRequest] = useState<GeneticRequest | null>(null)
|
||||||
const [isViewModalOpen, setIsViewModalOpen] = useState(false)
|
const [isViewModalOpen, setIsViewModalOpen] = useState(false)
|
||||||
const [backtests, setBacktests] = useState<Backtest[]>([])
|
const [backtests, setBacktests] = useState<LightBacktestResponse[]>([])
|
||||||
const [isLoadingBacktests, setIsLoadingBacktests] = useState(false)
|
const [isLoadingBacktests, setIsLoadingBacktests] = useState(false)
|
||||||
const [isFormCollapsed, setIsFormCollapsed] = useState(false)
|
const [isFormCollapsed, setIsFormCollapsed] = useState(false)
|
||||||
|
|
||||||
@@ -824,7 +824,7 @@ const BacktestGeneticBundle: React.FC = () => {
|
|||||||
<div className="card bg-base-100 shadow-xl">
|
<div className="card bg-base-100 shadow-xl">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<h3 className="card-title">Score vs Generation</h3>
|
<h3 className="card-title">Score vs Generation</h3>
|
||||||
<ScoreVsGeneration backtests={backtests} theme={theme}/>
|
<ScoreVsGeneration backtests={backtests as any} theme={theme}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -834,7 +834,7 @@ const BacktestGeneticBundle: React.FC = () => {
|
|||||||
<div className="card bg-base-100 shadow-xl">
|
<div className="card bg-base-100 shadow-xl">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<h3 className="card-title">Fitness vs Score vs Win Rate</h3>
|
<h3 className="card-title">Fitness vs Score vs Win Rate</h3>
|
||||||
<Fitness3DPlot backtests={backtests} theme={theme}/>
|
<Fitness3DPlot backtests={backtests as any} theme={theme}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -842,18 +842,18 @@ const BacktestGeneticBundle: React.FC = () => {
|
|||||||
<div className="card bg-base-100 shadow-xl">
|
<div className="card bg-base-100 shadow-xl">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<h3 className="card-title">Take Profit vs Stop Loss vs PnL</h3>
|
<h3 className="card-title">Take Profit vs Stop Loss vs PnL</h3>
|
||||||
<TPvsSLvsPnL3DPlot backtests={backtests} theme={theme}/>
|
<TPvsSLvsPnL3DPlot backtests={backtests as any} theme={theme}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Strategy Comparison Radar Chart */}
|
{/* Strategy Comparison Radar Chart */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<IndicatorsComparison backtests={backtests}/>
|
<IndicatorsComparison backtests={backtests as any}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BacktestTable
|
<BacktestTable
|
||||||
list={backtests}
|
list={backtests as any}
|
||||||
isFetching={false}
|
isFetching={false}
|
||||||
displaySummary={false}
|
displaySummary={false}
|
||||||
onSortChange={handleSortChange}
|
onSortChange={handleSortChange}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import useBacktestStore from '../../app/store/backtestStore'
|
|||||||
import {Loader, Slider} from '../../components/atoms'
|
import {Loader, Slider} from '../../components/atoms'
|
||||||
import {Modal, Toast} from '../../components/mollecules'
|
import {Modal, Toast} from '../../components/mollecules'
|
||||||
import {BacktestTable, UnifiedTradingModal} from '../../components/organism'
|
import {BacktestTable, UnifiedTradingModal} from '../../components/organism'
|
||||||
import type {Backtest} from '../../generated/ManagingApi'
|
import type {LightBacktestResponse} from '../../generated/ManagingApi'
|
||||||
import {BacktestClient} from '../../generated/ManagingApi'
|
import {BacktestClient} from '../../generated/ManagingApi'
|
||||||
|
|
||||||
const PAGE_SIZE = 50
|
const PAGE_SIZE = 50
|
||||||
@@ -27,7 +27,7 @@ const BacktestScanner: React.FC = () => {
|
|||||||
sortBy: 'score',
|
sortBy: 'score',
|
||||||
sortOrder: 'desc'
|
sortOrder: 'desc'
|
||||||
})
|
})
|
||||||
const [backtests, setBacktests] = useState<Backtest[]>([])
|
const [backtests, setBacktests] = useState<LightBacktestResponse[]>([])
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
|
||||||
const { apiUrl } = useApiUrlStore()
|
const { apiUrl } = useApiUrlStore()
|
||||||
@@ -39,7 +39,7 @@ const BacktestScanner: React.FC = () => {
|
|||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
try {
|
try {
|
||||||
const response = await backtestClient.backtest_GetBacktestsPaginated(page, PAGE_SIZE, sort.sortBy, sort.sortOrder)
|
const response = await backtestClient.backtest_GetBacktestsPaginated(page, PAGE_SIZE, sort.sortBy, sort.sortOrder)
|
||||||
setBacktests((response.backtests as Backtest[]) || [])
|
setBacktests((response.backtests as LightBacktestResponse[]) || [])
|
||||||
setTotalBacktests(response.totalCount || 0)
|
setTotalBacktests(response.totalCount || 0)
|
||||||
setTotalPages(response.totalPages || 0)
|
setTotalPages(response.totalPages || 0)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -55,7 +55,7 @@ const BacktestScanner: React.FC = () => {
|
|||||||
}, [currentPage, currentSort])
|
}, [currentPage, currentSort])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBacktestsFromStore(backtests)
|
setBacktestsFromStore(backtests as any) // Cast to any for backward compatibility
|
||||||
setLoading(isLoading)
|
setLoading(isLoading)
|
||||||
}, [backtests, setBacktestsFromStore, setLoading, isLoading])
|
}, [backtests, setBacktestsFromStore, setLoading, isLoading])
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ const BacktestScanner: React.FC = () => {
|
|||||||
|
|
||||||
const filters = formData || filterValues
|
const filters = formData || filterValues
|
||||||
|
|
||||||
const filteredBacktests = backtests.filter((backtest: any) => {
|
const filteredBacktests = backtests.filter((backtest: LightBacktestResponse) => {
|
||||||
// Ensure values are numbers and handle potential null/undefined values
|
// Ensure values are numbers and handle potential null/undefined values
|
||||||
const backtestWinRate = Number(backtest.winRate) || 0
|
const backtestWinRate = Number(backtest.winRate) || 0
|
||||||
const backtestScore = Number(backtest.score) || 0
|
const backtestScore = Number(backtest.score) || 0
|
||||||
@@ -129,7 +129,7 @@ const BacktestScanner: React.FC = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const backTestToDelete = backtests.filter((backtest: any) => {
|
const backTestToDelete = backtests.filter((backtest: LightBacktestResponse) => {
|
||||||
// Ensure values are numbers and handle potential null/undefined values
|
// Ensure values are numbers and handle potential null/undefined values
|
||||||
const backtestWinRate = Number(backtest.winRate) || 0
|
const backtestWinRate = Number(backtest.winRate) || 0
|
||||||
const backtestScore = Number(backtest.score) || 0
|
const backtestScore = Number(backtest.score) || 0
|
||||||
@@ -198,7 +198,7 @@ const BacktestScanner: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BacktestTable
|
<BacktestTable
|
||||||
list={backtests}
|
list={backtests as LightBacktestResponse[]} // Cast to any for backward compatibility
|
||||||
isFetching={isLoading}
|
isFetching={isLoading}
|
||||||
onSortChange={handleSortChange}
|
onSortChange={handleSortChange}
|
||||||
currentSort={currentSort}
|
currentSort={currentSort}
|
||||||
|
|||||||
Reference in New Issue
Block a user