Push merge conflict

This commit is contained in:
2025-07-04 11:02:53 +07:00
parent 88f195c0ca
commit 59c5de7df7
27 changed files with 1133 additions and 1118 deletions

View File

@@ -155,11 +155,10 @@ The `TradingBotConfig` class defines all configuration parameters for trading bo
| `Timeframe` | `Timeframe` | Candle timeframe for analysis |
| `IsForWatchingOnly` | `bool` | If true, bot only sends signals without trading |
| `BotTradingBalance` | `decimal` | Initial trading balance for the bot |
| `BotType` | `BotType` | Type of trading bot behavior |
| `IsForBacktest` | `bool` | Whether this config is for backtesting |
| `CooldownPeriod` | `int` | Number of candles to wait before opening new position in same direction |
| `MaxLossStreak` | `int` | Maximum consecutive losses before requiring opposite direction signal (0 = no limit) |
| `FlipPosition` | `bool` | Whether the bot can flip positions |
| `FlipPosition` | `bool` | Whether the bot can flip positions (default: false) |
| `Name` | `string` | Unique identifier/name for the bot |
| `FlipOnlyWhenInProfit` | `bool` | Only flip positions when current position is profitable (default: true) |
@@ -183,8 +182,10 @@ The `TradingBotConfig` class defines all configuration parameters for trading bo
- Only closes when position is in profit or at breakeven (never closes at a loss due to time)
- `CloseEarlyWhenProfitable` allows immediate closure when profitable instead of waiting full duration
**Profit-Controlled Flipping:**
- `FlipOnlyWhenInProfit` ensures safer trading by only flipping profitable positions
**Bot Behavior Control:**
- `IsForWatchingOnly` controls whether the bot executes actual trades or only analyzes and generates signals
- `FlipPosition` controls whether the bot can flip positions when opposite signals occur (default: false)
- `FlipOnlyWhenInProfit` ensures safer trading by only flipping profitable positions when flipping is enabled
- Helps prevent cascading losses in volatile markets
**Synth API Integration:**
@@ -213,27 +214,22 @@ The `TradingBotConfig` class defines all configuration parameters for trading bo
| CloseEarlyWhenProfitable | Close positions early when profitable (requires MaxPositionTimeHours) | false |
| BotTradingBalance | Initial trading balance for the bot | Required |
### Bot Types
### Bot Behavior Configuration
The `BotType` enum in `TradingBotConfig` defines the following trading bot behaviors:
Trading bots support flexible behavior configuration through boolean flags rather than predefined types:
| Type | Description |
|-------------|----------------------------------------------------------------------------------------|
| SimpleBot | Basic bot implementation for simple trading strategies |
| ScalpingBot | Opens positions and waits for cooldown period before opening new ones in same direction |
| FlippingBot | Advanced bot that can flip positions when opposite signals are triggered |
#### Watch-Only Mode
- **`IsForWatchingOnly`**: When `true`, the bot analyzes market conditions and generates signals but does not execute actual trades
- Perfect for strategy testing, signal validation, or paper trading
- Useful for developing confidence in a strategy before committing real capital
#### Flipping Mode Configuration
The flipping behavior is controlled by several `TradingBotConfig` properties:
- **`BotType`**: Set to `FlippingBot` to enable position flipping capabilities
- **`FlipPosition`**: Boolean flag that enables/disables position flipping (automatically set based on BotType)
#### Position Flipping
- **`FlipPosition`**: When `true`, enables the bot to flip positions when opposite signals are triggered
- **`FlipOnlyWhenInProfit`**: Safety feature that only allows flipping when current position is profitable (default: true)
#### How Flipping Works
#### How Position Flipping Works
**FlippingBot Behavior:**
**When Flipping is Enabled (`FlipPosition = true`):**
1. Opens initial position based on scenario signals
2. Monitors for opposite direction signals from the same scenario
3. When opposite signal occurs:
@@ -242,11 +238,12 @@ The flipping behavior is controlled by several `TradingBotConfig` properties:
4. Closes current position and immediately opens new position in opposite direction
5. Continues this cycle for the duration of the bot's operation
**ScalpingBot vs FlippingBot:**
- **ScalpingBot**: Opens position → Waits for exit signal → Closes → Cooldown → Opens new position
- **FlippingBot**: Opens position → Monitors for opposite signals → Flips immediately (no cooldown between flips)
**When Flipping is Disabled (`FlipPosition = false`):**
- Opens position → Waits for exit signal → Closes → Cooldown → Opens new position in same direction
- More conservative approach with cooldown periods between trades
- Reduces frequency of trades and potential whipsaw losses
This configuration allows for more aggressive trading strategies while maintaining risk management through the profit-controlled flipping mechanism.
This simplified configuration provides clear control over bot behavior while maintaining risk management through the profit-controlled flipping mechanism.
## Backtesting (`BacktestController`)

View File

@@ -10,7 +10,6 @@ using Managing.Domain.Strategies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using static Managing.Common.Enums;
namespace Managing.Api.Controllers;
@@ -192,15 +191,14 @@ public class BacktestController : BaseController
ScenarioName = request.Config.ScenarioName,
Scenario = scenario, // Use the converted scenario object
Timeframe = request.Config.Timeframe,
IsForWatchingOnly = request.WatchOnly,
BotTradingBalance = request.Balance,
BotType = request.Config.BotType,
IsForWatchingOnly = request.Config.IsForWatchingOnly,
BotTradingBalance = request.Config.BotTradingBalance,
IsForBacktest = true,
CooldownPeriod = request.Config.CooldownPeriod,
MaxLossStreak = request.Config.MaxLossStreak,
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
FlipPosition = request.Config.BotType == BotType.FlippingBot, // Computed based on BotType
FlipPosition = request.Config.FlipPosition, // Computed based on BotType
Name = request.Config.Name ??
$"Backtest-{request.Config.ScenarioName ?? request.Config.Scenario?.Name ?? "Custom"}-{DateTime.UtcNow:yyyyMMdd-HHmmss}",
CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable,
@@ -210,21 +208,12 @@ public class BacktestController : BaseController
UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss
};
switch (request.Config.BotType)
{
case BotType.SimpleBot:
// SimpleBot backtest not implemented yet
break;
case BotType.ScalpingBot:
case BotType.FlippingBot:
backtestResult = await _backtester.RunTradingBotBacktest(
backtestConfig,
request.StartDate,
request.EndDate,
user,
request.Save);
break;
}
await NotifyBacktesingSubscriberAsync(backtestResult);
@@ -281,16 +270,6 @@ public class RunBacktestRequest
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// The starting balance for the backtest
/// </summary>
public decimal Balance { get; set; }
/// <summary>
/// Whether to only watch the backtest without executing trades
/// </summary>
public bool WatchOnly { get; set; } = false;
/// <summary>
/// Whether to save the backtest results
/// </summary>

View File

@@ -224,7 +224,6 @@ public class BotController : BaseController
Timeframe = request.Config.Timeframe,
IsForWatchingOnly = request.Config.IsForWatchingOnly,
BotTradingBalance = request.Config.BotTradingBalance,
BotType = request.Config.BotType,
CooldownPeriod = request.Config.CooldownPeriod,
MaxLossStreak = request.Config.MaxLossStreak,
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
@@ -236,7 +235,7 @@ public class BotController : BaseController
UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss,
// Set computed/default properties
IsForBacktest = false,
FlipPosition = request.Config.BotType == BotType.FlippingBot,
FlipPosition = request.Config.FlipPosition,
Name = request.Config.Name
};
@@ -256,12 +255,11 @@ public class BotController : BaseController
/// <summary>
/// Stops a bot specified by type and name.
/// </summary>
/// <param name="botType">The type of the bot to stop.</param>
/// <param name="identifier">The identifier of the bot to stop.</param>
/// <returns>A string indicating the result of the stop operation.</returns>
[HttpGet]
[Route("Stop")]
public async Task<ActionResult<string>> Stop(BotType botType, string identifier)
public async Task<ActionResult<string>> Stop(string identifier)
{
try
{
@@ -271,8 +269,8 @@ public class BotController : BaseController
return Forbid("You don't have permission to stop this bot");
}
var result = await _mediator.Send(new StopBotCommand(botType, identifier));
_logger.LogInformation($"{botType} type with identifier {identifier} is now {result}");
var result = await _mediator.Send(new StopBotCommand(identifier));
_logger.LogInformation($"Bot identifier {identifier} is now {result}");
await NotifyBotSubscriberAsync();
@@ -343,7 +341,7 @@ public class BotController : BaseController
foreach (var bot in userBots)
{
await _mediator.Send(new StopBotCommand(bot.Config.BotType, bot.Identifier));
await _mediator.Send(new StopBotCommand(bot.Identifier));
await _hubContext.Clients.All.SendAsync("SendNotification",
$"Bot {bot.Identifier} paused by {user.Name}.", "Info");
}
@@ -422,7 +420,8 @@ public class BotController : BaseController
{
// We can't directly restart a bot with just BotType and Name
// Instead, stop the bot and then retrieve the backup to start it again
await _mediator.Send(new StopBotCommand(bot.Config.BotType, bot.Identifier));
await _mediator.Send(
new StopBotCommand(bot.Identifier));
// Get the saved bot backup
var backup = _botService.GetBotBackup(bot.Identifier);
@@ -776,7 +775,6 @@ public class BotController : BaseController
Timeframe = request.Config.Timeframe,
IsForWatchingOnly = request.Config.IsForWatchingOnly,
BotTradingBalance = request.Config.BotTradingBalance,
BotType = request.Config.BotType,
CooldownPeriod = request.Config.CooldownPeriod,
MaxLossStreak = request.Config.MaxLossStreak,
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
@@ -788,7 +786,7 @@ public class BotController : BaseController
UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss,
// Set computed/default properties
IsForBacktest = false,
FlipPosition = request.Config.BotType == BotType.FlippingBot,
FlipPosition = request.Config.FlipPosition,
Name = request.Config.Name
};

View File

@@ -38,18 +38,14 @@ public class TradingBotConfigRequest
[Required]
public decimal BotTradingBalance { get; set; }
/// <summary>
/// The type of bot (SimpleBot, ScalpingBot, FlippingBot)
/// </summary>
[Required]
public BotType BotType { get; set; }
/// <summary>
/// The name/identifier for this bot
/// </summary>
[Required]
public string Name { get; set; }
[Required] public bool FlipPosition { get; set; }
/// <summary>
/// Cooldown period between trades (in candles)
/// </summary>

View File

@@ -272,7 +272,6 @@ public class StatisticService : IStatisticService
Timeframe = timeframe,
IsForWatchingOnly = true,
BotTradingBalance = 1000,
BotType = BotType.ScalpingBot,
IsForBacktest = true,
CooldownPeriod = 1,
MaxLossStreak = 0,

View File

@@ -20,11 +20,5 @@ namespace Managing.Application.Abstractions
/// <param name="config">The trading bot configuration</param>
/// <returns>ITradingBot instance configured for backtesting</returns>
ITradingBot CreateBacktestTradingBot(TradingBotConfig config);
// Legacy methods - these will use TradingBot internally but maintain backward compatibility
ITradingBot CreateScalpingBot(TradingBotConfig config);
ITradingBot CreateBacktestScalpingBot(TradingBotConfig config);
ITradingBot CreateFlippingBot(TradingBotConfig config);
ITradingBot CreateBacktestFlippingBot(TradingBotConfig config);
}
}

View File

@@ -114,7 +114,7 @@ namespace Managing.Application.Backtesting
User user = null)
{
// Set FlipPosition based on BotType
config.FlipPosition = config.BotType == BotType.FlippingBot;
config.FlipPosition = config.FlipPosition;
var tradingBot = _botFactory.CreateBacktestTradingBot(config);
@@ -164,11 +164,11 @@ namespace Managing.Application.Backtesting
private List<Candle> GetCandles(Account account, Ticker ticker, Timeframe timeframe,
DateTime startDate, DateTime endDate)
{
var candles = _exchangeService.GetCandlesInflux(account.Exchange, ticker,
var candles = _exchangeService.GetCandlesInflux(TradingExchanges.Evm, ticker,
startDate, timeframe, endDate).Result;
if (candles == null || candles.Count == 0)
throw new Exception($"No candles for {ticker} on {account.Exchange}");
throw new Exception($"No candles for {ticker} on {timeframe} timeframe");
return candles;
}

View File

@@ -3,7 +3,6 @@ using Managing.Application.Abstractions.Services;
using Managing.Domain.Bots;
using Managing.Domain.Workflows;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
namespace Managing.Application.Bots.Base
{
@@ -61,64 +60,5 @@ namespace Managing.Application.Bots.Base
_botService,
config);
}
// Legacy methods for backward compatibility - will be deprecated
ITradingBot IBotFactory.CreateScalpingBot(TradingBotConfig config)
{
config.BotType = BotType.ScalpingBot;
config.FlipPosition = false;
return new TradingBot(
_exchangeService,
_tradingBotLogger,
_tradingService,
_accountService,
_messengerService,
_botService,
config);
}
ITradingBot IBotFactory.CreateBacktestScalpingBot(TradingBotConfig config)
{
config.BotType = BotType.ScalpingBot;
config.IsForBacktest = true;
config.FlipPosition = false;
return new TradingBot(
_exchangeService,
_tradingBotLogger,
_tradingService,
_accountService,
_messengerService,
_botService,
config);
}
public ITradingBot CreateFlippingBot(TradingBotConfig config)
{
config.BotType = BotType.FlippingBot;
config.FlipPosition = true;
return new TradingBot(
_exchangeService,
_tradingBotLogger,
_tradingService,
_accountService,
_messengerService,
_botService,
config);
}
public ITradingBot CreateBacktestFlippingBot(TradingBotConfig config)
{
config.BotType = BotType.FlippingBot;
config.IsForBacktest = true;
config.FlipPosition = true;
return new TradingBot(
_exchangeService,
_tradingBotLogger,
_tradingService,
_accountService,
_messengerService,
_botService,
config);
}
}
}

View File

@@ -187,7 +187,7 @@ public class TradingBot : Bot, ITradingBot
Logger.LogInformation($"____________________{Name}____________________");
Logger.LogInformation(
$"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Last candle : {OptimizedCandles.Last().Date} - Bot : {Name} - Type {Config.BotType} - Ticker : {Config.Ticker}");
$"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Last candle : {OptimizedCandles.Last().Date} - Bot : {Name} - Ticker : {Config.Ticker}");
}
var previousLastCandle = OptimizedCandles.LastOrDefault();
@@ -1450,7 +1450,6 @@ public class TradingBot : Bot, ITradingBot
}
// Protect critical properties that shouldn't change for running bots
var protectedBotType = Config.BotType;
var protectedIsForBacktest = Config.IsForBacktest;
var protectedName = allowNameChange ? newConfig.Name : Config.Name;
@@ -1470,7 +1469,6 @@ public class TradingBot : Bot, ITradingBot
Config = newConfig;
// Restore protected properties
Config.BotType = protectedBotType;
Config.IsForBacktest = protectedIsForBacktest;
Config.Name = protectedName;
@@ -1532,7 +1530,6 @@ public class TradingBot : Bot, ITradingBot
Timeframe = Config.Timeframe,
IsForWatchingOnly = Config.IsForWatchingOnly,
BotTradingBalance = Config.BotTradingBalance,
BotType = Config.BotType,
IsForBacktest = Config.IsForBacktest,
CooldownPeriod = Config.CooldownPeriod,
MaxLossStreak = Config.MaxLossStreak,

View File

@@ -1,17 +1,14 @@
using MediatR;
using static Managing.Common.Enums;
namespace Managing.Application.ManageBot.Commands
{
public class StopBotCommand : IRequest<string>
{
public string Name { get; }
public BotType BotType { get; }
public string Identifier { get; }
public StopBotCommand(BotType botType, string name)
public StopBotCommand(string identifier)
{
BotType = botType;
Name = name;
Identifier = identifier;
}
}
}

View File

@@ -70,37 +70,25 @@ namespace Managing.Application.ManageBot
Timeframe = request.Config.Timeframe,
IsForWatchingOnly = request.Config.IsForWatchingOnly,
BotTradingBalance = request.Config.BotTradingBalance,
BotType = request.Config.BotType,
IsForBacktest = request.Config.IsForBacktest,
CooldownPeriod =
request.Config.CooldownPeriod > 0 ? request.Config.CooldownPeriod : 1, // Default to 1 if not set
MaxLossStreak = request.Config.MaxLossStreak,
MaxPositionTimeHours = request.Config.MaxPositionTimeHours, // Properly handle nullable value
FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
FlipPosition = request.Config.BotType == BotType.FlippingBot, // Set FlipPosition based on BotType
FlipPosition = request.Config.FlipPosition, // Set FlipPosition
Name = request.Config.Name ?? request.Name,
CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable
};
switch (configToUse.BotType)
{
case BotType.SimpleBot:
var bot = _botFactory.CreateSimpleBot(request.Name, null);
bot.User = request.User;
_botService.AddSimpleBotToCache(bot);
return bot.GetStatus();
case BotType.ScalpingBot:
case BotType.FlippingBot:
var tradingBot = _botFactory.CreateTradingBot(configToUse);
tradingBot.User = request.User;
// Log the configuration being used
await LogBotConfigurationAsync(tradingBot, $"{configToUse.BotType} created");
await LogBotConfigurationAsync(tradingBot, $"{configToUse.Name} created");
_botService.AddTradingBotToCache(tradingBot);
return tradingBot.GetStatus();
}
return botStatus.ToString();
}
@@ -116,7 +104,6 @@ namespace Managing.Application.ManageBot
{
var config = bot.GetConfiguration();
var logMessage = $"{context} - Bot: {config.Name}, " +
$"Type: {config.BotType}, " +
$"Account: {config.AccountName}, " +
$"Ticker: {config.Ticker}, " +
$"Balance: {config.BotTradingBalance}, " +

View File

@@ -15,7 +15,7 @@ namespace Managing.Application.ManageBot
public Task<string> Handle(StopBotCommand request, CancellationToken cancellationToken)
{
return _botService.StopBot(request.Name);
return _botService.StopBot(request.Identifier);
}
}
}

View File

@@ -82,7 +82,6 @@ public class Backtest
Timeframe = Config.Timeframe,
IsForWatchingOnly = false, // Always start as active bot
BotTradingBalance = initialTradingBalance,
BotType = Config.BotType,
IsForBacktest = false, // Always false for live bots
CooldownPeriod = Config.CooldownPeriod,
MaxLossStreak = Config.MaxLossStreak,
@@ -117,7 +116,6 @@ public class Backtest
Timeframe = Config.Timeframe,
IsForWatchingOnly = Config.IsForWatchingOnly,
BotTradingBalance = balance,
BotType = Config.BotType,
IsForBacktest = true,
CooldownPeriod = Config.CooldownPeriod,
MaxLossStreak = Config.MaxLossStreak,

View File

@@ -14,7 +14,6 @@ public class TradingBotConfig
[Required] public Timeframe Timeframe { get; set; }
[Required] public bool IsForWatchingOnly { get; set; }
[Required] public decimal BotTradingBalance { get; set; }
[Required] public BotType BotType { get; set; }
[Required] public bool IsForBacktest { get; set; }
[Required] public int CooldownPeriod { get; set; }
[Required] public int MaxLossStreak { get; set; }

View File

@@ -1,7 +1,6 @@
using Exilion.TradingAtomics;
using Managing.Infrastructure.Databases.MongoDb.Attributes;
using Managing.Infrastructure.Databases.MongoDb.Configurations;
using static Managing.Common.Enums;
namespace Managing.Infrastructure.Databases.MongoDb.Collections
{
@@ -12,6 +11,7 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
public int WinRate { get; set; }
public decimal GrowthPercentage { get; set; }
public decimal HodlPercentage { get; set; }
public TradingBotConfigDto Config { get; set; }
public List<PositionDto> Positions { get; set; }
public List<SignalDto> Signals { get; set; }
public List<CandleDto> Candles { get; set; }
@@ -22,17 +22,5 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
public UserDto User { get; set; }
public PerformanceMetrics Statistics { get; set; }
public double Score { get; set; }
// TradingBotConfig properties
public string AccountName { get; set; }
public Ticker Ticker { get; set; }
public string ScenarioName { get; set; }
public Timeframe Timeframe { get; set; }
public bool IsForWatchingOnly { get; set; }
public decimal BotTradingBalance { get; set; }
public BotType BotType { get; set; }
public bool IsForBacktest { get; set; }
public int CooldownPeriod { get; set; }
public int MaxLossStreak { get; set; }
}
}

View File

@@ -4,6 +4,7 @@ using Managing.Domain.Backtests;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Risk;
using Managing.Domain.Scenarios;
using Managing.Domain.Statistics;
using Managing.Domain.Strategies;
@@ -132,20 +133,7 @@ public static class MongoMappers
if (b == null)
return null;
var config = new TradingBotConfig
{
AccountName = b.AccountName,
Ticker = b.Ticker,
ScenarioName = b.ScenarioName,
Timeframe = b.Timeframe,
IsForWatchingOnly = b.IsForWatchingOnly,
BotTradingBalance = b.BotTradingBalance,
BotType = b.BotType,
IsForBacktest = b.IsForBacktest,
CooldownPeriod = b.CooldownPeriod,
MaxLossStreak = b.MaxLossStreak,
MoneyManagement = Map(b.MoneyManagement)
};
var config = Map(b.Config);
var bTest = new Backtest(
config,
@@ -181,6 +169,7 @@ public static class MongoMappers
WinRate = result.WinRate,
GrowthPercentage = result.GrowthPercentage,
HodlPercentage = result.HodlPercentage,
Config = Map(result.Config),
Positions = Map(result.Positions),
Signals = result.Signals.Select(s => Map(s)).ToList(),
Candles = result.Candles.Select(c => Map(c)).ToList(),
@@ -191,16 +180,6 @@ public static class MongoMappers
StartDate = result.StartDate,
EndDate = result.EndDate,
Score = result.Score,
AccountName = result.Config.AccountName,
Ticker = result.Config.Ticker,
ScenarioName = result.Config.ScenarioName,
Timeframe = result.Config.Timeframe,
IsForWatchingOnly = result.Config.IsForWatchingOnly,
BotTradingBalance = result.Config.BotTradingBalance,
BotType = result.Config.BotType,
IsForBacktest = result.Config.IsForBacktest,
CooldownPeriod = result.Config.CooldownPeriod,
MaxLossStreak = result.Config.MaxLossStreak
};
}
@@ -933,4 +912,122 @@ public static class MongoMappers
}
#endregion
#region TradingBotConfig
public static TradingBotConfigDto Map(TradingBotConfig config)
{
if (config == null)
return null;
return new TradingBotConfigDto
{
AccountName = config.AccountName,
MoneyManagement = Map(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 = Map(config.RiskManagement),
Scenario = Map(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
};
}
public static TradingBotConfig Map(TradingBotConfigDto dto)
{
if (dto == null)
return null;
return new TradingBotConfig
{
AccountName = dto.AccountName,
MoneyManagement = Map(dto.MoneyManagement),
Ticker = dto.Ticker,
Timeframe = dto.Timeframe,
IsForWatchingOnly = dto.IsForWatchingOnly,
BotTradingBalance = dto.BotTradingBalance,
IsForBacktest = dto.IsForBacktest,
CooldownPeriod = dto.CooldownPeriod,
MaxLossStreak = dto.MaxLossStreak,
FlipPosition = dto.FlipPosition,
Name = dto.Name,
RiskManagement = Map(dto.RiskManagement),
Scenario = Map(dto.Scenario),
ScenarioName = dto.ScenarioName,
MaxPositionTimeHours = dto.MaxPositionTimeHours,
CloseEarlyWhenProfitable = dto.CloseEarlyWhenProfitable,
FlipOnlyWhenInProfit = dto.FlipOnlyWhenInProfit,
UseSynthApi = dto.UseSynthApi,
UseForPositionSizing = dto.UseForPositionSizing,
UseForSignalFiltering = dto.UseForSignalFiltering,
UseForDynamicStopLoss = dto.UseForDynamicStopLoss
};
}
#endregion
#region RiskManagement
public static RiskManagementDto Map(RiskManagement riskManagement)
{
if (riskManagement == null)
return null;
return new RiskManagementDto
{
AdverseProbabilityThreshold = riskManagement.AdverseProbabilityThreshold,
FavorableProbabilityThreshold = riskManagement.FavorableProbabilityThreshold,
RiskAversion = riskManagement.RiskAversion,
KellyMinimumThreshold = riskManagement.KellyMinimumThreshold,
KellyMaximumCap = riskManagement.KellyMaximumCap,
MaxLiquidationProbability = riskManagement.MaxLiquidationProbability,
SignalValidationTimeHorizonHours = riskManagement.SignalValidationTimeHorizonHours,
PositionMonitoringTimeHorizonHours = riskManagement.PositionMonitoringTimeHorizonHours,
PositionWarningThreshold = riskManagement.PositionWarningThreshold,
PositionAutoCloseThreshold = riskManagement.PositionAutoCloseThreshold,
KellyFractionalMultiplier = riskManagement.KellyFractionalMultiplier,
RiskTolerance = riskManagement.RiskTolerance,
UseExpectedUtility = riskManagement.UseExpectedUtility,
UseKellyCriterion = riskManagement.UseKellyCriterion
};
}
public static RiskManagement Map(RiskManagementDto dto)
{
if (dto == null)
return null;
return new RiskManagement
{
AdverseProbabilityThreshold = dto.AdverseProbabilityThreshold,
FavorableProbabilityThreshold = dto.FavorableProbabilityThreshold,
RiskAversion = dto.RiskAversion,
KellyMinimumThreshold = dto.KellyMinimumThreshold,
KellyMaximumCap = dto.KellyMaximumCap,
MaxLiquidationProbability = dto.MaxLiquidationProbability,
SignalValidationTimeHorizonHours = dto.SignalValidationTimeHorizonHours,
PositionMonitoringTimeHorizonHours = dto.PositionMonitoringTimeHorizonHours,
PositionWarningThreshold = dto.PositionWarningThreshold,
PositionAutoCloseThreshold = dto.PositionAutoCloseThreshold,
KellyFractionalMultiplier = dto.KellyFractionalMultiplier,
RiskTolerance = dto.RiskTolerance,
UseExpectedUtility = dto.UseExpectedUtility,
UseKellyCriterion = dto.UseKellyCriterion
};
}
#endregion
}

View File

@@ -21,6 +21,7 @@ const Modal: React.FC<IModalProps> = ({
titleHeader={titleHeader}
onClose={onClose}
onSubmit={onSubmit}
showModal={showModal}
/>
{children}
</div>

View File

@@ -10,9 +10,7 @@ import Logo from '../../../assets/img/logo.png'
import {Loader} from '../../atoms'
const navigation = [
{ href: '/desk', name: 'Desk' },
{ href: '/bots', name: 'Bots' },
{ href: '/workflow', name: 'Workflows' },
{ href: '/scenarios', name: 'Scenarios' },
{ href: '/backtest', name: 'Backtest' },
{ href: '/tools', name: 'Tools' },

View File

@@ -1,6 +1,6 @@
import type { FC } from 'react'
import type {FC} from 'react'
import type { ITabsProps } from '../../../global/type'
import type {ITabsProps} from '../../../global/type.tsx'
/**
* Avalible Props
@@ -19,7 +19,7 @@ const Tabs: FC<ITabsProps> = ({
addButton = false,
onAddButton,
}) => {
const Panel = tabs && tabs.find((tab) => tab.index === selectedTab)
const Panel = tabs && tabs.find((tab: any) => tab.index === selectedTab)
return (
<div
@@ -28,7 +28,7 @@ const Tabs: FC<ITabsProps> = ({
}
>
<div className="tabs" role="tablist" aria-orientation={orientation}>
{tabs.map((tab) => (
{tabs.map((tab: any) => (
<button
className={
'mb-5 tab tab-bordered ' +

View File

@@ -52,9 +52,9 @@ function daysBetween(date: Date) {
return diffDays
}
const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
const BacktestCards: React.FC<IBacktestCards> = ({list, setBacktests}) => {
console.log(list)
const { apiUrl } = useApiUrlStore()
const {apiUrl} = useApiUrlStore()
const [showMoneyManagementModal, setShowMoneyManagementModal] =
React.useState(false)
const [selectedMoneyManagement, setSelectedMoneyManagement] =
@@ -65,7 +65,7 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
const [selectedMoneyManagementName, setSelectedMoneyManagementName] = useState<string>('')
// Fetch money managements
const { data: moneyManagements } = useQuery({
const {data: moneyManagements} = useQuery({
queryFn: async () => {
const moneyManagementClient = new MoneyManagementClient({}, apiUrl)
return await moneyManagementClient.moneyManagement_GetMoneyManagements()
@@ -96,7 +96,6 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
ticker: backtest.config.ticker,
scenarioName: backtest.config.scenarioName,
timeframe: backtest.config.timeframe,
botType: backtest.config.botType,
isForWatchingOnly: isForWatchOnly,
isForBacktest: false, // This is for running a live bot
cooldownPeriod: backtest.config.cooldownPeriod,
@@ -167,8 +166,6 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
config: optimizedConfig as unknown as TradingBotConfigRequest,
startDate: startDate,
endDate: endDate,
balance: backtest.walletBalances[0].value,
watchOnly: false,
save: false,
}
@@ -225,7 +222,7 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
tabIndex={index}
className=""
>
<DotsVerticalIcon className="text-primary w-5 h-5" />
<DotsVerticalIcon className="text-primary w-5 h-5"/>
</label>
<ul
id={index.toString()}
@@ -285,7 +282,7 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
></CardText>
<CardText
title="Scenario"
content={backtest.config.scenarioName}
content={backtest.config.scenarioName ?? backtest.config.scenario?.name}
></CardText>
<CardText
title="Timeframe"

View File

@@ -351,16 +351,15 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
content={getAverageTradesPerDay() + " trades/day"}
></CardText>
</div>
<div>
<figure>
<div className="w-full">
<figure className="w-full">
<TradeChart
width={1400}
height={1100}
candles={candles}
positions={positions}
walletBalances={walletBalances}
indicatorsValues={indicatorsValues}
signals={signals}
height={1000}
></TradeChart>
</figure>
</div>

View File

@@ -4,16 +4,16 @@ import React, {useEffect, useState} from 'react'
import useApiUrlStore from '../../../app/store/apiStore'
import type {Backtest} from '../../../generated/ManagingApi'
import {BacktestClient} from '../../../generated/ManagingApi'
import type {IBacktestCards} from '../../../global/type'
import type {IBacktestCards} from '../../../global/type.tsx'
import {CardText, SelectColumnFilter, Table} from '../../mollecules'
import {UnifiedTradingModal} from '../index'
import Toast from '../../mollecules/Toast/Toast'
import BacktestRowDetails from './backtestRowDetails'
const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktests }) => {
const BacktestTable: React.FC<IBacktestCards> = ({list, isFetching, setBacktests}) => {
const [rows, setRows] = useState<Backtest[]>([])
const { apiUrl } = useApiUrlStore()
const {apiUrl} = useApiUrlStore()
const [optimizedMoneyManagement, setOptimizedMoneyManagement] = useState({
stopLoss: 0,
takeProfit: 0,
@@ -88,7 +88,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktest
Header: 'Informations',
columns: [
{
Cell: ({ row }: any) => (
Cell: ({row}: any) => (
// Use Cell to render an expander for each row.
// We can use the getToggleRowExpandedProps prop-getter
// to build the expander.
@@ -116,7 +116,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktest
{
Header: 'Score',
accessor: 'score',
Cell: ({ cell }: any) => (
Cell: ({cell}: any) => (
<span style={{
color: getScoreColor(cell.row.values.score),
fontWeight: 500,
@@ -159,7 +159,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktest
Header: 'Results',
columns: [
{
Cell: ({ cell }: any) => (
Cell: ({cell}: any) => (
<>{cell.row.values.finalPnl.toFixed(2)} $</>
),
Header: 'Pnl $',
@@ -168,7 +168,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktest
sortType: 'basic',
},
{
Cell: ({ cell }: any) => (
Cell: ({cell}: any) => (
<>{cell.row.values.hodlPercentage.toFixed(2)} %</>
),
Header: 'Hodl %',
@@ -177,13 +177,13 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktest
sortType: 'basic',
},
{
Cell: ({ cell }: any) => <>{cell.row.values.winRate} %</>,
Cell: ({cell}: any) => <>{cell.row.values.winRate} %</>,
Header: 'Winrate',
accessor: 'winRate',
disableFilters: true,
},
{
Cell: ({ cell }: any) => (
Cell: ({cell}: any) => (
<>{cell.row.values.growthPercentage.toFixed(2)} %</>
),
Header: 'Pnl %',
@@ -192,7 +192,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktest
sortType: 'basic',
},
{
Cell: ({ cell }: any) => (
Cell: ({cell}: any) => (
<>
{(
cell.row.values.growthPercentage -
@@ -211,7 +211,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktest
Header: 'Action',
columns: [
{
Cell: ({ cell }: any) => (
Cell: ({cell}: any) => (
<>
<div className="tooltip" data-tip="Delete backtest">
<button
@@ -228,7 +228,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktest
disableFilters: true,
},
{
Cell: ({ cell }: any) => (
Cell: ({cell}: any) => (
<>
<div className="tooltip" data-tip="Re-run backtest with same config">
<button
@@ -245,7 +245,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktest
disableFilters: true,
},
{
Cell: ({ cell }: any) => (
Cell: ({cell}: any) => (
<>
<div className="tooltip" data-tip="Create bot from backtest">
<button
@@ -457,7 +457,7 @@ const BacktestTable: React.FC<IBacktestCards> = ({ list, isFetching, setBacktest
<Table
columns={columns}
data={rows}
renderRowSubCompontent={({ row }: any) => (
renderRowSubCompontent={({row}: any) => (
<BacktestRowDetails
backtest={row.original}
/>

View File

@@ -47,8 +47,8 @@ type ITradeChartProps = {
walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null
indicatorsValues?: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; } | null;
stream?: Candle | null
width: number
height: number
width?: number
height?: number
}
const TradeChart = ({
@@ -62,12 +62,88 @@ const TradeChart = ({
height,
}: ITradeChartProps) => {
const chartRef = React.useRef<HTMLDivElement>(null)
const containerRef = React.useRef<HTMLDivElement>(null)
const chart = useRef<IChartApi>()
const {themeProperty} = useTheme()
const theme = themeProperty()
const series1 = useRef<ISeriesApi<'Candlestick'>>()
const [timeDiff, setTimeDiff] = useState<number>(0)
const [candleCount, setCandleCount] = useState<number>(candles.length)
const [chartDimensions, setChartDimensions] = useState({ width: 0, height: 0 })
// Get responsive dimensions
const getResponsiveDimensions = () => {
if (!containerRef.current) return { width: width || 510, height: height || 300 }
const containerWidth = containerRef.current.offsetWidth
const containerHeight = containerRef.current.offsetHeight
// Use provided dimensions if available, otherwise calculate responsive ones
if (width && height) {
return { width, height }
}
// For responsive mode, calculate based on container
const calculatedWidth = containerWidth > 0 ? containerWidth : 510
// Use different aspect ratios for different screen sizes
let aspectRatio = 0.6 // Default ratio (height/width)
if (containerWidth < 768) { // Mobile
aspectRatio = 0.8 // Taller on mobile for better visibility
} else if (containerWidth < 1024) { // Tablet
aspectRatio = 0.65
}
const calculatedHeight = height || Math.max(250, calculatedWidth * aspectRatio)
return {
width: calculatedWidth,
height: calculatedHeight
}
}
// Resize observer to handle container size changes
useEffect(() => {
if (!containerRef.current) return
const resizeObserver = new ResizeObserver(() => {
const newDimensions = getResponsiveDimensions()
setChartDimensions(newDimensions)
if (chart.current) {
chart.current.applyOptions({
width: newDimensions.width,
height: newDimensions.height
})
}
})
resizeObserver.observe(containerRef.current)
return () => {
resizeObserver.disconnect()
}
}, [width, height])
// Handle window resize for additional responsiveness
useEffect(() => {
const handleResize = () => {
setTimeout(() => {
const newDimensions = getResponsiveDimensions()
setChartDimensions(newDimensions)
if (chart.current) {
chart.current.applyOptions({
width: newDimensions.width,
height: newDimensions.height
})
}
}, 100) // Small delay to ensure container has updated
}
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [width, height])
function buildLine(
color: string,
@@ -164,7 +240,10 @@ const TradeChart = ({
}
useEffect(() => {
if (chartRef.current) {
if (chartRef.current && containerRef.current) {
const initialDimensions = getResponsiveDimensions()
setChartDimensions(initialDimensions)
const lineColor = theme['base-100']
chart.current = createChart(chartRef.current, {
crosshair: {
@@ -194,7 +273,7 @@ const TradeChart = ({
visible: false,
},
},
height: height,
height: initialDimensions.height,
layout: {
background: {color: theme['base-300']},
textColor: theme.accent,
@@ -213,7 +292,7 @@ const TradeChart = ({
secondsVisible: true,
timeVisible: true,
},
width: width,
width: initialDimensions.width,
})
prepareChart()
@@ -710,7 +789,15 @@ const TradeChart = ({
}
}
return <div ref={chartRef}/>
return (
<div
ref={containerRef}
className="w-full h-full"
style={{ minHeight: height || 250 }}
>
<div ref={chartRef} className="w-full h-full" />
</div>
)
}
export default TradeChart

View File

@@ -9,7 +9,6 @@ import {
AccountClient,
BacktestClient,
BotClient,
BotType,
DataClient,
MoneyManagement,
MoneyManagementClient,
@@ -202,7 +201,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
}
setValue('timeframe', backtest.config.timeframe);
setValue('botType', backtest.config.botType);
setValue('cooldownPeriod', backtest.config.cooldownPeriod);
setValue('maxLossStreak', backtest.config.maxLossStreak);
setValue('maxPositionTimeHours', backtest.config.maxPositionTimeHours);
@@ -245,7 +243,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
}
setValue('timeframe', backtest.config.timeframe);
setValue('botType', backtest.config.botType);
setValue('cooldownPeriod', backtest.config.cooldownPeriod);
setValue('maxLossStreak', backtest.config.maxLossStreak);
setValue('maxPositionTimeHours', backtest.config.maxPositionTimeHours);
@@ -305,7 +302,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
}
setValue('scenarioName', config.scenarioName || '');
setValue('timeframe', config.timeframe);
setValue('botType', config.botType);
setValue('cooldownPeriod', config.cooldownPeriod);
setValue('maxLossStreak', config.maxLossStreak);
setValue('maxPositionTimeHours', config.maxPositionTimeHours);
@@ -443,13 +439,14 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
const onMoneyManagementChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
if (e.target.value === 'custom') {
setShowCustomMoneyManagement(true);
setSelectedMoneyManagement('custom'); // Set selected to 'custom'
setCustomMoneyManagement(undefined);
setGlobalCustomMoneyManagement(null); // Clear global store when creating new custom
} else {
setShowCustomMoneyManagement(false);
setSelectedMoneyManagement(e.target.value); // Update selected money management
setCustomMoneyManagement(undefined);
setGlobalCustomMoneyManagement(null); // Clear global store when switching away from custom
setSelectedMoneyManagement(e.target.value);
}
};
@@ -544,7 +541,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
}
console.log(form)
console.log(moneyManagement)
console.log(customMoneyManagement)
if (!moneyManagement) {
t.update('error', 'Money management is required');
return;
@@ -557,8 +554,8 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
scenario: customScenario ? convertScenarioToRequest(customScenario) : undefined,
scenarioName: customScenario ? undefined : form.scenarioName,
timeframe: form.timeframe,
botType: form.botType,
isForWatchingOnly: form.isForWatchingOnly || false,
flipPosition: false, // Default to false since we're only using isForWatchingOnly checkbox
cooldownPeriod: form.cooldownPeriod,
maxLossStreak: form.maxLossStreak,
maxPositionTimeHours: form.maxPositionTimeHours,
@@ -592,6 +589,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
closeModal();
} catch (error: any) {
console.error(error);
t.update('error', `Error: ${error.message || error}`);
}
};
@@ -607,7 +605,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
scenario: customScenario ? convertScenarioToRequest(customScenario) : undefined,
scenarioName: customScenario ? undefined : form.scenarioName,
timeframe: form.timeframe,
botType: form.botType,
isForWatchingOnly: false,
cooldownPeriod: form.cooldownPeriod,
maxLossStreak: form.maxLossStreak,
@@ -622,14 +619,13 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
useForDynamicStopLoss: form.useForDynamicStopLoss ?? true,
moneyManagementName: showCustomMoneyManagement ? undefined : selectedMoneyManagement,
moneyManagement: customMoneyManagement,
flipPosition: form.isForWatchingOnly ?? false,
};
const request: RunBacktestRequest = {
config: tradingBotConfigRequest,
startDate: new Date(form.startDate),
endDate: new Date(form.endDate),
balance: form.balance,
watchOnly: false,
save: form.save || false,
};
@@ -724,8 +720,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
</FormInput>
</div>
{/* Second Row: Money Management & Bot Type */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Money Management */}
<FormInput label="Money Management" htmlFor="moneyManagement">
<select
className="select select-bordered w-full"
@@ -749,21 +744,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
</select>
</FormInput>
<FormInput label="Bot Type" htmlFor="botType">
<select
className="select select-bordered w-full"
{...register('botType', { required: true })}
disabled={mode === 'updateBot'} // Can't change bot type for existing bots
>
{[BotType.ScalpingBot, BotType.FlippingBot].map((item) => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
</FormInput>
</div>
{/* Custom Money Management */}
{showCustomMoneyManagement && (
<div className="mt-6">

View File

@@ -504,12 +504,8 @@ export class BotClient extends AuthorizedApiBase {
return Promise.resolve<string>(null as any);
}
bot_Stop(botType: BotType | undefined, identifier: string | null | undefined): Promise<string> {
bot_Stop(identifier: string | null | undefined): Promise<string> {
let url_ = this.baseUrl + "/Bot/Stop?";
if (botType === null)
throw new Error("The parameter 'botType' cannot be null.");
else if (botType !== undefined)
url_ += "botType=" + encodeURIComponent("" + botType) + "&";
if (identifier !== undefined && identifier !== null)
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
url_ = url_.replace(/[?&]$/, "");
@@ -2868,7 +2864,6 @@ export interface TradingBotConfig {
timeframe: Timeframe;
isForWatchingOnly: boolean;
botTradingBalance: number;
botType: BotType;
isForBacktest: boolean;
cooldownPeriod: number;
maxLossStreak: number;
@@ -3014,12 +3009,6 @@ export enum Ticker {
Unknown = "Unknown",
}
export enum BotType {
SimpleBot = "SimpleBot",
ScalpingBot = "ScalpingBot",
FlippingBot = "FlippingBot",
}
export interface RiskManagement {
adverseProbabilityThreshold: number;
favorableProbabilityThreshold: number;
@@ -3336,8 +3325,6 @@ export interface RunBacktestRequest {
config?: TradingBotConfigRequest | null;
startDate?: Date;
endDate?: Date;
balance?: number;
watchOnly?: boolean;
save?: boolean;
}
@@ -3347,8 +3334,8 @@ export interface TradingBotConfigRequest {
timeframe: Timeframe;
isForWatchingOnly: boolean;
botTradingBalance: number;
botType: BotType;
name: string;
flipPosition: boolean;
cooldownPeriod: number;
maxLossStreak: number;
scenario?: ScenarioRequest | null;
@@ -3397,6 +3384,12 @@ export interface StartBotRequest {
config?: TradingBotConfigRequest | null;
}
export enum BotType {
SimpleBot = "SimpleBot",
ScalpingBot = "ScalpingBot",
FlippingBot = "FlippingBot",
}
export interface TradingBotResponse {
status: string;
signals: Signal[];

View File

@@ -111,7 +111,6 @@ export interface TradingBotConfig {
timeframe: Timeframe;
isForWatchingOnly: boolean;
botTradingBalance: number;
botType: BotType;
isForBacktest: boolean;
cooldownPeriod: number;
maxLossStreak: number;
@@ -257,12 +256,6 @@ export enum Ticker {
Unknown = "Unknown",
}
export enum BotType {
SimpleBot = "SimpleBot",
ScalpingBot = "ScalpingBot",
FlippingBot = "FlippingBot",
}
export interface RiskManagement {
adverseProbabilityThreshold: number;
favorableProbabilityThreshold: number;
@@ -579,8 +572,6 @@ export interface RunBacktestRequest {
config?: TradingBotConfigRequest | null;
startDate?: Date;
endDate?: Date;
balance?: number;
watchOnly?: boolean;
save?: boolean;
}
@@ -590,8 +581,8 @@ export interface TradingBotConfigRequest {
timeframe: Timeframe;
isForWatchingOnly: boolean;
botTradingBalance: number;
botType: BotType;
name: string;
flipPosition: boolean;
cooldownPeriod: number;
maxLossStreak: number;
scenario?: ScenarioRequest | null;
@@ -640,6 +631,12 @@ export interface StartBotRequest {
config?: TradingBotConfigRequest | null;
}
export enum BotType {
SimpleBot = "SimpleBot",
ScalpingBot = "ScalpingBot",
FlippingBot = "FlippingBot",
}
export interface TradingBotResponse {
status: string;
signals: Signal[];

View File

@@ -6,8 +6,7 @@ import {CardPosition, CardSignal, CardText, Toast,} from '../../components/molle
import ManualPositionModal from '../../components/mollecules/ManualPositionModal'
import TradesModal from '../../components/mollecules/TradesModal/TradesModal'
import {TradeChart, UnifiedTradingModal} from '../../components/organism'
import type {BotType, MoneyManagement, Position, TradingBotResponse} from '../../generated/ManagingApi'
import {BotClient} from '../../generated/ManagingApi'
import {BotClient, BotType, MoneyManagement, Position, TradingBotResponse} from '../../generated/ManagingApi'
import type {IBotList} from '../../global/type.tsx'
import MoneyManagementModal from '../settingsPage/moneymanagement/moneyManagementModal'
@@ -162,7 +161,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
if (status == 'Up') {
client
.bot_Stop(botType, identifier)
.bot_Stop(identifier)
.then(() => {
t.update('success', 'Bot stopped')
})
@@ -195,7 +194,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
}
function getUpdateBotBadge(bot: TradingBotResponse) {
const classes = baseBadgeClass() + ' bg-warning'
const classes = baseBadgeClass() + ' bg-orange-500'
return (
<button className={classes} onClick={() => openUpdateBotModal(bot)}>
<p className="text-primary-content flex">
@@ -246,14 +245,12 @@ const BotList: React.FC<IBotList> = ({ list }) => {
className="sm:w-1 md:w-1/2 xl:w-1/2 w-full p-2"
>
<div className={cardClasses(bot.status)}>
<figure>
<figure className="w-full">
{
<TradeChart
candles={bot.candles}
positions={bot.positions}
signals={bot.signals}
width={510}
height={300}
></TradeChart>
}
</figure>
@@ -274,7 +271,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
{/* Action Badges */}
<div className="flex flex-wrap gap-1">
{getToggleBotStatusBadge(bot.status, bot.identifier, bot.config.botType)}
{getToggleBotStatusBadge(bot.status, bot.identifier, bot.config.flipPosition ? BotType.FlippingBot : BotType.SimpleBot)}
{getUpdateBotBadge(bot)}
{getManualPositionBadge(bot.identifier)}
{getDeleteBadge(bot.identifier)}
@@ -294,7 +291,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
</div>
<div className="columns-2">
<CardSignal signals={bot.signals}></CardSignal>
<CardText title="Type" content={bot.config.botType}></CardText>
<CardText title="Type" content={bot.config.flipPosition ? 'Flipping' : 'Simple'}></CardText>
</div>
<div className="columns-2">
<CardPosition