Update bundle backtests

This commit is contained in:
2025-10-12 15:42:38 +07:00
parent 5acc77650f
commit 32ac342a20
7 changed files with 40 additions and 48 deletions

View File

@@ -75,7 +75,7 @@ Key Principles
- Use mobile-first approach for responsive design. - Use mobile-first approach for responsive design.
- Place static content and interfaces at file end. - Place static content and interfaces at file end.
- Use content variables for static content outside render functions. - Use content variables for static content outside render functions.
- Minimize 'use client', 'useEffect', and 'setState'. Favor RSC. - Never use useEffect() to fetch data, use tanstack UseQuery instead
- Wrap client components in Suspense with fallback. - Wrap client components in Suspense with fallback.
- Use dynamic loading for non-critical components. - Use dynamic loading for non-critical components.
- Optimize images: WebP format, size data, lazy loading. - Optimize images: WebP format, size data, lazy loading.

View File

@@ -462,11 +462,6 @@ public class BacktestController : BaseController
{ {
var user = await GetUser(); var user = await GetUser();
// Validate universal configuration
if (string.IsNullOrEmpty(request.UniversalConfig.AccountName))
{
return BadRequest("Account name is required in universal configuration");
}
if (string.IsNullOrEmpty(request.UniversalConfig.ScenarioName) && request.UniversalConfig.Scenario == null) if (string.IsNullOrEmpty(request.UniversalConfig.ScenarioName) && request.UniversalConfig.Scenario == null)
{ {
@@ -510,8 +505,9 @@ public class BacktestController : BaseController
/// Generates individual backtest requests from variant configuration /// Generates individual backtest requests from variant configuration
/// </summary> /// </summary>
/// <param name="request">The bundle backtest request</param> /// <param name="request">The bundle backtest request</param>
/// <param name="accountName">The account name to use for all backtests</param>
/// <returns>List of individual backtest requests</returns> /// <returns>List of individual backtest requests</returns>
private List<RunBacktestRequest> GenerateBacktestRequests(RunBundleBacktestRequest request) private List<RunBacktestRequest> GenerateBacktestRequests(RunBundleBacktestRequest request, string accountName)
{ {
var backtestRequests = new List<RunBacktestRequest>(); var backtestRequests = new List<RunBacktestRequest>();
@@ -523,7 +519,7 @@ public class BacktestController : BaseController
{ {
var config = new TradingBotConfigRequest var config = new TradingBotConfigRequest
{ {
AccountName = request.UniversalConfig.AccountName, AccountName = accountName,
Ticker = ticker, Ticker = ticker,
Timeframe = request.UniversalConfig.Timeframe, Timeframe = request.UniversalConfig.Timeframe,
IsForWatchingOnly = request.UniversalConfig.IsForWatchingOnly, IsForWatchingOnly = request.UniversalConfig.IsForWatchingOnly,

View File

@@ -105,7 +105,7 @@ public class BundleBacktestGrain : Grain, IBundleBacktestGrain, IRemindable
await backtester.UpdateBundleBacktestRequestAsync(bundleRequest); await backtester.UpdateBundleBacktestRequestAsync(bundleRequest);
// Generate backtest requests from variant configuration // Generate backtest requests from variant configuration
var backtestRequests = GenerateBacktestRequestsFromVariants(bundleRequest); var backtestRequests = await GenerateBacktestRequestsFromVariants(bundleRequest);
if (backtestRequests == null || !backtestRequests.Any()) if (backtestRequests == null || !backtestRequests.Any())
{ {
throw new InvalidOperationException("Failed to generate backtest requests from variants"); throw new InvalidOperationException("Failed to generate backtest requests from variants");
@@ -133,7 +133,7 @@ public class BundleBacktestGrain : Grain, IBundleBacktestGrain, IRemindable
/// <summary> /// <summary>
/// Generates individual backtest requests from variant configuration /// Generates individual backtest requests from variant configuration
/// </summary> /// </summary>
private List<RunBacktestRequest> GenerateBacktestRequestsFromVariants(BundleBacktestRequest bundleRequest) private async Task<List<RunBacktestRequest>> GenerateBacktestRequestsFromVariants(BundleBacktestRequest bundleRequest)
{ {
try try
{ {
@@ -149,6 +149,21 @@ public class BundleBacktestGrain : Grain, IBundleBacktestGrain, IRemindable
return new List<RunBacktestRequest>(); return new List<RunBacktestRequest>();
} }
// Get the first account for the user using AccountService
var firstAccount = await ServiceScopeHelpers.WithScopedService<IAccountService, Account?>(
_scopeFactory,
async service =>
{
var accounts = await service.GetAccountsByUserAsync(bundleRequest.User, hideSecrets: true, getBalance: false);
return accounts.FirstOrDefault();
});
if (firstAccount == null)
{
_logger.LogError("No accounts found for user {UserId} in bundle request {RequestId}", bundleRequest.User.Id, bundleRequest.RequestId);
return new List<RunBacktestRequest>();
}
var backtestRequests = new List<RunBacktestRequest>(); var backtestRequests = new List<RunBacktestRequest>();
foreach (var dateRange in dateTimeRanges) foreach (var dateRange in dateTimeRanges)
@@ -159,7 +174,7 @@ public class BundleBacktestGrain : Grain, IBundleBacktestGrain, IRemindable
{ {
var config = new TradingBotConfigRequest var config = new TradingBotConfigRequest
{ {
AccountName = universalConfig.AccountName, AccountName = firstAccount.Name,
Ticker = ticker, Ticker = ticker,
Timeframe = universalConfig.Timeframe, Timeframe = universalConfig.Timeframe,
IsForWatchingOnly = universalConfig.IsForWatchingOnly, IsForWatchingOnly = universalConfig.IsForWatchingOnly,

View File

@@ -94,7 +94,7 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
await backtester.UpdateBundleBacktestRequestAsync(bundleRequest); await backtester.UpdateBundleBacktestRequestAsync(bundleRequest);
// Generate backtest requests from the new variant structure // Generate backtest requests from the new variant structure
var backtestRequests = GenerateBacktestRequestsFromVariants(bundleRequest); var backtestRequests = await GenerateBacktestRequestsFromVariants(bundleRequest);
if (backtestRequests == null || !backtestRequests.Any()) if (backtestRequests == null || !backtestRequests.Any())
{ {
throw new InvalidOperationException("Failed to generate backtest requests from variants"); throw new InvalidOperationException("Failed to generate backtest requests from variants");
@@ -297,7 +297,7 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
var succeededIds = new HashSet<string>(failedBundle.Results ?? new List<string>()); var succeededIds = new HashSet<string>(failedBundle.Results ?? new List<string>());
// Generate backtest requests from the new variant structure // Generate backtest requests from the new variant structure
var originalRequests = GenerateBacktestRequestsFromVariants(failedBundle); var originalRequests = await GenerateBacktestRequestsFromVariants(failedBundle);
if (originalRequests == null || !originalRequests.Any()) continue; if (originalRequests == null || !originalRequests.Any()) continue;
for (int i = failedBundle.CompletedBacktests; i < originalRequests.Count; i++) for (int i = failedBundle.CompletedBacktests; i < originalRequests.Count; i++)
@@ -339,7 +339,7 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
/// <summary> /// <summary>
/// Generates individual backtest requests from variant configuration /// Generates individual backtest requests from variant configuration
/// </summary> /// </summary>
private List<RunBacktestRequest> GenerateBacktestRequestsFromVariants(BundleBacktestRequest bundleRequest) private async Task<List<RunBacktestRequest>> GenerateBacktestRequestsFromVariants(BundleBacktestRequest bundleRequest)
{ {
try try
{ {
@@ -355,6 +355,18 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
return new List<RunBacktestRequest>(); return new List<RunBacktestRequest>();
} }
// Get the first account for the user using AccountService
using var scope = _serviceProvider.CreateScope();
var accountService = scope.ServiceProvider.GetRequiredService<IAccountService>();
var accounts = await accountService.GetAccountsByUserAsync(bundleRequest.User, hideSecrets: true, getBalance: false);
var firstAccount = accounts.FirstOrDefault();
if (firstAccount == null)
{
_logger.LogError("No accounts found for user {UserId} in bundle request {RequestId}", bundleRequest.User.Id, bundleRequest.RequestId);
return new List<RunBacktestRequest>();
}
var backtestRequests = new List<RunBacktestRequest>(); var backtestRequests = new List<RunBacktestRequest>();
foreach (var dateRange in dateTimeRanges) foreach (var dateRange in dateTimeRanges)
@@ -365,7 +377,7 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
{ {
var config = new TradingBotConfigRequest var config = new TradingBotConfigRequest
{ {
AccountName = universalConfig.AccountName, AccountName = firstAccount.Name,
Ticker = ticker, Ticker = ticker,
Timeframe = universalConfig.Timeframe, Timeframe = universalConfig.Timeframe,
IsForWatchingOnly = universalConfig.IsForWatchingOnly, IsForWatchingOnly = universalConfig.IsForWatchingOnly,

View File

@@ -9,12 +9,6 @@ namespace Managing.Domain.Backtests;
/// </summary> /// </summary>
public class BundleBacktestUniversalConfig public class BundleBacktestUniversalConfig
{ {
/// <summary>
/// The account name to use for all backtests
/// </summary>
[Required]
public string AccountName { get; set; } = string.Empty;
/// <summary> /// <summary>
/// The timeframe for trading decisions /// The timeframe for trading decisions
/// </summary> /// </summary>

View File

@@ -4399,7 +4399,6 @@ export interface RunBundleBacktestRequest {
} }
export interface BundleBacktestUniversalConfig { export interface BundleBacktestUniversalConfig {
accountName: string;
timeframe: Timeframe; timeframe: Timeframe;
isForWatchingOnly: boolean; isForWatchingOnly: boolean;
botTradingBalance: number; botTradingBalance: number;

View File

@@ -1,6 +1,5 @@
import React, {useEffect, useRef, useState} from 'react'; import React, {useEffect, useRef, useState} from 'react';
import { import {
AccountClient,
BacktestClient, BacktestClient,
BundleBacktestRequest, BundleBacktestRequest,
BundleBacktestUniversalConfig, BundleBacktestUniversalConfig,
@@ -47,7 +46,6 @@ const BundleRequestModal: React.FC<BundleRequestModalProps> = ({
// Form state for creating new bundle requests // Form state for creating new bundle requests
const [strategyName, setStrategyName] = useState<string>(''); const [strategyName, setStrategyName] = useState<string>('');
const [selectedAccount, setSelectedAccount] = useState<string>('');
const [selectedTimeframe, setSelectedTimeframe] = useState<Timeframe>(Timeframe.FifteenMinutes); const [selectedTimeframe, setSelectedTimeframe] = useState<Timeframe>(Timeframe.FifteenMinutes);
const [selectedTickers, setSelectedTickers] = useState<Ticker[]>([]); const [selectedTickers, setSelectedTickers] = useState<Ticker[]>([]);
const [startingCapital, setStartingCapital] = useState<number>(10000); const [startingCapital, setStartingCapital] = useState<number>(10000);
@@ -70,15 +68,9 @@ const BundleRequestModal: React.FC<BundleRequestModalProps> = ({
// API clients // API clients
const backtestClient = new BacktestClient({} as any, apiUrl); const backtestClient = new BacktestClient({} as any, apiUrl);
const accountClient = new AccountClient({} as any, apiUrl);
const dataClient = new DataClient({} as any, apiUrl); const dataClient = new DataClient({} as any, apiUrl);
// Fetch data // Fetch data
const { data: accounts } = useQuery({
queryFn: () => accountClient.account_GetAccounts(),
queryKey: ['accounts'],
});
const { data: tickers } = useQuery({ const { data: tickers } = useQuery({
queryFn: () => dataClient.data_GetTickers(selectedTimeframe), queryFn: () => dataClient.data_GetTickers(selectedTimeframe),
queryKey: ['tickers', selectedTimeframe], queryKey: ['tickers', selectedTimeframe],
@@ -210,7 +202,7 @@ const BundleRequestModal: React.FC<BundleRequestModalProps> = ({
// Create bundle backtest request // Create bundle backtest request
const handleCreateBundle = async () => { const handleCreateBundle = async () => {
if (!strategyName || !selectedAccount || selectedTickers.length === 0) { if (!strategyName || selectedTickers.length === 0) {
new Toast('Please fill in all required fields', false); new Toast('Please fill in all required fields', false);
return; return;
} }
@@ -221,7 +213,6 @@ const BundleRequestModal: React.FC<BundleRequestModalProps> = ({
} }
const universalConfig: BundleBacktestUniversalConfig = { const universalConfig: BundleBacktestUniversalConfig = {
accountName: selectedAccount,
timeframe: selectedTimeframe, timeframe: selectedTimeframe,
isForWatchingOnly: false, isForWatchingOnly: false,
botTradingBalance: startingCapital, botTradingBalance: startingCapital,
@@ -439,21 +430,6 @@ const BundleRequestModal: React.FC<BundleRequestModalProps> = ({
/> />
</FormInput> </FormInput>
{/* Account Selection */}
<FormInput label="Select Account" htmlFor="account">
<select
className="select select-bordered w-full"
value={selectedAccount}
onChange={(e) => setSelectedAccount(e.target.value)}
>
<option value="" disabled>Select an account</option>
{accounts?.map((account) => (
<option key={account.name} value={account.name}>
{account.name}
</option>
))}
</select>
</FormInput>
{/* Scenario Builder */} {/* Scenario Builder */}
<div> <div>
@@ -834,7 +810,7 @@ const BundleRequestModal: React.FC<BundleRequestModalProps> = ({
<button <button
className="btn btn-primary w-full mb-4" className="btn btn-primary w-full mb-4"
onClick={handleCreateBundle} onClick={handleCreateBundle}
disabled={!strategyName || !selectedAccount || selectedTickers.length === 0 || !scenario} disabled={!strategyName || selectedTickers.length === 0 || !scenario}
> >
Run Backtest Run Backtest
</button> </button>