Fix front and backtest
This commit is contained in:
@@ -30,7 +30,6 @@ namespace Managing.Application.Abstractions
|
|||||||
int GetWinRate();
|
int GetWinRate();
|
||||||
decimal GetProfitAndLoss();
|
decimal GetProfitAndLoss();
|
||||||
decimal GetTotalFees();
|
decimal GetTotalFees();
|
||||||
void LoadScenario(string scenarioName);
|
|
||||||
void LoadScenario(Scenario scenario);
|
void LoadScenario(Scenario scenario);
|
||||||
void UpdateIndicatorsValues();
|
void UpdateIndicatorsValues();
|
||||||
Task LoadAccount();
|
Task LoadAccount();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Managing.Application.Abstractions;
|
using Managing.Application.Abstractions;
|
||||||
using Managing.Application.Abstractions.Repositories;
|
using Managing.Application.Abstractions.Repositories;
|
||||||
using Managing.Application.Abstractions.Services;
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Application.Bots;
|
||||||
using Managing.Core.FixedSizedQueue;
|
using Managing.Core.FixedSizedQueue;
|
||||||
using Managing.Domain.Accounts;
|
using Managing.Domain.Accounts;
|
||||||
using Managing.Domain.Backtests;
|
using Managing.Domain.Backtests;
|
||||||
@@ -113,24 +114,15 @@ namespace Managing.Application.Backtesting
|
|||||||
List<Candle> candles,
|
List<Candle> candles,
|
||||||
User user = null)
|
User user = null)
|
||||||
{
|
{
|
||||||
// Set FlipPosition based on BotType
|
|
||||||
config.FlipPosition = config.FlipPosition;
|
|
||||||
|
|
||||||
var tradingBot = _botFactory.CreateBacktestTradingBot(config);
|
var tradingBot = _botFactory.CreateBacktestTradingBot(config);
|
||||||
|
|
||||||
// Load scenario - prefer Scenario object over ScenarioName
|
// Scenario and indicators should already be loaded in constructor by BotService
|
||||||
if (config.Scenario != null)
|
// This is just a validation check to ensure everything loaded properly
|
||||||
|
if (tradingBot is TradingBot bot && !bot.Indicators.Any())
|
||||||
{
|
{
|
||||||
tradingBot.LoadScenario(config.Scenario);
|
throw new InvalidOperationException(
|
||||||
}
|
$"No indicators were loaded for scenario '{config.ScenarioName ?? config.Scenario?.Name}'. " +
|
||||||
else if (!string.IsNullOrEmpty(config.ScenarioName))
|
"This indicates a problem with scenario loading.");
|
||||||
{
|
|
||||||
tradingBot.LoadScenario(config.ScenarioName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException(
|
|
||||||
"Either Scenario object or ScenarioName must be provided in TradingBotConfig");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tradingBot.User = user;
|
tradingBot.User = user;
|
||||||
@@ -218,7 +210,6 @@ namespace Managing.Application.Backtesting
|
|||||||
_logger.LogInformation("Backtest processing completed. Calculating final results...");
|
_logger.LogInformation("Backtest processing completed. Calculating final results...");
|
||||||
|
|
||||||
bot.Candles = new HashSet<Candle>(candles);
|
bot.Candles = new HashSet<Candle>(candles);
|
||||||
// bot.UpdateIndicatorsValues();
|
|
||||||
|
|
||||||
var indicatorsValues = GetIndicatorsValues(bot.Config.Scenario.Indicators, candles);
|
var indicatorsValues = GetIndicatorsValues(bot.Config.Scenario.Indicators, candles);
|
||||||
|
|
||||||
@@ -256,7 +247,8 @@ namespace Managing.Application.Backtesting
|
|||||||
Statistics = stats,
|
Statistics = stats,
|
||||||
OptimizedMoneyManagement = optimizedMoneyManagement,
|
OptimizedMoneyManagement = optimizedMoneyManagement,
|
||||||
IndicatorsValues = AggregateValues(indicatorsValues, bot.IndicatorsValues),
|
IndicatorsValues = AggregateValues(indicatorsValues, bot.IndicatorsValues),
|
||||||
Score = score
|
Score = score,
|
||||||
|
Id = Guid.NewGuid().ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -38,27 +38,14 @@ namespace Managing.Application.Bots.Base
|
|||||||
|
|
||||||
ITradingBot IBotFactory.CreateTradingBot(TradingBotConfig config)
|
ITradingBot IBotFactory.CreateTradingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
return new TradingBot(
|
// Delegate to BotService which handles scenario loading properly
|
||||||
_exchangeService,
|
return _botService.CreateTradingBot(config);
|
||||||
_tradingBotLogger,
|
|
||||||
_tradingService,
|
|
||||||
_accountService,
|
|
||||||
_messengerService,
|
|
||||||
_botService,
|
|
||||||
config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ITradingBot IBotFactory.CreateBacktestTradingBot(TradingBotConfig config)
|
ITradingBot IBotFactory.CreateBacktestTradingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
config.IsForBacktest = true;
|
// Delegate to BotService which handles scenario loading properly
|
||||||
return new TradingBot(
|
return _botService.CreateBacktestTradingBot(config);
|
||||||
_exchangeService,
|
|
||||||
_tradingBotLogger,
|
|
||||||
_tradingService,
|
|
||||||
_accountService,
|
|
||||||
_messengerService,
|
|
||||||
_botService,
|
|
||||||
config);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,6 +76,16 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
WalletBalances = new Dictionary<DateTime, decimal>();
|
WalletBalances = new Dictionary<DateTime, decimal>();
|
||||||
IndicatorsValues = new Dictionary<IndicatorType, IndicatorsResultBase>();
|
IndicatorsValues = new Dictionary<IndicatorType, IndicatorsResultBase>();
|
||||||
|
|
||||||
|
// Load indicators if scenario is provided in config
|
||||||
|
if (Config.Scenario != null)
|
||||||
|
{
|
||||||
|
LoadIndicators(Config.Scenario);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Scenario object must be provided in TradingBotConfig. ScenarioName alone is not sufficient.");
|
||||||
|
}
|
||||||
|
|
||||||
if (!Config.IsForBacktest)
|
if (!Config.IsForBacktest)
|
||||||
{
|
{
|
||||||
Interval = CandleExtensions.GetIntervalFromTimeframe(Config.Timeframe);
|
Interval = CandleExtensions.GetIntervalFromTimeframe(Config.Timeframe);
|
||||||
@@ -91,7 +101,13 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
|
|
||||||
if (!Config.IsForBacktest)
|
if (!Config.IsForBacktest)
|
||||||
{
|
{
|
||||||
LoadScenario(Config.ScenarioName);
|
// Scenario and indicators should already be loaded in constructor
|
||||||
|
// This is just a safety check
|
||||||
|
if (Config.Scenario == null || !Indicators.Any())
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Scenario or indicators not loaded properly in constructor. This indicates a configuration error.");
|
||||||
|
}
|
||||||
|
|
||||||
PreloadCandles().GetAwaiter().GetResult();
|
PreloadCandles().GetAwaiter().GetResult();
|
||||||
CancelAllOrders().GetAwaiter().GetResult();
|
CancelAllOrders().GetAwaiter().GetResult();
|
||||||
|
|
||||||
@@ -127,33 +143,32 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadScenario(string scenarioName)
|
|
||||||
{
|
|
||||||
if (Config.Scenario != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var scenario = TradingService.GetScenarioByName(scenarioName);
|
|
||||||
if (scenario == null)
|
|
||||||
{
|
|
||||||
Logger.LogWarning("No scenario found for this scenario name");
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LoadIndicators(ScenarioHelpers.GetIndicatorsFromScenario(scenario));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadScenario(Scenario scenario)
|
public void LoadScenario(Scenario scenario)
|
||||||
{
|
{
|
||||||
if (scenario == null)
|
if (scenario == null)
|
||||||
{
|
{
|
||||||
Logger.LogWarning("Null scenario provided");
|
var errorMessage = "Null scenario provided";
|
||||||
Stop();
|
Logger.LogWarning(errorMessage);
|
||||||
|
|
||||||
|
// If called during construction, throw exception instead of Stop()
|
||||||
|
if (Status == BotStatus.Down)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(errorMessage);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Store the scenario in config and load indicators
|
||||||
|
Config.Scenario = scenario;
|
||||||
LoadIndicators(ScenarioHelpers.GetIndicatorsFromScenario(scenario));
|
LoadIndicators(ScenarioHelpers.GetIndicatorsFromScenario(scenario));
|
||||||
|
|
||||||
|
Logger.LogInformation($"Loaded scenario '{scenario.Name}' with {Indicators.Count} indicators");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,10 +179,15 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
|
|
||||||
public void LoadIndicators(IEnumerable<IIndicator> indicators)
|
public void LoadIndicators(IEnumerable<IIndicator> indicators)
|
||||||
{
|
{
|
||||||
foreach (var strategy in indicators)
|
// Clear existing indicators to prevent duplicates
|
||||||
|
Indicators.Clear();
|
||||||
|
|
||||||
|
foreach (var indicator in indicators)
|
||||||
{
|
{
|
||||||
Indicators.Add(strategy);
|
Indicators.Add(indicator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.LogInformation($"Loaded {Indicators.Count} indicators for bot '{Name}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Run()
|
public async Task Run()
|
||||||
@@ -1444,9 +1464,9 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
throw new ArgumentException("Account name cannot be null or empty");
|
throw new ArgumentException("Account name cannot be null or empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(newConfig.ScenarioName))
|
if (newConfig.Scenario == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Scenario name cannot be null or empty");
|
throw new ArgumentException("Scenario object must be provided in configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protect critical properties that shouldn't change for running bots
|
// Protect critical properties that shouldn't change for running bots
|
||||||
@@ -1487,9 +1507,17 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
|
|
||||||
// If scenario changed, reload it
|
// If scenario changed, reload it
|
||||||
var currentScenario = Config.Scenario?.Name;
|
var currentScenario = Config.Scenario?.Name;
|
||||||
if (Config.ScenarioName != currentScenario)
|
var newScenario = newConfig.Scenario?.Name;
|
||||||
|
if (newScenario != currentScenario)
|
||||||
{
|
{
|
||||||
LoadScenario(Config.ScenarioName);
|
if (newConfig.Scenario != null)
|
||||||
|
{
|
||||||
|
LoadScenario(newConfig.Scenario);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("New scenario object must be provided when updating configuration.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await LogInformation("✅ **Configuration Applied**\n" +
|
await LogInformation("✅ **Configuration Applied**\n" +
|
||||||
|
|||||||
@@ -134,6 +134,25 @@ namespace Managing.Application.ManageBot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure the scenario is properly loaded from database if needed
|
||||||
|
if (scalpingConfig.Scenario == null && !string.IsNullOrEmpty(scalpingConfig.ScenarioName))
|
||||||
|
{
|
||||||
|
var scenario = _tradingService.GetScenarioByName(scalpingConfig.ScenarioName);
|
||||||
|
if (scenario != null)
|
||||||
|
{
|
||||||
|
scalpingConfig.Scenario = scenario;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Scenario '{scalpingConfig.ScenarioName}' not found in database when loading backup");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scalpingConfig.Scenario == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Scenario object must be provided or ScenarioName must be valid when loading backup");
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure critical properties are set correctly for restored bots
|
// Ensure critical properties are set correctly for restored bots
|
||||||
scalpingConfig.IsForBacktest = false;
|
scalpingConfig.IsForBacktest = false;
|
||||||
|
|
||||||
@@ -235,6 +254,25 @@ namespace Managing.Application.ManageBot
|
|||||||
if (_botTasks.TryGetValue(identifier, out var botTaskWrapper) &&
|
if (_botTasks.TryGetValue(identifier, out var botTaskWrapper) &&
|
||||||
botTaskWrapper.BotInstance is TradingBot tradingBot)
|
botTaskWrapper.BotInstance is TradingBot tradingBot)
|
||||||
{
|
{
|
||||||
|
// Ensure the scenario is properly loaded from database if needed
|
||||||
|
if (newConfig.Scenario == null && !string.IsNullOrEmpty(newConfig.ScenarioName))
|
||||||
|
{
|
||||||
|
var scenario = _tradingService.GetScenarioByName(newConfig.ScenarioName);
|
||||||
|
if (scenario != null)
|
||||||
|
{
|
||||||
|
newConfig.Scenario = scenario;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Scenario '{newConfig.ScenarioName}' not found in database when updating configuration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newConfig.Scenario == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Scenario object must be provided or ScenarioName must be valid when updating configuration");
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the bot name is changing
|
// Check if the bot name is changing
|
||||||
if (newConfig.Name != identifier && !string.IsNullOrEmpty(newConfig.Name))
|
if (newConfig.Name != identifier && !string.IsNullOrEmpty(newConfig.Name))
|
||||||
{
|
{
|
||||||
@@ -279,6 +317,25 @@ namespace Managing.Application.ManageBot
|
|||||||
|
|
||||||
public ITradingBot CreateTradingBot(TradingBotConfig config)
|
public ITradingBot CreateTradingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
|
// Ensure the scenario is properly loaded from database if needed
|
||||||
|
if (config.Scenario == null && !string.IsNullOrEmpty(config.ScenarioName))
|
||||||
|
{
|
||||||
|
var scenario = _tradingService.GetScenarioByName(config.ScenarioName);
|
||||||
|
if (scenario != null)
|
||||||
|
{
|
||||||
|
config.Scenario = scenario;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Scenario '{config.ScenarioName}' not found in database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Scenario == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Scenario object must be provided or ScenarioName must be valid");
|
||||||
|
}
|
||||||
|
|
||||||
return new TradingBot(
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
_tradingBotLogger,
|
_tradingBotLogger,
|
||||||
@@ -291,6 +348,25 @@ namespace Managing.Application.ManageBot
|
|||||||
|
|
||||||
public ITradingBot CreateBacktestTradingBot(TradingBotConfig config)
|
public ITradingBot CreateBacktestTradingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
|
// Ensure the scenario is properly loaded from database if needed
|
||||||
|
if (config.Scenario == null && !string.IsNullOrEmpty(config.ScenarioName))
|
||||||
|
{
|
||||||
|
var scenario = _tradingService.GetScenarioByName(config.ScenarioName);
|
||||||
|
if (scenario != null)
|
||||||
|
{
|
||||||
|
config.Scenario = scenario;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Scenario '{config.ScenarioName}' not found in database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Scenario == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Scenario object must be provided or ScenarioName must be valid");
|
||||||
|
}
|
||||||
|
|
||||||
config.IsForBacktest = true;
|
config.IsForBacktest = true;
|
||||||
return new TradingBot(
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
@@ -304,6 +380,25 @@ namespace Managing.Application.ManageBot
|
|||||||
|
|
||||||
public ITradingBot CreateScalpingBot(TradingBotConfig config)
|
public ITradingBot CreateScalpingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
|
// Ensure the scenario is properly loaded from database if needed
|
||||||
|
if (config.Scenario == null && !string.IsNullOrEmpty(config.ScenarioName))
|
||||||
|
{
|
||||||
|
var scenario = _tradingService.GetScenarioByName(config.ScenarioName);
|
||||||
|
if (scenario != null)
|
||||||
|
{
|
||||||
|
config.Scenario = scenario;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Scenario '{config.ScenarioName}' not found in database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Scenario == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Scenario object must be provided or ScenarioName must be valid");
|
||||||
|
}
|
||||||
|
|
||||||
config.FlipPosition = false;
|
config.FlipPosition = false;
|
||||||
return new TradingBot(
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
@@ -317,6 +412,25 @@ namespace Managing.Application.ManageBot
|
|||||||
|
|
||||||
public ITradingBot CreateBacktestScalpingBot(TradingBotConfig config)
|
public ITradingBot CreateBacktestScalpingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
|
// Ensure the scenario is properly loaded from database if needed
|
||||||
|
if (config.Scenario == null && !string.IsNullOrEmpty(config.ScenarioName))
|
||||||
|
{
|
||||||
|
var scenario = _tradingService.GetScenarioByName(config.ScenarioName);
|
||||||
|
if (scenario != null)
|
||||||
|
{
|
||||||
|
config.Scenario = scenario;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Scenario '{config.ScenarioName}' not found in database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Scenario == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Scenario object must be provided or ScenarioName must be valid");
|
||||||
|
}
|
||||||
|
|
||||||
config.IsForBacktest = true;
|
config.IsForBacktest = true;
|
||||||
config.FlipPosition = false;
|
config.FlipPosition = false;
|
||||||
return new TradingBot(
|
return new TradingBot(
|
||||||
@@ -331,6 +445,25 @@ namespace Managing.Application.ManageBot
|
|||||||
|
|
||||||
public ITradingBot CreateFlippingBot(TradingBotConfig config)
|
public ITradingBot CreateFlippingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
|
// Ensure the scenario is properly loaded from database if needed
|
||||||
|
if (config.Scenario == null && !string.IsNullOrEmpty(config.ScenarioName))
|
||||||
|
{
|
||||||
|
var scenario = _tradingService.GetScenarioByName(config.ScenarioName);
|
||||||
|
if (scenario != null)
|
||||||
|
{
|
||||||
|
config.Scenario = scenario;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Scenario '{config.ScenarioName}' not found in database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Scenario == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Scenario object must be provided or ScenarioName must be valid");
|
||||||
|
}
|
||||||
|
|
||||||
config.FlipPosition = true;
|
config.FlipPosition = true;
|
||||||
return new TradingBot(
|
return new TradingBot(
|
||||||
_exchangeService,
|
_exchangeService,
|
||||||
@@ -344,6 +477,25 @@ namespace Managing.Application.ManageBot
|
|||||||
|
|
||||||
public ITradingBot CreateBacktestFlippingBot(TradingBotConfig config)
|
public ITradingBot CreateBacktestFlippingBot(TradingBotConfig config)
|
||||||
{
|
{
|
||||||
|
// Ensure the scenario is properly loaded from database if needed
|
||||||
|
if (config.Scenario == null && !string.IsNullOrEmpty(config.ScenarioName))
|
||||||
|
{
|
||||||
|
var scenario = _tradingService.GetScenarioByName(config.ScenarioName);
|
||||||
|
if (scenario != null)
|
||||||
|
{
|
||||||
|
config.Scenario = scenario;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Scenario '{config.ScenarioName}' not found in database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Scenario == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Scenario object must be provided or ScenarioName must be valid");
|
||||||
|
}
|
||||||
|
|
||||||
config.IsForBacktest = true;
|
config.IsForBacktest = true;
|
||||||
config.FlipPosition = true;
|
config.FlipPosition = true;
|
||||||
return new TradingBot(
|
return new TradingBot(
|
||||||
|
|||||||
40
src/Managing.WebApp/src/app/store/backtestStore.ts
Normal file
40
src/Managing.WebApp/src/app/store/backtestStore.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import {create} from 'zustand'
|
||||||
|
import type {Backtest} from '../../generated/ManagingApi'
|
||||||
|
|
||||||
|
interface BacktestStore {
|
||||||
|
backtests: Backtest[]
|
||||||
|
isLoading: boolean
|
||||||
|
setBacktests: (backtests: Backtest[]) => void
|
||||||
|
addBacktest: (backtest: Backtest) => void
|
||||||
|
removeBacktest: (id: string) => void
|
||||||
|
setLoading: (loading: boolean) => void
|
||||||
|
clearBacktests: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const useBacktestStore = create<BacktestStore>((set, get) => ({
|
||||||
|
backtests: [],
|
||||||
|
isLoading: false,
|
||||||
|
|
||||||
|
setBacktests: (backtests: Backtest[]) =>
|
||||||
|
set({ backtests }),
|
||||||
|
|
||||||
|
addBacktest: (backtest: Backtest) =>
|
||||||
|
set((state) => ({
|
||||||
|
backtests: [...state.backtests, backtest]
|
||||||
|
})),
|
||||||
|
|
||||||
|
removeBacktest: (id: string) =>
|
||||||
|
set((state) => ({
|
||||||
|
backtests: state.backtests.filter(backtest =>
|
||||||
|
String(backtest.id) !== String(id)
|
||||||
|
)
|
||||||
|
})),
|
||||||
|
|
||||||
|
setLoading: (loading: boolean) =>
|
||||||
|
set({ isLoading: loading }),
|
||||||
|
|
||||||
|
clearBacktests: () =>
|
||||||
|
set({ backtests: [] })
|
||||||
|
}))
|
||||||
|
|
||||||
|
export default useBacktestStore
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import React, {useState} from 'react'
|
||||||
|
import {Modal} from '../index'
|
||||||
|
import type {Backtest} from '../../../generated/ManagingApi'
|
||||||
|
|
||||||
|
interface ConfigDisplayModalProps {
|
||||||
|
showModal: boolean
|
||||||
|
onClose: () => void
|
||||||
|
backtest: Backtest | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConfigDisplayModal: React.FC<ConfigDisplayModalProps> = ({
|
||||||
|
showModal,
|
||||||
|
onClose,
|
||||||
|
backtest,
|
||||||
|
}) => {
|
||||||
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
|
const formatConfigForDisplay = (config: any) => {
|
||||||
|
// Create a clean config object without undefined values
|
||||||
|
const cleanConfig: Record<string, any> = {
|
||||||
|
accountName: config.accountName,
|
||||||
|
moneyManagement: config.moneyManagement,
|
||||||
|
ticker: config.ticker,
|
||||||
|
timeframe: config.timeframe,
|
||||||
|
isForWatchingOnly: config.isForWatchingOnly,
|
||||||
|
botTradingBalance: config.botTradingBalance,
|
||||||
|
isForBacktest: config.isForBacktest,
|
||||||
|
cooldownPeriod: config.cooldownPeriod,
|
||||||
|
maxLossStreak: config.maxLossStreak,
|
||||||
|
flipPosition: config.flipPosition,
|
||||||
|
name: config.name,
|
||||||
|
riskManagement: config.riskManagement,
|
||||||
|
scenario: config.scenario,
|
||||||
|
scenarioName: config.scenarioName,
|
||||||
|
maxPositionTimeHours: config.maxPositionTimeHours,
|
||||||
|
closeEarlyWhenProfitable: config.closeEarlyWhenProfitable,
|
||||||
|
flipOnlyWhenInProfit: config.flipOnlyWhenInProfit,
|
||||||
|
useSynthApi: config.useSynthApi,
|
||||||
|
useForPositionSizing: config.useForPositionSizing,
|
||||||
|
useForSignalFiltering: config.useForSignalFiltering,
|
||||||
|
useForDynamicStopLoss: config.useForDynamicStopLoss,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove undefined values
|
||||||
|
Object.keys(cleanConfig).forEach(key => {
|
||||||
|
if (cleanConfig[key] === undefined || cleanConfig[key] === null) {
|
||||||
|
delete cleanConfig[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return JSON.stringify(cleanConfig, null, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCopyConfig = async () => {
|
||||||
|
if (!backtest?.config) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const configJson = formatConfigForDisplay(backtest.config)
|
||||||
|
await navigator.clipboard.writeText(configJson)
|
||||||
|
setCopied(true)
|
||||||
|
setTimeout(() => setCopied(false), 2000)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to copy config:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backtest) return null
|
||||||
|
|
||||||
|
const configJson = formatConfigForDisplay(backtest.config)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
showModal={showModal}
|
||||||
|
onClose={onClose}
|
||||||
|
titleHeader="Backtest Configuration"
|
||||||
|
>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<h3 className="text-lg font-semibold">Configuration JSON</h3>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`btn btn-sm ${copied ? 'btn-success' : 'btn-primary'}`}
|
||||||
|
onClick={handleCopyConfig}
|
||||||
|
>
|
||||||
|
{copied ? (
|
||||||
|
<>
|
||||||
|
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
Copied!
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
|
||||||
|
</svg>
|
||||||
|
Copy Config
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-base-200 rounded-lg p-4 max-h-96 overflow-auto">
|
||||||
|
<pre className="text-sm text-base-content whitespace-pre-wrap break-words">
|
||||||
|
{configJson}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-sm text-base-content/70">
|
||||||
|
<p>This configuration can be copied and imported into the backtest modal to replicate this exact setup.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConfigDisplayModal
|
||||||
@@ -11,8 +11,8 @@ import {Loader} from '../../atoms'
|
|||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{ href: '/bots', name: 'Bots' },
|
{ href: '/bots', name: 'Bots' },
|
||||||
{ href: '/scenarios', name: 'Scenarios' },
|
|
||||||
{ href: '/backtest', name: 'Backtest' },
|
{ href: '/backtest', name: 'Backtest' },
|
||||||
|
{ href: '/scenarios', name: 'Scenarios' },
|
||||||
{ href: '/tools', name: 'Tools' },
|
{ href: '/tools', name: 'Tools' },
|
||||||
{ href: '/settings', name: 'Settings' },
|
{ href: '/settings', name: 'Settings' },
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ export { default as LogIn } from './LogIn/LogIn'
|
|||||||
export { default as GridTile } from './GridTile/GridTile'
|
export { default as GridTile } from './GridTile/GridTile'
|
||||||
export { default as SelectColumnFilter } from './Table/SelectColumnFilter'
|
export { default as SelectColumnFilter } from './Table/SelectColumnFilter'
|
||||||
export { default as Card } from './Card/Card'
|
export { default as Card } from './Card/Card'
|
||||||
|
export { default as ConfigDisplayModal } from './ConfigDisplayModal/ConfigDisplayModal'
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import React, {useEffect, useState} from 'react'
|
|||||||
import {useQuery} from '@tanstack/react-query'
|
import {useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
import useApiUrlStore from '../../../app/store/apiStore'
|
import useApiUrlStore from '../../../app/store/apiStore'
|
||||||
|
import useBacktestStore from '../../../app/store/backtestStore'
|
||||||
import type {
|
import type {
|
||||||
Backtest,
|
Backtest,
|
||||||
MoneyManagement,
|
MoneyManagement,
|
||||||
@@ -13,7 +14,6 @@ import type {
|
|||||||
TradingBotConfigRequest
|
TradingBotConfigRequest
|
||||||
} from '../../../generated/ManagingApi'
|
} from '../../../generated/ManagingApi'
|
||||||
import {BacktestClient, BotClient, MoneyManagementClient} from '../../../generated/ManagingApi'
|
import {BacktestClient, BotClient, MoneyManagementClient} from '../../../generated/ManagingApi'
|
||||||
import type {IBacktestCards} from '../../../global/type.tsx'
|
|
||||||
import MoneyManagementModal from '../../../pages/settingsPage/moneymanagement/moneyManagementModal'
|
import MoneyManagementModal from '../../../pages/settingsPage/moneymanagement/moneyManagementModal'
|
||||||
import {CardPosition, CardText, Toast} from '../../mollecules'
|
import {CardPosition, CardText, Toast} from '../../mollecules'
|
||||||
import CardPositionItem from '../Trading/CardPositionItem'
|
import CardPositionItem from '../Trading/CardPositionItem'
|
||||||
@@ -52,9 +52,14 @@ function daysBetween(date: Date) {
|
|||||||
return diffDays
|
return diffDays
|
||||||
}
|
}
|
||||||
|
|
||||||
const BacktestCards: React.FC<IBacktestCards> = ({list, setBacktests}) => {
|
interface BacktestCardsProps {
|
||||||
|
list: Backtest[] | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const BacktestCards: React.FC<BacktestCardsProps> = ({list}) => {
|
||||||
console.log(list)
|
console.log(list)
|
||||||
const {apiUrl} = useApiUrlStore()
|
const {apiUrl} = useApiUrlStore()
|
||||||
|
const {addBacktest, removeBacktest} = useBacktestStore()
|
||||||
const [showMoneyManagementModal, setShowMoneyManagementModal] =
|
const [showMoneyManagementModal, setShowMoneyManagementModal] =
|
||||||
React.useState(false)
|
React.useState(false)
|
||||||
const [selectedMoneyManagement, setSelectedMoneyManagement] =
|
const [selectedMoneyManagement, setSelectedMoneyManagement] =
|
||||||
@@ -173,13 +178,29 @@ const BacktestCards: React.FC<IBacktestCards> = ({list, setBacktests}) => {
|
|||||||
.backtest_Run(request)
|
.backtest_Run(request)
|
||||||
.then((backtest: Backtest) => {
|
.then((backtest: Backtest) => {
|
||||||
t.update('success', `${backtest.config.ticker} Backtest Succeeded`)
|
t.update('success', `${backtest.config.ticker} Backtest Succeeded`)
|
||||||
setBacktests((arr: Backtest[]) => [...arr, backtest])
|
addBacktest(backtest)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
t.update('error', 'Error :' + err)
|
t.update('error', 'Error :' + err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteBacktest(id: string) {
|
||||||
|
const t = new Toast('Deleting backtest')
|
||||||
|
const client = new BacktestClient({}, apiUrl)
|
||||||
|
|
||||||
|
await client
|
||||||
|
.backtest_DeleteBacktest(id)
|
||||||
|
.then(() => {
|
||||||
|
t.update('success', 'Backtest deleted')
|
||||||
|
// Remove the deleted backtest from the store
|
||||||
|
removeBacktest(id)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
t.update('error', err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function saveMoneyManagement(moneyManagement: MoneyManagement) {
|
function saveMoneyManagement(moneyManagement: MoneyManagement) {
|
||||||
setSelectedMoneyManagement(moneyManagement)
|
setSelectedMoneyManagement(moneyManagement)
|
||||||
setShowMoneyManagementModal(true)
|
setShowMoneyManagementModal(true)
|
||||||
@@ -194,10 +215,15 @@ const BacktestCards: React.FC<IBacktestCards> = ({list, setBacktests}) => {
|
|||||||
>
|
>
|
||||||
<div className="indicator">
|
<div className="indicator">
|
||||||
<div className="indicator-item indicator-top">
|
<div className="indicator-item indicator-top">
|
||||||
<button className="btn btn-primary h-5 min-h-0 px-2 mr-5 rounded-full">
|
<div className="tooltip" data-tip="Delete backtest">
|
||||||
|
<button
|
||||||
|
className="btn btn-primary h-5 min-h-0 px-2 mr-5 rounded-full"
|
||||||
|
onClick={() => deleteBacktest(backtest.id)}
|
||||||
|
>
|
||||||
<TrashIcon width={15}></TrashIcon>
|
<TrashIcon width={15}></TrashIcon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="card bg-base-300 shadow-xl">
|
<div className="card bg-base-300 shadow-xl">
|
||||||
<figure className="z-0">
|
<figure className="z-0">
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
|
|||||||
showModal,
|
showModal,
|
||||||
closeModal,
|
closeModal,
|
||||||
setBacktests,
|
setBacktests,
|
||||||
showLoopSlider = false,
|
|
||||||
}) => {
|
}) => {
|
||||||
// Get date 15 days ago for start date
|
// Get date 15 days ago for start date
|
||||||
const defaultStartDate = new Date();
|
const defaultStartDate = new Date();
|
||||||
@@ -77,9 +76,7 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
|
|||||||
|
|
||||||
const [selectedAccount, setSelectedAccount] = useState<string>('')
|
const [selectedAccount, setSelectedAccount] = useState<string>('')
|
||||||
const [selectedTimeframe, setSelectedTimeframe] = useState<Timeframe>(Timeframe.OneHour)
|
const [selectedTimeframe, setSelectedTimeframe] = useState<Timeframe>(Timeframe.OneHour)
|
||||||
const [selectedLoopQuantity, setLoopQuantity] = React.useState<number>(
|
|
||||||
showLoopSlider ? 3 : 1
|
|
||||||
)
|
|
||||||
const [balance, setBalance] = useState<number>(10000)
|
const [balance, setBalance] = useState<number>(10000)
|
||||||
|
|
||||||
const [customMoneyManagement, setCustomMoneyManagement] =
|
const [customMoneyManagement, setCustomMoneyManagement] =
|
||||||
@@ -204,17 +201,7 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
|
|||||||
t.update('success', `${backtest.config.ticker} Backtest Succeeded`)
|
t.update('success', `${backtest.config.ticker} Backtest Succeeded`)
|
||||||
setBacktests((arr) => [...arr, backtest])
|
setBacktests((arr) => [...arr, backtest])
|
||||||
|
|
||||||
if (showLoopSlider && selectedLoopQuantity > loopCount) {
|
|
||||||
const nextCount = loopCount + 1
|
|
||||||
const mm: MoneyManagement = {
|
|
||||||
leverage: backtest.optimizedMoneyManagement.leverage,
|
|
||||||
name: backtest.optimizedMoneyManagement.name + nextCount,
|
|
||||||
stopLoss: backtest.optimizedMoneyManagement.stopLoss,
|
|
||||||
takeProfit: backtest.optimizedMoneyManagement.takeProfit,
|
|
||||||
timeframe: backtest.optimizedMoneyManagement.timeframe,
|
|
||||||
}
|
|
||||||
await runBacktest(form, ticker, scenarioName, mm, nextCount)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
t.update('error', 'Error: ' + err)
|
t.update('error', 'Error: ' + err)
|
||||||
throw err; // Re-throw the error to be caught by the caller
|
throw err; // Re-throw the error to be caught by the caller
|
||||||
|
|||||||
@@ -2,18 +2,24 @@ import {ChevronDownIcon, ChevronRightIcon, CogIcon, PlayIcon, TrashIcon} from '@
|
|||||||
import React, {useEffect, useState} from 'react'
|
import React, {useEffect, useState} from 'react'
|
||||||
|
|
||||||
import useApiUrlStore from '../../../app/store/apiStore'
|
import useApiUrlStore from '../../../app/store/apiStore'
|
||||||
|
import useBacktestStore from '../../../app/store/backtestStore'
|
||||||
import type {Backtest} from '../../../generated/ManagingApi'
|
import type {Backtest} from '../../../generated/ManagingApi'
|
||||||
import {BacktestClient} from '../../../generated/ManagingApi'
|
import {BacktestClient} from '../../../generated/ManagingApi'
|
||||||
import type {IBacktestCards} from '../../../global/type.tsx'
|
import {CardText, ConfigDisplayModal, SelectColumnFilter, Table} from '../../mollecules'
|
||||||
import {CardText, SelectColumnFilter, Table} from '../../mollecules'
|
|
||||||
import {UnifiedTradingModal} from '../index'
|
import {UnifiedTradingModal} from '../index'
|
||||||
import Toast from '../../mollecules/Toast/Toast'
|
import Toast from '../../mollecules/Toast/Toast'
|
||||||
|
|
||||||
import BacktestRowDetails from './backtestRowDetails'
|
import BacktestRowDetails from './backtestRowDetails'
|
||||||
|
|
||||||
const BacktestTable: React.FC<IBacktestCards> = ({list, isFetching, setBacktests}) => {
|
interface BacktestTableProps {
|
||||||
|
list: Backtest[] | undefined
|
||||||
|
isFetching?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const BacktestTable: React.FC<BacktestTableProps> = ({list, isFetching}) => {
|
||||||
const [rows, setRows] = useState<Backtest[]>([])
|
const [rows, setRows] = useState<Backtest[]>([])
|
||||||
const {apiUrl} = useApiUrlStore()
|
const {apiUrl} = useApiUrlStore()
|
||||||
|
const {removeBacktest} = useBacktestStore()
|
||||||
const [optimizedMoneyManagement, setOptimizedMoneyManagement] = useState({
|
const [optimizedMoneyManagement, setOptimizedMoneyManagement] = useState({
|
||||||
stopLoss: 0,
|
stopLoss: 0,
|
||||||
takeProfit: 0,
|
takeProfit: 0,
|
||||||
@@ -36,6 +42,10 @@ const BacktestTable: React.FC<IBacktestCards> = ({list, isFetching, setBacktests
|
|||||||
const [showBacktestConfigModal, setShowBacktestConfigModal] = useState(false)
|
const [showBacktestConfigModal, setShowBacktestConfigModal] = useState(false)
|
||||||
const [selectedBacktestForRerun, setSelectedBacktestForRerun] = useState<Backtest | null>(null)
|
const [selectedBacktestForRerun, setSelectedBacktestForRerun] = useState<Backtest | null>(null)
|
||||||
|
|
||||||
|
// Config display modal state
|
||||||
|
const [showConfigDisplayModal, setShowConfigDisplayModal] = useState(false)
|
||||||
|
const [selectedBacktestForConfigView, setSelectedBacktestForConfigView] = useState<Backtest | null>(null)
|
||||||
|
|
||||||
const handleOpenBotConfigModal = (backtest: Backtest) => {
|
const handleOpenBotConfigModal = (backtest: Backtest) => {
|
||||||
setSelectedBacktest(backtest)
|
setSelectedBacktest(backtest)
|
||||||
setShowBotConfigModal(true)
|
setShowBotConfigModal(true)
|
||||||
@@ -56,6 +66,16 @@ const BacktestTable: React.FC<IBacktestCards> = ({list, isFetching, setBacktests
|
|||||||
setSelectedBacktestForRerun(null)
|
setSelectedBacktestForRerun(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleOpenConfigDisplayModal = (backtest: Backtest) => {
|
||||||
|
setSelectedBacktestForConfigView(backtest)
|
||||||
|
setShowConfigDisplayModal(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseConfigDisplayModal = () => {
|
||||||
|
setShowConfigDisplayModal(false)
|
||||||
|
setSelectedBacktestForConfigView(null)
|
||||||
|
}
|
||||||
|
|
||||||
async function deleteBacktest(id: string) {
|
async function deleteBacktest(id: string) {
|
||||||
const t = new Toast('Deleting backtest')
|
const t = new Toast('Deleting backtest')
|
||||||
const client = new BacktestClient({}, apiUrl)
|
const client = new BacktestClient({}, apiUrl)
|
||||||
@@ -64,11 +84,8 @@ const BacktestTable: React.FC<IBacktestCards> = ({list, isFetching, setBacktests
|
|||||||
.backtest_DeleteBacktest(id)
|
.backtest_DeleteBacktest(id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
t.update('success', 'Backtest deleted')
|
t.update('success', 'Backtest deleted')
|
||||||
// Remove the deleted backtest from the list
|
// Remove the deleted backtest from the store
|
||||||
if (list) {
|
removeBacktest(id)
|
||||||
const updatedList = list.filter(backtest => backtest.id !== id);
|
|
||||||
setBacktests(updatedList);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
t.update('error', err)
|
t.update('error', err)
|
||||||
@@ -218,7 +235,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({list, isFetching, setBacktests
|
|||||||
data-value={cell.row.values.name}
|
data-value={cell.row.values.name}
|
||||||
onClick={() => deleteBacktest(cell.row.values.id)}
|
onClick={() => deleteBacktest(cell.row.values.id)}
|
||||||
>
|
>
|
||||||
<TrashIcon className="text-accent w-4"></TrashIcon>
|
<TrashIcon className="text-error w-4"></TrashIcon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -227,15 +244,35 @@ const BacktestTable: React.FC<IBacktestCards> = ({list, isFetching, setBacktests
|
|||||||
accessor: 'id',
|
accessor: 'id',
|
||||||
disableFilters: true,
|
disableFilters: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
Cell: ({cell}: any) => (
|
Cell: ({cell}: any) => (
|
||||||
<>
|
<>
|
||||||
<div className="tooltip" data-tip="Re-run backtest with same config">
|
<div className="tooltip" data-tip="Create bot from backtest">
|
||||||
|
<button
|
||||||
|
data-value={cell.row.values.name}
|
||||||
|
onClick={() => handleOpenBotConfigModal(cell.row.original as Backtest)}
|
||||||
|
>
|
||||||
|
<PlayIcon className="text-success w-4"></PlayIcon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
Header: '',
|
||||||
|
accessor: 'runner',
|
||||||
|
disableFilters: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Cell: ({cell}: any) => (
|
||||||
|
<>
|
||||||
|
<div className="tooltip" data-tip="Rerun backtest with same config">
|
||||||
<button
|
<button
|
||||||
data-value={cell.row.values.name}
|
data-value={cell.row.values.name}
|
||||||
onClick={() => handleOpenBacktestConfigModal(cell.row.original as Backtest)}
|
onClick={() => handleOpenBacktestConfigModal(cell.row.original as Backtest)}
|
||||||
>
|
>
|
||||||
<CogIcon className="text-info w-4"></CogIcon>
|
<svg className="w-4 h-4 text-warning" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -247,18 +284,18 @@ const BacktestTable: React.FC<IBacktestCards> = ({list, isFetching, setBacktests
|
|||||||
{
|
{
|
||||||
Cell: ({cell}: any) => (
|
Cell: ({cell}: any) => (
|
||||||
<>
|
<>
|
||||||
<div className="tooltip" data-tip="Create bot from backtest">
|
<div className="tooltip" data-tip="View/Copy configuration">
|
||||||
<button
|
<button
|
||||||
data-value={cell.row.values.name}
|
data-value={cell.row.values.name}
|
||||||
onClick={() => handleOpenBotConfigModal(cell.row.original as Backtest)}
|
onClick={() => handleOpenConfigDisplayModal(cell.row.original as Backtest)}
|
||||||
>
|
>
|
||||||
<PlayIcon className="text-primary w-4"></PlayIcon>
|
<CogIcon className="text-info w-4"></CogIcon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
Header: '',
|
Header: '',
|
||||||
accessor: 'runner',
|
accessor: 'config',
|
||||||
disableFilters: true,
|
disableFilters: true,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -481,10 +518,15 @@ const BacktestTable: React.FC<IBacktestCards> = ({list, isFetching, setBacktests
|
|||||||
mode="backtest"
|
mode="backtest"
|
||||||
backtest={selectedBacktestForRerun}
|
backtest={selectedBacktestForRerun}
|
||||||
closeModal={handleCloseBacktestConfigModal}
|
closeModal={handleCloseBacktestConfigModal}
|
||||||
setBacktests={setBacktests}
|
|
||||||
showLoopSlider={true}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Config Display Modal */}
|
||||||
|
<ConfigDisplayModal
|
||||||
|
showModal={showConfigDisplayModal}
|
||||||
|
onClose={handleCloseConfigDisplayModal}
|
||||||
|
backtest={selectedBacktestForConfigView}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import React, {useEffect, useState} from 'react'
|
|||||||
import {type SubmitHandler, useForm} from 'react-hook-form'
|
import {type SubmitHandler, useForm} from 'react-hook-form'
|
||||||
|
|
||||||
import useApiUrlStore from '../../../app/store/apiStore'
|
import useApiUrlStore from '../../../app/store/apiStore'
|
||||||
|
import useBacktestStore from '../../../app/store/backtestStore'
|
||||||
import {useCustomMoneyManagement} from '../../../app/store/customMoneyManagement'
|
import {useCustomMoneyManagement} from '../../../app/store/customMoneyManagement'
|
||||||
import {useCustomScenario} from '../../../app/store/customScenario'
|
import {useCustomScenario} from '../../../app/store/customScenario'
|
||||||
import {
|
import {
|
||||||
@@ -26,7 +27,7 @@ import {
|
|||||||
UpdateBotConfigRequest,
|
UpdateBotConfigRequest,
|
||||||
} from '../../../generated/ManagingApi'
|
} from '../../../generated/ManagingApi'
|
||||||
import type {IUnifiedTradingConfigInput, UnifiedTradingModalProps} from '../../../global/type'
|
import type {IUnifiedTradingConfigInput, UnifiedTradingModalProps} from '../../../global/type'
|
||||||
import {Loader, Slider} from '../../atoms'
|
import {Loader} from '../../atoms'
|
||||||
import {Modal, Toast} from '../../mollecules'
|
import {Modal, Toast} from '../../mollecules'
|
||||||
import FormInput from '../../mollecules/FormInput/FormInput'
|
import FormInput from '../../mollecules/FormInput/FormInput'
|
||||||
import CustomMoneyManagement from '../CustomMoneyManagement/CustomMoneyManagement'
|
import CustomMoneyManagement from '../CustomMoneyManagement/CustomMoneyManagement'
|
||||||
@@ -36,7 +37,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
showModal,
|
showModal,
|
||||||
closeModal,
|
closeModal,
|
||||||
mode,
|
mode,
|
||||||
showLoopSlider = false,
|
|
||||||
setBacktests,
|
setBacktests,
|
||||||
backtest,
|
backtest,
|
||||||
existingBot,
|
existingBot,
|
||||||
@@ -118,11 +118,9 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
// State for collapsible sections
|
// State for collapsible sections
|
||||||
const [showAdvancedParams, setShowAdvancedParams] = useState(false);
|
const [showAdvancedParams, setShowAdvancedParams] = useState(false);
|
||||||
const [showRiskManagement, setShowRiskManagement] = useState(false);
|
const [showRiskManagement, setShowRiskManagement] = useState(false);
|
||||||
|
const [showJsonImport, setShowJsonImport] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
// State for loop slider (backtests only)
|
|
||||||
const [selectedLoopQuantity, setLoopQuantity] = React.useState<number>(
|
|
||||||
showLoopSlider ? 3 : 1
|
|
||||||
);
|
|
||||||
|
|
||||||
// Custom components state
|
// Custom components state
|
||||||
const [customMoneyManagement, setCustomMoneyManagement] = useState<MoneyManagement | undefined>(undefined);
|
const [customMoneyManagement, setCustomMoneyManagement] = useState<MoneyManagement | undefined>(undefined);
|
||||||
@@ -136,7 +134,12 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
// Selected ticker for bots (separate from form tickers for backtests)
|
// Selected ticker for bots (separate from form tickers for backtests)
|
||||||
const [selectedTicker, setSelectedTicker] = useState<Ticker | undefined>(undefined);
|
const [selectedTicker, setSelectedTicker] = useState<Ticker | undefined>(undefined);
|
||||||
|
|
||||||
|
// JSON import state
|
||||||
|
const [jsonConfig, setJsonConfig] = useState('');
|
||||||
|
const [importError, setImportError] = useState('');
|
||||||
|
|
||||||
const { apiUrl } = useApiUrlStore();
|
const { apiUrl } = useApiUrlStore();
|
||||||
|
const { addBacktest } = useBacktestStore();
|
||||||
const { setCustomMoneyManagement: setGlobalCustomMoneyManagement } = useCustomMoneyManagement();
|
const { setCustomMoneyManagement: setGlobalCustomMoneyManagement } = useCustomMoneyManagement();
|
||||||
const { setCustomScenario: setGlobalCustomScenario } = useCustomScenario();
|
const { setCustomScenario: setGlobalCustomScenario } = useCustomScenario();
|
||||||
|
|
||||||
@@ -436,6 +439,84 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleImportJsonConfig = () => {
|
||||||
|
try {
|
||||||
|
setImportError('');
|
||||||
|
const config = JSON.parse(jsonConfig);
|
||||||
|
|
||||||
|
// Load the configuration into the form
|
||||||
|
if (config.accountName) setValue('accountName', config.accountName);
|
||||||
|
if (config.timeframe) setValue('timeframe', config.timeframe);
|
||||||
|
if (config.cooldownPeriod !== undefined) setValue('cooldownPeriod', config.cooldownPeriod);
|
||||||
|
if (config.maxLossStreak !== undefined) setValue('maxLossStreak', config.maxLossStreak);
|
||||||
|
if (config.maxPositionTimeHours !== undefined) setValue('maxPositionTimeHours', config.maxPositionTimeHours);
|
||||||
|
if (config.flipOnlyWhenInProfit !== undefined) setValue('flipOnlyWhenInProfit', config.flipOnlyWhenInProfit);
|
||||||
|
if (config.closeEarlyWhenProfitable !== undefined) setValue('closeEarlyWhenProfitable', config.closeEarlyWhenProfitable);
|
||||||
|
if (config.botTradingBalance !== undefined) setValue('balance', config.botTradingBalance);
|
||||||
|
if (config.useSynthApi !== undefined) setValue('useSynthApi', config.useSynthApi);
|
||||||
|
if (config.useForPositionSizing !== undefined) setValue('useForPositionSizing', config.useForPositionSizing);
|
||||||
|
if (config.useForSignalFiltering !== undefined) setValue('useForSignalFiltering', config.useForSignalFiltering);
|
||||||
|
if (config.useForDynamicStopLoss !== undefined) setValue('useForDynamicStopLoss', config.useForDynamicStopLoss);
|
||||||
|
|
||||||
|
// For bot modes, set the ticker
|
||||||
|
if (mode !== 'backtest' && config.ticker) {
|
||||||
|
setSelectedTicker(config.ticker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For backtest mode, set tickers array
|
||||||
|
if (mode === 'backtest' && config.ticker) {
|
||||||
|
setValue('tickers', [config.ticker]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle scenario
|
||||||
|
if (config.scenario) {
|
||||||
|
setShowCustomScenario(true);
|
||||||
|
setCustomScenario(config.scenario);
|
||||||
|
setGlobalCustomScenario(config.scenario);
|
||||||
|
setSelectedScenario('custom');
|
||||||
|
} else if (config.scenarioName) {
|
||||||
|
setSelectedScenario(config.scenarioName);
|
||||||
|
setValue('scenarioName', config.scenarioName);
|
||||||
|
setShowCustomScenario(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle money management
|
||||||
|
if (config.moneyManagement) {
|
||||||
|
setShowCustomMoneyManagement(true);
|
||||||
|
setCustomMoneyManagement(config.moneyManagement);
|
||||||
|
|
||||||
|
// Convert decimal values to percentages for UI display
|
||||||
|
const formattedMoneyManagement = {
|
||||||
|
...config.moneyManagement,
|
||||||
|
stopLoss: config.moneyManagement.stopLoss * 100,
|
||||||
|
takeProfit: config.moneyManagement.takeProfit * 100,
|
||||||
|
};
|
||||||
|
setGlobalCustomMoneyManagement(formattedMoneyManagement);
|
||||||
|
setSelectedMoneyManagement('custom');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle risk management
|
||||||
|
if (config.riskManagement) {
|
||||||
|
setValue('useCustomRiskManagement', true);
|
||||||
|
setValue('riskManagement', config.riskManagement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bot-specific fields
|
||||||
|
if (mode !== 'backtest') {
|
||||||
|
if (config.name) setValue('name', config.name);
|
||||||
|
if (config.isForWatchingOnly !== undefined) setValue('isForWatchingOnly', config.isForWatchingOnly);
|
||||||
|
if (config.flipPosition !== undefined) setValue('flipPosition', config.flipPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
new Toast('Configuration imported successfully!', false);
|
||||||
|
setShowJsonImport(false);
|
||||||
|
setJsonConfig('');
|
||||||
|
} catch (error) {
|
||||||
|
setImportError('Invalid JSON format. Please check your configuration.');
|
||||||
|
console.error('JSON import error:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onMoneyManagementChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
const onMoneyManagementChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
if (e.target.value === 'custom') {
|
if (e.target.value === 'custom') {
|
||||||
setShowCustomMoneyManagement(true);
|
setShowCustomMoneyManagement(true);
|
||||||
@@ -632,21 +713,9 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
const backtest = await backtestClient.backtest_Run(request);
|
const backtest = await backtestClient.backtest_Run(request);
|
||||||
|
|
||||||
t.update('success', `${ticker} Backtest Succeeded`);
|
t.update('success', `${ticker} Backtest Succeeded`);
|
||||||
if (setBacktests) {
|
addBacktest(backtest);
|
||||||
setBacktests((arr) => [...arr, backtest]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showLoopSlider && selectedLoopQuantity > loopCount) {
|
|
||||||
const nextCount = loopCount + 1;
|
|
||||||
const mm: MoneyManagement = {
|
|
||||||
leverage: backtest.optimizedMoneyManagement.leverage,
|
|
||||||
name: backtest.optimizedMoneyManagement.name + nextCount,
|
|
||||||
stopLoss: backtest.optimizedMoneyManagement.stopLoss,
|
|
||||||
takeProfit: backtest.optimizedMoneyManagement.takeProfit,
|
|
||||||
timeframe: backtest.optimizedMoneyManagement.timeframe,
|
|
||||||
};
|
|
||||||
await runBacktest(form, ticker, nextCount);
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
t.update('error', 'Error: ' + err);
|
t.update('error', 'Error: ' + err);
|
||||||
throw err;
|
throw err;
|
||||||
@@ -682,6 +751,81 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
</FormInput>
|
</FormInput>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* JSON Import Section */}
|
||||||
|
<div className="divider">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-outline btn-sm normal-case"
|
||||||
|
onClick={() => setShowJsonImport(!showJsonImport)}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="w-4 h-4 mr-2"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10" />
|
||||||
|
</svg>
|
||||||
|
Import Configuration from JSON
|
||||||
|
<svg
|
||||||
|
className={`w-4 h-4 ml-2 transition-transform duration-200 ${showJsonImport ? 'rotate-180' : ''}`}
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showJsonImport && (
|
||||||
|
<div className="space-y-4 border border-accent rounded-lg p-4 bg-base-100">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text">Paste Configuration JSON</span>
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
className="textarea textarea-bordered w-full h-32"
|
||||||
|
placeholder='Paste your configuration JSON here...'
|
||||||
|
value={jsonConfig}
|
||||||
|
onChange={(e) => setJsonConfig(e.target.value)}
|
||||||
|
/>
|
||||||
|
{importError && (
|
||||||
|
<div className="alert alert-error">
|
||||||
|
<svg className="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<span>{importError}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary btn-sm"
|
||||||
|
onClick={handleImportJsonConfig}
|
||||||
|
disabled={!jsonConfig.trim()}
|
||||||
|
>
|
||||||
|
Import Configuration
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-ghost btn-sm"
|
||||||
|
onClick={() => {
|
||||||
|
setJsonConfig('');
|
||||||
|
setImportError('');
|
||||||
|
setShowJsonImport(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-base-content/70">
|
||||||
|
<p>Copy a configuration from the backtest table and paste it here to quickly load all settings.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* First Row: Account & Timeframe */}
|
{/* First Row: Account & Timeframe */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<FormInput label="Account" htmlFor="accountName">
|
<FormInput label="Account" htmlFor="accountName">
|
||||||
@@ -938,28 +1082,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Loop Slider (if enabled for backtests) */}
|
|
||||||
{showLoopSlider && mode === 'backtest' && (
|
|
||||||
<FormInput
|
|
||||||
label={
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
Loop
|
|
||||||
<div className="tooltip tooltip-top" data-tip="Number of optimization loops to run for money management. Each loop uses the optimized parameters from the previous iteration">
|
|
||||||
<span className="badge badge-info badge-xs">i</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
htmlFor="loop"
|
|
||||||
>
|
|
||||||
<Slider
|
|
||||||
id="loopSlider"
|
|
||||||
min="1"
|
|
||||||
max="10"
|
|
||||||
value={selectedLoopQuantity.toString()}
|
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setLoopQuantity(Number(e.target.value))}
|
|
||||||
/>
|
|
||||||
</FormInput>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Max Loss Streak & Max Position Time */}
|
{/* Max Loss Streak & Max Position Time */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ export interface UnifiedTradingModalProps {
|
|||||||
showModal: boolean
|
showModal: boolean
|
||||||
closeModal: () => void
|
closeModal: () => void
|
||||||
mode: 'backtest' | 'createBot' | 'updateBot'
|
mode: 'backtest' | 'createBot' | 'updateBot'
|
||||||
showLoopSlider?: boolean
|
|
||||||
|
|
||||||
// For backtests
|
// For backtests
|
||||||
setBacktests?: React.Dispatch<React.SetStateAction<Backtest[]>>
|
setBacktests?: React.Dispatch<React.SetStateAction<Backtest[]>>
|
||||||
|
|||||||
@@ -86,7 +86,15 @@ export type BacktestModalProps = {
|
|||||||
showModal: boolean
|
showModal: boolean
|
||||||
closeModal: () => void
|
closeModal: () => void
|
||||||
setBacktests: React.Dispatch<React.SetStateAction<Backtest[]>>
|
setBacktests: React.Dispatch<React.SetStateAction<Backtest[]>>
|
||||||
showLoopSlider?: boolean
|
}
|
||||||
|
|
||||||
|
export type UnifiedTradingModalProps = {
|
||||||
|
showModal: boolean
|
||||||
|
closeModal: () => void
|
||||||
|
mode: 'backtest' | 'createBot' | 'updateBot'
|
||||||
|
setBacktests?: React.Dispatch<React.SetStateAction<Backtest[]>>
|
||||||
|
backtest?: Backtest
|
||||||
|
existingBot?: any // TradingBotResponse or whatever the bot type is
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ISpotlightBadge = {
|
export type ISpotlightBadge = {
|
||||||
@@ -104,7 +112,6 @@ export type IBacktestsFormInput = {
|
|||||||
save: boolean
|
save: boolean
|
||||||
balance: number
|
balance: number
|
||||||
moneyManagement: MoneyManagement
|
moneyManagement: MoneyManagement
|
||||||
loop: number
|
|
||||||
startDate: string
|
startDate: string
|
||||||
endDate: string
|
endDate: string
|
||||||
cooldownPeriod: number
|
cooldownPeriod: number
|
||||||
@@ -122,7 +129,7 @@ export type IBacktestsFormInput = {
|
|||||||
export type IBacktestCards = {
|
export type IBacktestCards = {
|
||||||
list: Backtest[] | undefined
|
list: Backtest[] | undefined
|
||||||
isFetching?: boolean
|
isFetching?: boolean
|
||||||
setBacktests: React.Dispatch<React.SetStateAction<Backtest[]>>
|
setBacktests?: React.Dispatch<React.SetStateAction<Backtest[]>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IFormInput = {
|
export type IFormInput = {
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import React, {useState} from 'react'
|
|||||||
|
|
||||||
import 'react-toastify/dist/ReactToastify.css'
|
import 'react-toastify/dist/ReactToastify.css'
|
||||||
import {Tabs} from '../../components/mollecules'
|
import {Tabs} from '../../components/mollecules'
|
||||||
import type {TabsType} from '../../global/type'
|
|
||||||
|
|
||||||
import BacktestLoop from './backtestLoop'
|
|
||||||
import BacktestPlayground from './backtestPlayground'
|
import BacktestPlayground from './backtestPlayground'
|
||||||
import BacktestScanner from './backtestScanner'
|
import BacktestScanner from './backtestScanner'
|
||||||
import BacktestUpload from './backtestUpload'
|
import BacktestUpload from './backtestUpload'
|
||||||
|
import type {TabsType} from '../../global/type.tsx'
|
||||||
|
|
||||||
// Tabs Array
|
// Tabs Array
|
||||||
const tabs: TabsType = [
|
const tabs: TabsType = [
|
||||||
@@ -21,14 +19,9 @@ const tabs: TabsType = [
|
|||||||
index: 2,
|
index: 2,
|
||||||
label: 'Scanner',
|
label: 'Scanner',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Component: BacktestLoop,
|
|
||||||
index: 3,
|
|
||||||
label: 'Loop',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Component: BacktestUpload,
|
Component: BacktestUpload,
|
||||||
index: 4,
|
index: 3,
|
||||||
label: 'Upload',
|
label: 'Upload',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import React, {useState} from 'react'
|
import React, {useState} from 'react'
|
||||||
import 'react-toastify/dist/ReactToastify.css'
|
import 'react-toastify/dist/ReactToastify.css'
|
||||||
|
|
||||||
|
import useBacktestStore from '../../app/store/backtestStore'
|
||||||
import {BacktestCards, UnifiedTradingModal} from '../../components/organism'
|
import {BacktestCards, UnifiedTradingModal} from '../../components/organism'
|
||||||
import type {Backtest} from '../../generated/ManagingApi'
|
|
||||||
|
|
||||||
const BacktestPlayground: React.FC = () => {
|
const BacktestPlayground: React.FC = () => {
|
||||||
const [backtestingResult, setBacktest] = useState<Backtest[]>([])
|
const { backtests: backtestingResult } = useBacktestStore()
|
||||||
|
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
|
|
||||||
function openModal() {
|
function openModal() {
|
||||||
@@ -22,12 +21,11 @@ const BacktestPlayground: React.FC = () => {
|
|||||||
<button className="btn" onClick={openModal}>
|
<button className="btn" onClick={openModal}>
|
||||||
Run New Backtest
|
Run New Backtest
|
||||||
</button>
|
</button>
|
||||||
<BacktestCards list={backtestingResult} setBacktests={setBacktest} />
|
<BacktestCards list={backtestingResult} />
|
||||||
<UnifiedTradingModal
|
<UnifiedTradingModal
|
||||||
mode="backtest"
|
mode="backtest"
|
||||||
showModal={showModal}
|
showModal={showModal}
|
||||||
closeModal={closeModal}
|
closeModal={closeModal}
|
||||||
setBacktests={setBacktest}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ import React, {useEffect, useState} from 'react'
|
|||||||
|
|
||||||
import 'react-toastify/dist/ReactToastify.css'
|
import 'react-toastify/dist/ReactToastify.css'
|
||||||
import useApiUrlStore from '../../app/store/apiStore'
|
import useApiUrlStore from '../../app/store/apiStore'
|
||||||
|
import useBacktestStore from '../../app/store/backtestStore'
|
||||||
import {Loader} from '../../components/atoms'
|
import {Loader} 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 {BacktestClient} from '../../generated/ManagingApi'
|
import {BacktestClient} from '../../generated/ManagingApi'
|
||||||
|
|
||||||
const BacktestScanner: React.FC = () => {
|
const BacktestScanner: React.FC = () => {
|
||||||
const [backtestingResult, setBacktest] = useState<Backtest[]>([])
|
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
const [showModalRemoveBacktest, setShowModalRemoveBacktest] = useState(false)
|
const [showModalRemoveBacktest, setShowModalRemoveBacktest] = useState(false)
|
||||||
const { apiUrl } = useApiUrlStore()
|
const { apiUrl } = useApiUrlStore()
|
||||||
|
const { backtests: backtestingResult, setBacktests, setLoading } = useBacktestStore()
|
||||||
const client = new BacktestClient({}, apiUrl)
|
const client = new BacktestClient({}, apiUrl)
|
||||||
|
|
||||||
const { isLoading, refetch, data: backtests } = useQuery({
|
const { isLoading, refetch, data: backtests } = useQuery({
|
||||||
@@ -24,9 +24,13 @@ const BacktestScanner: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (backtests) {
|
if (backtests) {
|
||||||
setBacktest(backtests)
|
setBacktests(backtests)
|
||||||
}
|
}
|
||||||
}, [backtests])
|
}, [backtests, setBacktests])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(isLoading)
|
||||||
|
}, [isLoading, setLoading])
|
||||||
|
|
||||||
const openModalRemoveBacktests = () => {
|
const openModalRemoveBacktests = () => {
|
||||||
setShowModalRemoveBacktest(true)
|
setShowModalRemoveBacktest(true)
|
||||||
@@ -99,13 +103,12 @@ const BacktestScanner: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BacktestTable list={backtestingResult} isFetching={isLoading} setBacktests={setBacktest} />
|
<BacktestTable list={backtestingResult} isFetching={isLoading} />
|
||||||
|
|
||||||
<UnifiedTradingModal
|
<UnifiedTradingModal
|
||||||
mode="backtest"
|
mode="backtest"
|
||||||
showModal={showModal}
|
showModal={showModal}
|
||||||
closeModal={closeModal}
|
closeModal={closeModal}
|
||||||
setBacktests={setBacktest}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/****************************/}
|
{/****************************/}
|
||||||
|
|||||||
Reference in New Issue
Block a user