Update bundle backtests
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user