diff --git a/src/Managing.Api/Controllers/BotController.cs b/src/Managing.Api/Controllers/BotController.cs index 8c79bd9a..1bd68680 100644 --- a/src/Managing.Api/Controllers/BotController.cs +++ b/src/Managing.Api/Controllers/BotController.cs @@ -255,7 +255,7 @@ public class BotController : BaseController catch (Exception ex) { _logger.LogError(ex, "Error stopping bot {Identifier}", identifier); - + // Check if this is an Orleans exception that wasn't caught earlier if (OrleansExceptionHelper.IsOrleansException(ex)) { @@ -265,7 +265,7 @@ public class BotController : BaseController identifier, ex.GetType().Name); return StatusCode(503, userMessage); } - + return StatusCode(500, $"Error stopping bot: {ex.Message}"); } } @@ -814,7 +814,7 @@ public class BotController : BaseController UseForSignalFiltering = request.Config.UseForSignalFiltering, UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss, // Set computed/default properties - TradingType = TradingType.Futures, + TradingType = request.Config.TradingType, FlipPosition = request.Config.FlipPosition, Name = request.Config.Name }; @@ -955,6 +955,13 @@ public class BotController : BaseController } } + var tradingType = request.Config.TradingType switch + { + TradingType.BacktestFutures => TradingType.Futures, + TradingType.BacktestSpot => TradingType.Spot, + _ => TradingType.Futures + }; + // Map the request to the full TradingBotConfig var config = new TradingBotConfig { @@ -976,9 +983,9 @@ public class BotController : BaseController UseForSignalFiltering = request.Config.UseForSignalFiltering, UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss, // Set computed/default properties - TradingType = TradingType.Futures, FlipPosition = request.Config.FlipPosition, - Name = request.Config.Name + Name = request.Config.Name, + TradingType = tradingType }; return (config, user); diff --git a/src/Managing.Application/Backtests/BacktestJobService.cs b/src/Managing.Application/Backtests/BacktestJobService.cs index 6793707f..bf40f15e 100644 --- a/src/Managing.Application/Backtests/BacktestJobService.cs +++ b/src/Managing.Application/Backtests/BacktestJobService.cs @@ -197,7 +197,7 @@ public class JobService Timeframe = backtestRequest.Config.Timeframe, IsForWatchingOnly = backtestRequest.Config.IsForWatchingOnly, BotTradingBalance = backtestRequest.Config.BotTradingBalance, - TradingType = TradingType.BacktestFutures, + TradingType = backtestRequest.Config.TradingType, CooldownPeriod = backtestRequest.Config.CooldownPeriod ?? 1, MaxLossStreak = backtestRequest.Config.MaxLossStreak, MaxPositionTimeHours = backtestRequest.Config.MaxPositionTimeHours, diff --git a/src/Managing.Application/Backtests/Backtester.cs b/src/Managing.Application/Backtests/Backtester.cs index 13b97050..98f0140f 100644 --- a/src/Managing.Application/Backtests/Backtester.cs +++ b/src/Managing.Application/Backtests/Backtester.cs @@ -447,7 +447,8 @@ namespace Managing.Application.Backtests UseSynthApi = universalConfig.UseSynthApi, UseForPositionSizing = universalConfig.UseForPositionSizing, UseForSignalFiltering = universalConfig.UseForSignalFiltering, - UseForDynamicStopLoss = universalConfig.UseForDynamicStopLoss + UseForDynamicStopLoss = universalConfig.UseForDynamicStopLoss, + TradingType = universalConfig.TradingType }; var backtestRequest = new RunBacktestRequest diff --git a/src/Managing.Application/Bots/SpotBot.cs b/src/Managing.Application/Bots/SpotBot.cs index 54a5c1db..ea7b04b7 100644 --- a/src/Managing.Application/Bots/SpotBot.cs +++ b/src/Managing.Application/Bots/SpotBot.cs @@ -544,6 +544,18 @@ public class SpotBot : TradingBotBase, ITradingBot return; } + // Ensure account is loaded before accessing Account.Exchange + if (Account == null) + { + Logger.LogWarning("Cannot update signals: Account is null. Loading account..."); + await LoadAccountAsync(); + if (Account == null) + { + Logger.LogError("Cannot update signals: Account failed to load"); + return; + } + } + // Live trading: use ScenarioRunnerGrain to get signals await ServiceScopeHelpers.WithScopedService(_scopeFactory, async grainFactory => { diff --git a/src/Managing.Application/ManageBot/StartCopyTradingCommandHandler.cs b/src/Managing.Application/ManageBot/StartCopyTradingCommandHandler.cs index 75206523..b599376f 100644 --- a/src/Managing.Application/ManageBot/StartCopyTradingCommandHandler.cs +++ b/src/Managing.Application/ManageBot/StartCopyTradingCommandHandler.cs @@ -187,7 +187,7 @@ namespace Managing.Application.ManageBot MasterBotUserId = masterBot.User.Id, // Set computed/default properties - TradingType = TradingType.Futures, + TradingType = masterConfig.TradingType, Name = masterConfig.Name }; diff --git a/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs b/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs index c3fc5bf7..5e14b003 100644 --- a/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs +++ b/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs @@ -60,11 +60,24 @@ public class ScenarioRunnerGrain : Grain, IScenarioRunnerGrain { try { + if (candle == null) + { + _logger.LogWarning($"Cannot generate signals: candle is null for {config.Ticker} for {config.Name}"); + return null; + } + var candlesHashSet = await GetCandlesAsync(tradingExchanges, config); // Convert to ordered List to preserve chronological order for indicators var candlesList = candlesHashSet.OrderBy(c => c.Date).ToList(); - if (candlesList.LastOrDefault()!.Date <= candle.Date) + if (candlesList.Count == 0) + { + _logger.LogWarning($"No candles available for {config.Ticker} for {config.Name}"); + return null; // No candles available, cannot generate signal + } + + var lastCandle = candlesList.Last(); + if (lastCandle.Date <= candle.Date) { _logger.LogWarning($"No new candles for {config.Ticker} for {config.Name}"); return null; // No new candles, no need to generate a signal diff --git a/src/Managing.Application/Workers/BundleBacktestHealthCheckWorker.cs b/src/Managing.Application/Workers/BundleBacktestHealthCheckWorker.cs index 0a9a611a..b27e5105 100644 --- a/src/Managing.Application/Workers/BundleBacktestHealthCheckWorker.cs +++ b/src/Managing.Application/Workers/BundleBacktestHealthCheckWorker.cs @@ -22,7 +22,10 @@ public class BundleBacktestHealthCheckWorker : BackgroundService private readonly ILogger _logger; private readonly TimeSpan _checkInterval = TimeSpan.FromMinutes(30); private readonly TimeSpan _inactiveThreshold = TimeSpan.FromMinutes(2); // Check bundles inactive for 2+ minutes - private readonly TimeSpan _stuckThreshold = TimeSpan.FromHours(2); // Consider bundle stuck if no progress for 2 hours + + private readonly TimeSpan + _stuckThreshold = TimeSpan.FromHours(2); // Consider bundle stuck if no progress for 2 hours + private readonly IMessengerService _messengerService; public BundleBacktestHealthCheckWorker( @@ -80,15 +83,17 @@ public class BundleBacktestHealthCheckWorker : BackgroundService _logger.LogInformation("Starting bundle health check..."); // Check bundles in Pending and Running status - var pendingBundles = await backtestRepository.GetBundleBacktestRequestsByStatusAsync(BundleBacktestRequestStatus.Pending); - var runningBundles = await backtestRepository.GetBundleBacktestRequestsByStatusAsync(BundleBacktestRequestStatus.Running); + var pendingBundles = + await backtestRepository.GetBundleBacktestRequestsByStatusAsync(BundleBacktestRequestStatus.Pending); + var runningBundles = + await backtestRepository.GetBundleBacktestRequestsByStatusAsync(BundleBacktestRequestStatus.Running); // Only check bundles that haven't been updated in more than the inactive threshold var inactiveThresholdTime = DateTime.UtcNow.Add(-_inactiveThreshold); var allBundlesToCheck = pendingBundles.Concat(runningBundles) .Where(b => b.UpdatedAt < inactiveThresholdTime) .ToList(); - + _logger.LogInformation( "Found {TotalCount} bundles (from {PendingTotal} pending and {RunningTotal} running) that haven't been updated in >{InactiveMinutes} minutes", allBundlesToCheck.Count, pendingBundles.Count(), runningBundles.Count(), _inactiveThreshold.TotalMinutes); @@ -106,15 +111,15 @@ public class BundleBacktestHealthCheckWorker : BackgroundService { var (stuckCount, missingJobs, healthyCount) = await CheckSingleBundleHealthAsync( bundle, backtestRepository, jobRepository); - + stuckBundlesCount += stuckCount; missingJobsCount += missingJobs; healthyBundlesCount += healthyCount; } catch (Exception ex) { - _logger.LogError(ex, - "Error checking health for bundle {BundleRequestId}", + _logger.LogError(ex, + "Error checking health for bundle {BundleRequestId}", bundle.RequestId); } } @@ -138,7 +143,8 @@ public class BundleBacktestHealthCheckWorker : BackgroundService _logger.LogDebug( "Bundle {BundleRequestId} ({Status}): Expected {Expected} jobs, Found {Actual} jobs, Completed {Completed}/{Total}", - bundle.RequestId, bundle.Status, expectedJobCount, actualJobCount, bundle.CompletedBacktests, bundle.TotalBacktests); + bundle.RequestId, bundle.Status, expectedJobCount, actualJobCount, bundle.CompletedBacktests, + bundle.TotalBacktests); // Check 1: Missing jobs - bundle has no jobs or fewer jobs than expected if (actualJobCount == 0 || actualJobCount < expectedJobCount) @@ -151,7 +157,7 @@ public class BundleBacktestHealthCheckWorker : BackgroundService if (bundle.Status == BundleBacktestRequestStatus.Running) { var timeSinceUpdate = DateTime.UtcNow - bundle.UpdatedAt; - + if (timeSinceUpdate > _stuckThreshold) { await HandleStuckBundleAsync(bundle, timeSinceUpdate, jobs, backtestRepository, jobRepository); @@ -172,7 +178,8 @@ public class BundleBacktestHealthCheckWorker : BackgroundService if (allJobsPending || hasFailedJobs) { - await HandleStalePendingBundleAsync(bundle, timeSinceCreation, jobs, backtestRepository, jobRepository); + await HandleStalePendingBundleAsync(bundle, timeSinceCreation, jobs, backtestRepository, + jobRepository); return (StuckCount: 1, MissingJobsCount: 0, HealthyCount: 0); } } @@ -184,7 +191,8 @@ public class BundleBacktestHealthCheckWorker : BackgroundService var totalProcessedJobs = completedJobs + failedJobs; if (totalProcessedJobs == bundle.TotalBacktests && - (bundle.Status == BundleBacktestRequestStatus.Running || bundle.Status == BundleBacktestRequestStatus.Pending)) + (bundle.Status == BundleBacktestRequestStatus.Running || + bundle.Status == BundleBacktestRequestStatus.Pending)) { await HandleCompletedBundleAsync(bundle, completedJobs, failedJobs, backtestRepository); return (StuckCount: 0, MissingJobsCount: 0, HealthyCount: 1); @@ -212,13 +220,13 @@ public class BundleBacktestHealthCheckWorker : BackgroundService // Generate all backtest requests from bundle variants var allBacktestRequests = await GenerateBacktestRequestsFromVariants(bundle); - + if (allBacktestRequests == null || !allBacktestRequests.Any()) { _logger.LogError( "Failed to generate backtest requests from variants for bundle {BundleRequestId}", bundle.RequestId); - + bundle.ErrorMessage = $"Failed to regenerate jobs: Could not generate backtest requests from variants."; bundle.UpdatedAt = DateTime.UtcNow; await backtestRepository.UpdateBundleBacktestRequestAsync(bundle); @@ -227,13 +235,13 @@ public class BundleBacktestHealthCheckWorker : BackgroundService // Get existing jobs for this bundle var existingJobs = (await jobRepository.GetByBundleRequestIdAsync(bundle.RequestId)).ToList(); - + // Create a set of existing job config signatures for quick lookup var existingJobSignatures = existingJobs .Select(j => { var config = JsonSerializer.Deserialize(j.ConfigJson); - return config != null + return config != null ? $"{config.Ticker}_{config.Timeframe}_{config.Name}_{j.StartDate:yyyyMMdd}_{j.EndDate:yyyyMMdd}" : null; }) @@ -244,7 +252,8 @@ public class BundleBacktestHealthCheckWorker : BackgroundService var missingRequests = allBacktestRequests .Where(req => { - var signature = $"{req.Config.Ticker}_{req.Config.Timeframe}_{req.Config.Name}_{req.StartDate:yyyyMMdd}_{req.EndDate:yyyyMMdd}"; + var signature = + $"{req.Config.Ticker}_{req.Config.Timeframe}_{req.Config.Name}_{req.StartDate:yyyyMMdd}_{req.EndDate:yyyyMMdd}"; return !existingJobSignatures.Contains(signature); }) .ToList(); @@ -269,7 +278,7 @@ public class BundleBacktestHealthCheckWorker : BackgroundService bundle.ErrorMessage = null; bundle.CompletedAt = null; } - + bundle.UpdatedAt = DateTime.UtcNow; await backtestRepository.UpdateBundleBacktestRequestAsync(bundle); @@ -282,7 +291,7 @@ public class BundleBacktestHealthCheckWorker : BackgroundService _logger.LogWarning( "No missing jobs found to recreate for bundle {BundleRequestId}. All {ExpectedCount} jobs already exist.", bundle.RequestId, expectedJobCount); - + bundle.UpdatedAt = DateTime.UtcNow; await backtestRepository.UpdateBundleBacktestRequestAsync(bundle); } @@ -292,7 +301,7 @@ public class BundleBacktestHealthCheckWorker : BackgroundService _logger.LogError(ex, "Error recreating missing jobs for bundle {BundleRequestId}", bundle.RequestId); - + bundle.ErrorMessage = $"Error recreating jobs: {ex.Message}"; bundle.UpdatedAt = DateTime.UtcNow; await backtestRepository.UpdateBundleBacktestRequestAsync(bundle); @@ -324,7 +333,8 @@ public class BundleBacktestHealthCheckWorker : BackgroundService // Get the first account for the user using var scope = _scopeFactory.CreateScope(); var accountService = scope.ServiceProvider.GetRequiredService(); - var accounts = await accountService.GetAccountsByUserAsync(bundleRequest.User, hideSecrets: true, getBalance: false); + var accounts = + await accountService.GetAccountsByUserAsync(bundleRequest.User, hideSecrets: true, getBalance: false); var firstAccount = accounts.FirstOrDefault(); if (firstAccount == null) @@ -362,7 +372,8 @@ public class BundleBacktestHealthCheckWorker : BackgroundService UseSynthApi = universalConfig.UseSynthApi, UseForPositionSizing = universalConfig.UseForPositionSizing, UseForSignalFiltering = universalConfig.UseForSignalFiltering, - UseForDynamicStopLoss = universalConfig.UseForDynamicStopLoss + UseForDynamicStopLoss = universalConfig.UseForDynamicStopLoss, + TradingType = universalConfig.TradingType }; var backtestRequest = new RunBacktestRequest @@ -435,12 +446,14 @@ public class BundleBacktestHealthCheckWorker : BackgroundService "Bundle {BundleRequestId} has all jobs completed ({Completed} completed, {Failed} failed). Updating bundle status.", bundle.RequestId, completedJobs, failedJobs); - bundle.Status = failedJobs == 0 ? BundleBacktestRequestStatus.Completed : BundleBacktestRequestStatus.Completed; + bundle.Status = failedJobs == 0 + ? BundleBacktestRequestStatus.Completed + : BundleBacktestRequestStatus.Completed; bundle.CompletedBacktests = completedJobs; bundle.FailedBacktests = failedJobs; bundle.CompletedAt = DateTime.UtcNow; bundle.UpdatedAt = DateTime.UtcNow; - + if (failedJobs > 0) { bundle.ErrorMessage = $"{failedJobs} backtests failed"; @@ -451,11 +464,11 @@ public class BundleBacktestHealthCheckWorker : BackgroundService // Some jobs are still pending or running - bundle is genuinely stuck // Reset any stale running jobs back to pending var runningJobs = jobs.Where(j => j.Status == JobStatus.Running).ToList(); - + foreach (var job in runningJobs) { - var timeSinceJobHeartbeat = job.LastHeartbeat.HasValue - ? DateTime.UtcNow - job.LastHeartbeat.Value + var timeSinceJobHeartbeat = job.LastHeartbeat.HasValue + ? DateTime.UtcNow - job.LastHeartbeat.Value : DateTime.UtcNow - job.CreatedAt; if (timeSinceJobHeartbeat > TimeSpan.FromMinutes(30)) @@ -473,7 +486,8 @@ public class BundleBacktestHealthCheckWorker : BackgroundService // Update bundle timestamp to give it another chance bundle.UpdatedAt = DateTime.UtcNow; - bundle.ErrorMessage = $"Bundle was stuck. Reset {runningJobs.Count(j => j.Status == JobStatus.Pending)} stale jobs to pending."; + bundle.ErrorMessage = + $"Bundle was stuck. Reset {runningJobs.Count(j => j.Status == JobStatus.Pending)} stale jobs to pending."; } await backtestRepository.UpdateBundleBacktestRequestAsync(bundle); @@ -571,7 +585,7 @@ public class BundleBacktestHealthCheckWorker : BackgroundService { var message = $"❌ Bundle backtest '{bundle.Name}' (ID: {bundle.RequestId}) failed: {reason}"; await _messengerService.SendMessage(message, bundle.User.TelegramChannel); - + _logger.LogInformation( "Sent failure notification to user {UserId} for bundle {BundleRequestId}", bundle.User.Id, bundle.RequestId); @@ -579,10 +593,9 @@ public class BundleBacktestHealthCheckWorker : BackgroundService } catch (Exception ex) { - _logger.LogError(ex, - "Failed to send notification to user for bundle {BundleRequestId}", + _logger.LogError(ex, + "Failed to send notification to user for bundle {BundleRequestId}", bundle.RequestId); } } -} - +} \ No newline at end of file diff --git a/src/Managing.Application/Workers/BundleBacktestWorker.cs b/src/Managing.Application/Workers/BundleBacktestWorker.cs index bb651aa2..d469afbc 100644 --- a/src/Managing.Application/Workers/BundleBacktestWorker.cs +++ b/src/Managing.Application/Workers/BundleBacktestWorker.cs @@ -403,7 +403,8 @@ public class BundleBacktestWorker : BaseWorker UseSynthApi = universalConfig.UseSynthApi, UseForPositionSizing = universalConfig.UseForPositionSizing, UseForSignalFiltering = universalConfig.UseForSignalFiltering, - UseForDynamicStopLoss = universalConfig.UseForDynamicStopLoss + UseForDynamicStopLoss = universalConfig.UseForDynamicStopLoss, + TradingType = universalConfig.TradingType }; var backtestRequest = new RunBacktestRequest diff --git a/src/Managing.Domain/Bots/TradingBotConfigRequest.cs b/src/Managing.Domain/Bots/TradingBotConfigRequest.cs index 6ddd530c..2136bf86 100644 --- a/src/Managing.Domain/Bots/TradingBotConfigRequest.cs +++ b/src/Managing.Domain/Bots/TradingBotConfigRequest.cs @@ -110,4 +110,6 @@ public class TradingBotConfigRequest /// Whether to use Synth predictions for dynamic stop-loss/take-profit adjustments /// public bool UseForDynamicStopLoss { get; set; } = true; + + public TradingType TradingType { get; set; } } \ No newline at end of file diff --git a/src/Managing.WebApp/src/components/organism/UnifiedTradingModal/UnifiedTradingModal.tsx b/src/Managing.WebApp/src/components/organism/UnifiedTradingModal/UnifiedTradingModal.tsx index ea1957bb..3a60ac11 100644 --- a/src/Managing.WebApp/src/components/organism/UnifiedTradingModal/UnifiedTradingModal.tsx +++ b/src/Managing.WebApp/src/components/organism/UnifiedTradingModal/UnifiedTradingModal.tsx @@ -25,6 +25,7 @@ import { Ticker, Timeframe, TradingBotConfigRequest, + TradingType, UpdateBotConfigRequest, } from '../../../generated/ManagingApi' import type {IUnifiedTradingConfigInput, UnifiedTradingModalProps} from '../../../global/type' @@ -83,6 +84,7 @@ const UnifiedTradingModal: React.FC = ({ flipOnlyWhenInProfit: true, balance: 10000, closeEarlyWhenProfitable: false, + tradingType: TradingType.BacktestSpot, useSynthApi: false, useForPositionSizing: true, useForSignalFiltering: true, @@ -217,7 +219,8 @@ const UnifiedTradingModal: React.FC = ({ setValue('useForPositionSizing', backtest.config.useForPositionSizing ?? true); setValue('useForSignalFiltering', backtest.config.useForSignalFiltering ?? true); setValue('useForDynamicStopLoss', backtest.config.useForDynamicStopLoss ?? true); - + setValue('tradingType', (backtest.config as any).tradingType || TradingType.BacktestSpot); + // Use backtest's money management as custom if (backtest.config.moneyManagement) { setShowCustomMoneyManagement(true); @@ -259,7 +262,8 @@ const UnifiedTradingModal: React.FC = ({ setValue('useForPositionSizing', backtest.config.useForPositionSizing ?? true); setValue('useForSignalFiltering', backtest.config.useForSignalFiltering ?? true); setValue('useForDynamicStopLoss', backtest.config.useForDynamicStopLoss ?? true); - + setValue('tradingType', (backtest.config as any).tradingType || TradingType.BacktestSpot); + // Set tickers for backtest (array) if (backtest.config.ticker) { setValue('tickers', [backtest.config.ticker]); @@ -318,7 +322,8 @@ const UnifiedTradingModal: React.FC = ({ setValue('useForPositionSizing', config.useForPositionSizing ?? true); setValue('useForSignalFiltering', config.useForSignalFiltering ?? true); setValue('useForDynamicStopLoss', config.useForDynamicStopLoss ?? true); - + setValue('tradingType', (config as any).tradingType || TradingType.BacktestSpot); + // Handle money management - if it exists, treat as custom for update mode if (config.moneyManagement) { setShowCustomMoneyManagement(true); @@ -664,6 +669,7 @@ const UnifiedTradingModal: React.FC = ({ useForPositionSizing: form.useForPositionSizing ?? true, useForSignalFiltering: form.useForSignalFiltering ?? true, useForDynamicStopLoss: form.useForDynamicStopLoss ?? true, + tradingType: form.tradingType || TradingType.BacktestSpot, moneyManagementName: showCustomMoneyManagement ? undefined : selectedMoneyManagement, moneyManagement: moneyManagement, }; @@ -889,6 +895,18 @@ const UnifiedTradingModal: React.FC = ({ ))} + + {(mode === 'createBot' || mode === 'updateBot') && ( + + + + )} {/* Money Management */} diff --git a/src/Managing.WebApp/src/generated/ManagingApi.ts b/src/Managing.WebApp/src/generated/ManagingApi.ts index ebedf224..4094764f 100644 --- a/src/Managing.WebApp/src/generated/ManagingApi.ts +++ b/src/Managing.WebApp/src/generated/ManagingApi.ts @@ -5298,10 +5298,10 @@ export interface BundleBacktestUniversalConfig { isForWatchingOnly: boolean; botTradingBalance: number; botName: string; + tradingType: TradingType; flipPosition: boolean; cooldownPeriod?: number | null; maxLossStreak?: number; - tradingType?: TradingType; scenario?: ScenarioRequest | null; scenarioName?: string | null; maxPositionTimeHours?: number | null; diff --git a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts index b72d0461..6771774c 100644 --- a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts +++ b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts @@ -803,6 +803,7 @@ export interface BundleBacktestUniversalConfig { isForWatchingOnly: boolean; botTradingBalance: number; botName: string; + tradingType: TradingType; flipPosition: boolean; cooldownPeriod?: number | null; maxLossStreak?: number; diff --git a/src/Managing.WebApp/src/global/type.tsx b/src/Managing.WebApp/src/global/type.tsx index 7f6b3801..78960bce 100644 --- a/src/Managing.WebApp/src/global/type.tsx +++ b/src/Managing.WebApp/src/global/type.tsx @@ -15,7 +15,8 @@ import type { Timeframe, TradeDirection, TradingBotResponse, - TradingExchanges + TradingExchanges, + TradingType } from '../generated/ManagingApi' import {FC, ReactNode} from 'react' @@ -71,6 +72,7 @@ export type IBacktestsFormInput = { maxPositionTimeHours?: number | null flipOnlyWhenInProfit?: boolean closeEarlyWhenProfitable?: boolean + tradingType?: TradingType // Synth API fields useSynthApi?: boolean useForPositionSizing?: boolean