Fix spot bot
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<IGrainFactory>(_scopeFactory, async grainFactory =>
|
||||
{
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,7 +22,10 @@ public class BundleBacktestHealthCheckWorker : BackgroundService
|
||||
private readonly ILogger<BundleBacktestHealthCheckWorker> _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<TradingBotConfig>(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<IAccountService>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -403,7 +403,8 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
|
||||
UseSynthApi = universalConfig.UseSynthApi,
|
||||
UseForPositionSizing = universalConfig.UseForPositionSizing,
|
||||
UseForSignalFiltering = universalConfig.UseForSignalFiltering,
|
||||
UseForDynamicStopLoss = universalConfig.UseForDynamicStopLoss
|
||||
UseForDynamicStopLoss = universalConfig.UseForDynamicStopLoss,
|
||||
TradingType = universalConfig.TradingType
|
||||
};
|
||||
|
||||
var backtestRequest = new RunBacktestRequest
|
||||
|
||||
@@ -110,4 +110,6 @@ public class TradingBotConfigRequest
|
||||
/// Whether to use Synth predictions for dynamic stop-loss/take-profit adjustments
|
||||
/// </summary>
|
||||
public bool UseForDynamicStopLoss { get; set; } = true;
|
||||
|
||||
public TradingType TradingType { get; set; }
|
||||
}
|
||||
@@ -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<UnifiedTradingModalProps> = ({
|
||||
flipOnlyWhenInProfit: true,
|
||||
balance: 10000,
|
||||
closeEarlyWhenProfitable: false,
|
||||
tradingType: TradingType.BacktestSpot,
|
||||
useSynthApi: false,
|
||||
useForPositionSizing: true,
|
||||
useForSignalFiltering: true,
|
||||
@@ -217,7 +219,8 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
||||
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<UnifiedTradingModalProps> = ({
|
||||
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<UnifiedTradingModalProps> = ({
|
||||
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<UnifiedTradingModalProps> = ({
|
||||
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<UnifiedTradingModalProps> = ({
|
||||
))}
|
||||
</select>
|
||||
</FormInput>
|
||||
|
||||
{(mode === 'createBot' || mode === 'updateBot') && (
|
||||
<FormInput label="Trading Type" htmlFor="tradingType">
|
||||
<select
|
||||
className="select select-bordered w-full"
|
||||
{...register('tradingType', { required: true })}
|
||||
>
|
||||
<option value={TradingType.BacktestSpot}>Spot</option>
|
||||
<option value={TradingType.BacktestFutures}>Futures</option>
|
||||
</select>
|
||||
</FormInput>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Money Management */}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -803,6 +803,7 @@ export interface BundleBacktestUniversalConfig {
|
||||
isForWatchingOnly: boolean;
|
||||
botTradingBalance: number;
|
||||
botName: string;
|
||||
tradingType: TradingType;
|
||||
flipPosition: boolean;
|
||||
cooldownPeriod?: number | null;
|
||||
maxLossStreak?: number;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user