Push merge conflict
This commit is contained in:
@@ -155,11 +155,10 @@ The `TradingBotConfig` class defines all configuration parameters for trading bo
|
|||||||
| `Timeframe` | `Timeframe` | Candle timeframe for analysis |
|
| `Timeframe` | `Timeframe` | Candle timeframe for analysis |
|
||||||
| `IsForWatchingOnly` | `bool` | If true, bot only sends signals without trading |
|
| `IsForWatchingOnly` | `bool` | If true, bot only sends signals without trading |
|
||||||
| `BotTradingBalance` | `decimal` | Initial trading balance for the bot |
|
| `BotTradingBalance` | `decimal` | Initial trading balance for the bot |
|
||||||
| `BotType` | `BotType` | Type of trading bot behavior |
|
|
||||||
| `IsForBacktest` | `bool` | Whether this config is for backtesting |
|
| `IsForBacktest` | `bool` | Whether this config is for backtesting |
|
||||||
| `CooldownPeriod` | `int` | Number of candles to wait before opening new position in same direction |
|
| `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) |
|
| `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 |
|
| `Name` | `string` | Unique identifier/name for the bot |
|
||||||
| `FlipOnlyWhenInProfit` | `bool` | Only flip positions when current position is profitable (default: true) |
|
| `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)
|
- 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
|
- `CloseEarlyWhenProfitable` allows immediate closure when profitable instead of waiting full duration
|
||||||
|
|
||||||
**Profit-Controlled Flipping:**
|
**Bot Behavior Control:**
|
||||||
- `FlipOnlyWhenInProfit` ensures safer trading by only flipping profitable positions
|
- `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
|
- Helps prevent cascading losses in volatile markets
|
||||||
|
|
||||||
**Synth API Integration:**
|
**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 |
|
| CloseEarlyWhenProfitable | Close positions early when profitable (requires MaxPositionTimeHours) | false |
|
||||||
| BotTradingBalance | Initial trading balance for the bot | Required |
|
| 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 |
|
#### Watch-Only Mode
|
||||||
|-------------|----------------------------------------------------------------------------------------|
|
- **`IsForWatchingOnly`**: When `true`, the bot analyzes market conditions and generates signals but does not execute actual trades
|
||||||
| SimpleBot | Basic bot implementation for simple trading strategies |
|
- Perfect for strategy testing, signal validation, or paper trading
|
||||||
| ScalpingBot | Opens positions and waits for cooldown period before opening new ones in same direction |
|
- Useful for developing confidence in a strategy before committing real capital
|
||||||
| FlippingBot | Advanced bot that can flip positions when opposite signals are triggered |
|
|
||||||
|
|
||||||
#### Flipping Mode Configuration
|
#### Position Flipping
|
||||||
|
- **`FlipPosition`**: When `true`, enables the bot to flip positions when opposite signals are triggered
|
||||||
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)
|
|
||||||
- **`FlipOnlyWhenInProfit`**: Safety feature that only allows flipping when current position is profitable (default: true)
|
- **`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
|
1. Opens initial position based on scenario signals
|
||||||
2. Monitors for opposite direction signals from the same scenario
|
2. Monitors for opposite direction signals from the same scenario
|
||||||
3. When opposite signal occurs:
|
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
|
4. Closes current position and immediately opens new position in opposite direction
|
||||||
5. Continues this cycle for the duration of the bot's operation
|
5. Continues this cycle for the duration of the bot's operation
|
||||||
|
|
||||||
**ScalpingBot vs FlippingBot:**
|
**When Flipping is Disabled (`FlipPosition = false`):**
|
||||||
- **ScalpingBot**: Opens position → Waits for exit signal → Closes → Cooldown → Opens new position
|
- Opens position → Waits for exit signal → Closes → Cooldown → Opens new position in same direction
|
||||||
- **FlippingBot**: Opens position → Monitors for opposite signals → Flips immediately (no cooldown between flips)
|
- 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`)
|
## Backtesting (`BacktestController`)
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Managing.Domain.Strategies;
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using static Managing.Common.Enums;
|
|
||||||
|
|
||||||
namespace Managing.Api.Controllers;
|
namespace Managing.Api.Controllers;
|
||||||
|
|
||||||
@@ -192,15 +191,14 @@ public class BacktestController : BaseController
|
|||||||
ScenarioName = request.Config.ScenarioName,
|
ScenarioName = request.Config.ScenarioName,
|
||||||
Scenario = scenario, // Use the converted scenario object
|
Scenario = scenario, // Use the converted scenario object
|
||||||
Timeframe = request.Config.Timeframe,
|
Timeframe = request.Config.Timeframe,
|
||||||
IsForWatchingOnly = request.WatchOnly,
|
IsForWatchingOnly = request.Config.IsForWatchingOnly,
|
||||||
BotTradingBalance = request.Balance,
|
BotTradingBalance = request.Config.BotTradingBalance,
|
||||||
BotType = request.Config.BotType,
|
|
||||||
IsForBacktest = true,
|
IsForBacktest = true,
|
||||||
CooldownPeriod = request.Config.CooldownPeriod,
|
CooldownPeriod = request.Config.CooldownPeriod,
|
||||||
MaxLossStreak = request.Config.MaxLossStreak,
|
MaxLossStreak = request.Config.MaxLossStreak,
|
||||||
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
|
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
|
||||||
FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
|
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 ??
|
Name = request.Config.Name ??
|
||||||
$"Backtest-{request.Config.ScenarioName ?? request.Config.Scenario?.Name ?? "Custom"}-{DateTime.UtcNow:yyyyMMdd-HHmmss}",
|
$"Backtest-{request.Config.ScenarioName ?? request.Config.Scenario?.Name ?? "Custom"}-{DateTime.UtcNow:yyyyMMdd-HHmmss}",
|
||||||
CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable,
|
CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable,
|
||||||
@@ -210,21 +208,12 @@ public class BacktestController : BaseController
|
|||||||
UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss
|
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(
|
backtestResult = await _backtester.RunTradingBotBacktest(
|
||||||
backtestConfig,
|
backtestConfig,
|
||||||
request.StartDate,
|
request.StartDate,
|
||||||
request.EndDate,
|
request.EndDate,
|
||||||
user,
|
user,
|
||||||
request.Save);
|
request.Save);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
await NotifyBacktesingSubscriberAsync(backtestResult);
|
await NotifyBacktesingSubscriberAsync(backtestResult);
|
||||||
|
|
||||||
@@ -281,16 +270,6 @@ public class RunBacktestRequest
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime EndDate { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Whether to save the backtest results
|
/// Whether to save the backtest results
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -224,7 +224,6 @@ public class BotController : BaseController
|
|||||||
Timeframe = request.Config.Timeframe,
|
Timeframe = request.Config.Timeframe,
|
||||||
IsForWatchingOnly = request.Config.IsForWatchingOnly,
|
IsForWatchingOnly = request.Config.IsForWatchingOnly,
|
||||||
BotTradingBalance = request.Config.BotTradingBalance,
|
BotTradingBalance = request.Config.BotTradingBalance,
|
||||||
BotType = request.Config.BotType,
|
|
||||||
CooldownPeriod = request.Config.CooldownPeriod,
|
CooldownPeriod = request.Config.CooldownPeriod,
|
||||||
MaxLossStreak = request.Config.MaxLossStreak,
|
MaxLossStreak = request.Config.MaxLossStreak,
|
||||||
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
|
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
|
||||||
@@ -236,7 +235,7 @@ public class BotController : BaseController
|
|||||||
UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss,
|
UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss,
|
||||||
// Set computed/default properties
|
// Set computed/default properties
|
||||||
IsForBacktest = false,
|
IsForBacktest = false,
|
||||||
FlipPosition = request.Config.BotType == BotType.FlippingBot,
|
FlipPosition = request.Config.FlipPosition,
|
||||||
Name = request.Config.Name
|
Name = request.Config.Name
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -256,12 +255,11 @@ public class BotController : BaseController
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stops a bot specified by type and name.
|
/// Stops a bot specified by type and name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="botType">The type of the bot to stop.</param>
|
|
||||||
/// <param name="identifier">The identifier 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>
|
/// <returns>A string indicating the result of the stop operation.</returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("Stop")]
|
[Route("Stop")]
|
||||||
public async Task<ActionResult<string>> Stop(BotType botType, string identifier)
|
public async Task<ActionResult<string>> Stop(string identifier)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -271,8 +269,8 @@ public class BotController : BaseController
|
|||||||
return Forbid("You don't have permission to stop this bot");
|
return Forbid("You don't have permission to stop this bot");
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _mediator.Send(new StopBotCommand(botType, identifier));
|
var result = await _mediator.Send(new StopBotCommand(identifier));
|
||||||
_logger.LogInformation($"{botType} type with identifier {identifier} is now {result}");
|
_logger.LogInformation($"Bot identifier {identifier} is now {result}");
|
||||||
|
|
||||||
await NotifyBotSubscriberAsync();
|
await NotifyBotSubscriberAsync();
|
||||||
|
|
||||||
@@ -343,7 +341,7 @@ public class BotController : BaseController
|
|||||||
|
|
||||||
foreach (var bot in userBots)
|
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",
|
await _hubContext.Clients.All.SendAsync("SendNotification",
|
||||||
$"Bot {bot.Identifier} paused by {user.Name}.", "Info");
|
$"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
|
// 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
|
// 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
|
// Get the saved bot backup
|
||||||
var backup = _botService.GetBotBackup(bot.Identifier);
|
var backup = _botService.GetBotBackup(bot.Identifier);
|
||||||
@@ -776,7 +775,6 @@ public class BotController : BaseController
|
|||||||
Timeframe = request.Config.Timeframe,
|
Timeframe = request.Config.Timeframe,
|
||||||
IsForWatchingOnly = request.Config.IsForWatchingOnly,
|
IsForWatchingOnly = request.Config.IsForWatchingOnly,
|
||||||
BotTradingBalance = request.Config.BotTradingBalance,
|
BotTradingBalance = request.Config.BotTradingBalance,
|
||||||
BotType = request.Config.BotType,
|
|
||||||
CooldownPeriod = request.Config.CooldownPeriod,
|
CooldownPeriod = request.Config.CooldownPeriod,
|
||||||
MaxLossStreak = request.Config.MaxLossStreak,
|
MaxLossStreak = request.Config.MaxLossStreak,
|
||||||
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
|
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
|
||||||
@@ -788,7 +786,7 @@ public class BotController : BaseController
|
|||||||
UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss,
|
UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss,
|
||||||
// Set computed/default properties
|
// Set computed/default properties
|
||||||
IsForBacktest = false,
|
IsForBacktest = false,
|
||||||
FlipPosition = request.Config.BotType == BotType.FlippingBot,
|
FlipPosition = request.Config.FlipPosition,
|
||||||
Name = request.Config.Name
|
Name = request.Config.Name
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,18 +38,14 @@ public class TradingBotConfigRequest
|
|||||||
[Required]
|
[Required]
|
||||||
public decimal BotTradingBalance { get; set; }
|
public decimal BotTradingBalance { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of bot (SimpleBot, ScalpingBot, FlippingBot)
|
|
||||||
/// </summary>
|
|
||||||
[Required]
|
|
||||||
public BotType BotType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name/identifier for this bot
|
/// The name/identifier for this bot
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[Required] public bool FlipPosition { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cooldown period between trades (in candles)
|
/// Cooldown period between trades (in candles)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -272,7 +272,6 @@ public class StatisticService : IStatisticService
|
|||||||
Timeframe = timeframe,
|
Timeframe = timeframe,
|
||||||
IsForWatchingOnly = true,
|
IsForWatchingOnly = true,
|
||||||
BotTradingBalance = 1000,
|
BotTradingBalance = 1000,
|
||||||
BotType = BotType.ScalpingBot,
|
|
||||||
IsForBacktest = true,
|
IsForBacktest = true,
|
||||||
CooldownPeriod = 1,
|
CooldownPeriod = 1,
|
||||||
MaxLossStreak = 0,
|
MaxLossStreak = 0,
|
||||||
|
|||||||
@@ -20,11 +20,5 @@ namespace Managing.Application.Abstractions
|
|||||||
/// <param name="config">The trading bot configuration</param>
|
/// <param name="config">The trading bot configuration</param>
|
||||||
/// <returns>ITradingBot instance configured for backtesting</returns>
|
/// <returns>ITradingBot instance configured for backtesting</returns>
|
||||||
ITradingBot CreateBacktestTradingBot(TradingBotConfig config);
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ namespace Managing.Application.Backtesting
|
|||||||
User user = null)
|
User user = null)
|
||||||
{
|
{
|
||||||
// Set FlipPosition based on BotType
|
// Set FlipPosition based on BotType
|
||||||
config.FlipPosition = config.BotType == BotType.FlippingBot;
|
config.FlipPosition = config.FlipPosition;
|
||||||
|
|
||||||
var tradingBot = _botFactory.CreateBacktestTradingBot(config);
|
var tradingBot = _botFactory.CreateBacktestTradingBot(config);
|
||||||
|
|
||||||
@@ -164,11 +164,11 @@ namespace Managing.Application.Backtesting
|
|||||||
private List<Candle> GetCandles(Account account, Ticker ticker, Timeframe timeframe,
|
private List<Candle> GetCandles(Account account, Ticker ticker, Timeframe timeframe,
|
||||||
DateTime startDate, DateTime endDate)
|
DateTime startDate, DateTime endDate)
|
||||||
{
|
{
|
||||||
var candles = _exchangeService.GetCandlesInflux(account.Exchange, ticker,
|
var candles = _exchangeService.GetCandlesInflux(TradingExchanges.Evm, ticker,
|
||||||
startDate, timeframe, endDate).Result;
|
startDate, timeframe, endDate).Result;
|
||||||
|
|
||||||
if (candles == null || candles.Count == 0)
|
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;
|
return candles;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Managing.Application.Abstractions.Services;
|
|||||||
using Managing.Domain.Bots;
|
using Managing.Domain.Bots;
|
||||||
using Managing.Domain.Workflows;
|
using Managing.Domain.Workflows;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using static Managing.Common.Enums;
|
|
||||||
|
|
||||||
namespace Managing.Application.Bots.Base
|
namespace Managing.Application.Bots.Base
|
||||||
{
|
{
|
||||||
@@ -61,64 +60,5 @@ namespace Managing.Application.Bots.Base
|
|||||||
_botService,
|
_botService,
|
||||||
config);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,7 +187,7 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
|
|
||||||
Logger.LogInformation($"____________________{Name}____________________");
|
Logger.LogInformation($"____________________{Name}____________________");
|
||||||
Logger.LogInformation(
|
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();
|
var previousLastCandle = OptimizedCandles.LastOrDefault();
|
||||||
@@ -1450,7 +1450,6 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Protect critical properties that shouldn't change for running bots
|
// Protect critical properties that shouldn't change for running bots
|
||||||
var protectedBotType = Config.BotType;
|
|
||||||
var protectedIsForBacktest = Config.IsForBacktest;
|
var protectedIsForBacktest = Config.IsForBacktest;
|
||||||
var protectedName = allowNameChange ? newConfig.Name : Config.Name;
|
var protectedName = allowNameChange ? newConfig.Name : Config.Name;
|
||||||
|
|
||||||
@@ -1470,7 +1469,6 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
Config = newConfig;
|
Config = newConfig;
|
||||||
|
|
||||||
// Restore protected properties
|
// Restore protected properties
|
||||||
Config.BotType = protectedBotType;
|
|
||||||
Config.IsForBacktest = protectedIsForBacktest;
|
Config.IsForBacktest = protectedIsForBacktest;
|
||||||
Config.Name = protectedName;
|
Config.Name = protectedName;
|
||||||
|
|
||||||
@@ -1532,7 +1530,6 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
Timeframe = Config.Timeframe,
|
Timeframe = Config.Timeframe,
|
||||||
IsForWatchingOnly = Config.IsForWatchingOnly,
|
IsForWatchingOnly = Config.IsForWatchingOnly,
|
||||||
BotTradingBalance = Config.BotTradingBalance,
|
BotTradingBalance = Config.BotTradingBalance,
|
||||||
BotType = Config.BotType,
|
|
||||||
IsForBacktest = Config.IsForBacktest,
|
IsForBacktest = Config.IsForBacktest,
|
||||||
CooldownPeriod = Config.CooldownPeriod,
|
CooldownPeriod = Config.CooldownPeriod,
|
||||||
MaxLossStreak = Config.MaxLossStreak,
|
MaxLossStreak = Config.MaxLossStreak,
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using static Managing.Common.Enums;
|
|
||||||
|
|
||||||
namespace Managing.Application.ManageBot.Commands
|
namespace Managing.Application.ManageBot.Commands
|
||||||
{
|
{
|
||||||
public class StopBotCommand : IRequest<string>
|
public class StopBotCommand : IRequest<string>
|
||||||
{
|
{
|
||||||
public string Name { get; }
|
public string Identifier { get; }
|
||||||
public BotType BotType { get; }
|
|
||||||
|
|
||||||
public StopBotCommand(BotType botType, string name)
|
public StopBotCommand(string identifier)
|
||||||
{
|
{
|
||||||
BotType = botType;
|
Identifier = identifier;
|
||||||
Name = name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,37 +70,25 @@ namespace Managing.Application.ManageBot
|
|||||||
Timeframe = request.Config.Timeframe,
|
Timeframe = request.Config.Timeframe,
|
||||||
IsForWatchingOnly = request.Config.IsForWatchingOnly,
|
IsForWatchingOnly = request.Config.IsForWatchingOnly,
|
||||||
BotTradingBalance = request.Config.BotTradingBalance,
|
BotTradingBalance = request.Config.BotTradingBalance,
|
||||||
BotType = request.Config.BotType,
|
|
||||||
IsForBacktest = request.Config.IsForBacktest,
|
IsForBacktest = request.Config.IsForBacktest,
|
||||||
CooldownPeriod =
|
CooldownPeriod =
|
||||||
request.Config.CooldownPeriod > 0 ? request.Config.CooldownPeriod : 1, // Default to 1 if not set
|
request.Config.CooldownPeriod > 0 ? request.Config.CooldownPeriod : 1, // Default to 1 if not set
|
||||||
MaxLossStreak = request.Config.MaxLossStreak,
|
MaxLossStreak = request.Config.MaxLossStreak,
|
||||||
MaxPositionTimeHours = request.Config.MaxPositionTimeHours, // Properly handle nullable value
|
MaxPositionTimeHours = request.Config.MaxPositionTimeHours, // Properly handle nullable value
|
||||||
FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
|
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,
|
Name = request.Config.Name ?? request.Name,
|
||||||
CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable
|
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);
|
var tradingBot = _botFactory.CreateTradingBot(configToUse);
|
||||||
tradingBot.User = request.User;
|
tradingBot.User = request.User;
|
||||||
|
|
||||||
// Log the configuration being used
|
// Log the configuration being used
|
||||||
await LogBotConfigurationAsync(tradingBot, $"{configToUse.BotType} created");
|
await LogBotConfigurationAsync(tradingBot, $"{configToUse.Name} created");
|
||||||
|
|
||||||
_botService.AddTradingBotToCache(tradingBot);
|
_botService.AddTradingBotToCache(tradingBot);
|
||||||
return tradingBot.GetStatus();
|
return tradingBot.GetStatus();
|
||||||
}
|
|
||||||
|
|
||||||
return botStatus.ToString();
|
return botStatus.ToString();
|
||||||
}
|
}
|
||||||
@@ -116,7 +104,6 @@ namespace Managing.Application.ManageBot
|
|||||||
{
|
{
|
||||||
var config = bot.GetConfiguration();
|
var config = bot.GetConfiguration();
|
||||||
var logMessage = $"{context} - Bot: {config.Name}, " +
|
var logMessage = $"{context} - Bot: {config.Name}, " +
|
||||||
$"Type: {config.BotType}, " +
|
|
||||||
$"Account: {config.AccountName}, " +
|
$"Account: {config.AccountName}, " +
|
||||||
$"Ticker: {config.Ticker}, " +
|
$"Ticker: {config.Ticker}, " +
|
||||||
$"Balance: {config.BotTradingBalance}, " +
|
$"Balance: {config.BotTradingBalance}, " +
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Managing.Application.ManageBot
|
|||||||
|
|
||||||
public Task<string> Handle(StopBotCommand request, CancellationToken cancellationToken)
|
public Task<string> Handle(StopBotCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return _botService.StopBot(request.Name);
|
return _botService.StopBot(request.Identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,6 @@ public class Backtest
|
|||||||
Timeframe = Config.Timeframe,
|
Timeframe = Config.Timeframe,
|
||||||
IsForWatchingOnly = false, // Always start as active bot
|
IsForWatchingOnly = false, // Always start as active bot
|
||||||
BotTradingBalance = initialTradingBalance,
|
BotTradingBalance = initialTradingBalance,
|
||||||
BotType = Config.BotType,
|
|
||||||
IsForBacktest = false, // Always false for live bots
|
IsForBacktest = false, // Always false for live bots
|
||||||
CooldownPeriod = Config.CooldownPeriod,
|
CooldownPeriod = Config.CooldownPeriod,
|
||||||
MaxLossStreak = Config.MaxLossStreak,
|
MaxLossStreak = Config.MaxLossStreak,
|
||||||
@@ -117,7 +116,6 @@ public class Backtest
|
|||||||
Timeframe = Config.Timeframe,
|
Timeframe = Config.Timeframe,
|
||||||
IsForWatchingOnly = Config.IsForWatchingOnly,
|
IsForWatchingOnly = Config.IsForWatchingOnly,
|
||||||
BotTradingBalance = balance,
|
BotTradingBalance = balance,
|
||||||
BotType = Config.BotType,
|
|
||||||
IsForBacktest = true,
|
IsForBacktest = true,
|
||||||
CooldownPeriod = Config.CooldownPeriod,
|
CooldownPeriod = Config.CooldownPeriod,
|
||||||
MaxLossStreak = Config.MaxLossStreak,
|
MaxLossStreak = Config.MaxLossStreak,
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ public class TradingBotConfig
|
|||||||
[Required] public Timeframe Timeframe { get; set; }
|
[Required] public Timeframe Timeframe { get; set; }
|
||||||
[Required] public bool IsForWatchingOnly { get; set; }
|
[Required] public bool IsForWatchingOnly { get; set; }
|
||||||
[Required] public decimal BotTradingBalance { get; set; }
|
[Required] public decimal BotTradingBalance { get; set; }
|
||||||
[Required] public BotType BotType { get; set; }
|
|
||||||
[Required] public bool IsForBacktest { get; set; }
|
[Required] public bool IsForBacktest { get; set; }
|
||||||
[Required] public int CooldownPeriod { get; set; }
|
[Required] public int CooldownPeriod { get; set; }
|
||||||
[Required] public int MaxLossStreak { get; set; }
|
[Required] public int MaxLossStreak { get; set; }
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Exilion.TradingAtomics;
|
using Exilion.TradingAtomics;
|
||||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||||
using static Managing.Common.Enums;
|
|
||||||
|
|
||||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||||
{
|
{
|
||||||
@@ -12,6 +11,7 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
|||||||
public int WinRate { get; set; }
|
public int WinRate { get; set; }
|
||||||
public decimal GrowthPercentage { get; set; }
|
public decimal GrowthPercentage { get; set; }
|
||||||
public decimal HodlPercentage { get; set; }
|
public decimal HodlPercentage { get; set; }
|
||||||
|
public TradingBotConfigDto Config { get; set; }
|
||||||
public List<PositionDto> Positions { get; set; }
|
public List<PositionDto> Positions { get; set; }
|
||||||
public List<SignalDto> Signals { get; set; }
|
public List<SignalDto> Signals { get; set; }
|
||||||
public List<CandleDto> Candles { get; set; }
|
public List<CandleDto> Candles { get; set; }
|
||||||
@@ -22,17 +22,5 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
|||||||
public UserDto User { get; set; }
|
public UserDto User { get; set; }
|
||||||
public PerformanceMetrics Statistics { get; set; }
|
public PerformanceMetrics Statistics { get; set; }
|
||||||
public double Score { 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; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ using Managing.Domain.Backtests;
|
|||||||
using Managing.Domain.Bots;
|
using Managing.Domain.Bots;
|
||||||
using Managing.Domain.Candles;
|
using Managing.Domain.Candles;
|
||||||
using Managing.Domain.MoneyManagements;
|
using Managing.Domain.MoneyManagements;
|
||||||
|
using Managing.Domain.Risk;
|
||||||
using Managing.Domain.Scenarios;
|
using Managing.Domain.Scenarios;
|
||||||
using Managing.Domain.Statistics;
|
using Managing.Domain.Statistics;
|
||||||
using Managing.Domain.Strategies;
|
using Managing.Domain.Strategies;
|
||||||
@@ -132,20 +133,7 @@ public static class MongoMappers
|
|||||||
if (b == null)
|
if (b == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var config = new TradingBotConfig
|
var config = Map(b.Config);
|
||||||
{
|
|
||||||
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 bTest = new Backtest(
|
var bTest = new Backtest(
|
||||||
config,
|
config,
|
||||||
@@ -181,6 +169,7 @@ public static class MongoMappers
|
|||||||
WinRate = result.WinRate,
|
WinRate = result.WinRate,
|
||||||
GrowthPercentage = result.GrowthPercentage,
|
GrowthPercentage = result.GrowthPercentage,
|
||||||
HodlPercentage = result.HodlPercentage,
|
HodlPercentage = result.HodlPercentage,
|
||||||
|
Config = Map(result.Config),
|
||||||
Positions = Map(result.Positions),
|
Positions = Map(result.Positions),
|
||||||
Signals = result.Signals.Select(s => Map(s)).ToList(),
|
Signals = result.Signals.Select(s => Map(s)).ToList(),
|
||||||
Candles = result.Candles.Select(c => Map(c)).ToList(),
|
Candles = result.Candles.Select(c => Map(c)).ToList(),
|
||||||
@@ -191,16 +180,6 @@ public static class MongoMappers
|
|||||||
StartDate = result.StartDate,
|
StartDate = result.StartDate,
|
||||||
EndDate = result.EndDate,
|
EndDate = result.EndDate,
|
||||||
Score = result.Score,
|
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
|
#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
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ const Modal: React.FC<IModalProps> = ({
|
|||||||
titleHeader={titleHeader}
|
titleHeader={titleHeader}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
|
showModal={showModal}
|
||||||
/>
|
/>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ import Logo from '../../../assets/img/logo.png'
|
|||||||
import {Loader} from '../../atoms'
|
import {Loader} from '../../atoms'
|
||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{ href: '/desk', name: 'Desk' },
|
|
||||||
{ href: '/bots', name: 'Bots' },
|
{ href: '/bots', name: 'Bots' },
|
||||||
{ href: '/workflow', name: 'Workflows' },
|
|
||||||
{ href: '/scenarios', name: 'Scenarios' },
|
{ href: '/scenarios', name: 'Scenarios' },
|
||||||
{ href: '/backtest', name: 'Backtest' },
|
{ href: '/backtest', name: 'Backtest' },
|
||||||
{ href: '/tools', name: 'Tools' },
|
{ href: '/tools', name: 'Tools' },
|
||||||
|
|||||||
@@ -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
|
* Avalible Props
|
||||||
@@ -19,7 +19,7 @@ const Tabs: FC<ITabsProps> = ({
|
|||||||
addButton = false,
|
addButton = false,
|
||||||
onAddButton,
|
onAddButton,
|
||||||
}) => {
|
}) => {
|
||||||
const Panel = tabs && tabs.find((tab) => tab.index === selectedTab)
|
const Panel = tabs && tabs.find((tab: any) => tab.index === selectedTab)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -28,7 +28,7 @@ const Tabs: FC<ITabsProps> = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="tabs" role="tablist" aria-orientation={orientation}>
|
<div className="tabs" role="tablist" aria-orientation={orientation}>
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab: any) => (
|
||||||
<button
|
<button
|
||||||
className={
|
className={
|
||||||
'mb-5 tab tab-bordered ' +
|
'mb-5 tab tab-bordered ' +
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
|
|||||||
ticker: backtest.config.ticker,
|
ticker: backtest.config.ticker,
|
||||||
scenarioName: backtest.config.scenarioName,
|
scenarioName: backtest.config.scenarioName,
|
||||||
timeframe: backtest.config.timeframe,
|
timeframe: backtest.config.timeframe,
|
||||||
botType: backtest.config.botType,
|
|
||||||
isForWatchingOnly: isForWatchOnly,
|
isForWatchingOnly: isForWatchOnly,
|
||||||
isForBacktest: false, // This is for running a live bot
|
isForBacktest: false, // This is for running a live bot
|
||||||
cooldownPeriod: backtest.config.cooldownPeriod,
|
cooldownPeriod: backtest.config.cooldownPeriod,
|
||||||
@@ -167,8 +166,6 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
|
|||||||
config: optimizedConfig as unknown as TradingBotConfigRequest,
|
config: optimizedConfig as unknown as TradingBotConfigRequest,
|
||||||
startDate: startDate,
|
startDate: startDate,
|
||||||
endDate: endDate,
|
endDate: endDate,
|
||||||
balance: backtest.walletBalances[0].value,
|
|
||||||
watchOnly: false,
|
|
||||||
save: false,
|
save: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +282,7 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
|
|||||||
></CardText>
|
></CardText>
|
||||||
<CardText
|
<CardText
|
||||||
title="Scenario"
|
title="Scenario"
|
||||||
content={backtest.config.scenarioName}
|
content={backtest.config.scenarioName ?? backtest.config.scenario?.name}
|
||||||
></CardText>
|
></CardText>
|
||||||
<CardText
|
<CardText
|
||||||
title="Timeframe"
|
title="Timeframe"
|
||||||
|
|||||||
@@ -351,16 +351,15 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
|
|||||||
content={getAverageTradesPerDay() + " trades/day"}
|
content={getAverageTradesPerDay() + " trades/day"}
|
||||||
></CardText>
|
></CardText>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="w-full">
|
||||||
<figure>
|
<figure className="w-full">
|
||||||
<TradeChart
|
<TradeChart
|
||||||
width={1400}
|
|
||||||
height={1100}
|
|
||||||
candles={candles}
|
candles={candles}
|
||||||
positions={positions}
|
positions={positions}
|
||||||
walletBalances={walletBalances}
|
walletBalances={walletBalances}
|
||||||
indicatorsValues={indicatorsValues}
|
indicatorsValues={indicatorsValues}
|
||||||
signals={signals}
|
signals={signals}
|
||||||
|
height={1000}
|
||||||
></TradeChart>
|
></TradeChart>
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import React, {useEffect, useState} from 'react'
|
|||||||
import useApiUrlStore from '../../../app/store/apiStore'
|
import useApiUrlStore from '../../../app/store/apiStore'
|
||||||
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'
|
import type {IBacktestCards} from '../../../global/type.tsx'
|
||||||
import {CardText, 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'
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ type ITradeChartProps = {
|
|||||||
walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null
|
walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null
|
||||||
indicatorsValues?: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; } | null;
|
indicatorsValues?: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; } | null;
|
||||||
stream?: Candle | null
|
stream?: Candle | null
|
||||||
width: number
|
width?: number
|
||||||
height: number
|
height?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const TradeChart = ({
|
const TradeChart = ({
|
||||||
@@ -62,12 +62,88 @@ const TradeChart = ({
|
|||||||
height,
|
height,
|
||||||
}: ITradeChartProps) => {
|
}: ITradeChartProps) => {
|
||||||
const chartRef = React.useRef<HTMLDivElement>(null)
|
const chartRef = React.useRef<HTMLDivElement>(null)
|
||||||
|
const containerRef = React.useRef<HTMLDivElement>(null)
|
||||||
const chart = useRef<IChartApi>()
|
const chart = useRef<IChartApi>()
|
||||||
const {themeProperty} = useTheme()
|
const {themeProperty} = useTheme()
|
||||||
const theme = themeProperty()
|
const theme = themeProperty()
|
||||||
const series1 = useRef<ISeriesApi<'Candlestick'>>()
|
const series1 = useRef<ISeriesApi<'Candlestick'>>()
|
||||||
const [timeDiff, setTimeDiff] = useState<number>(0)
|
const [timeDiff, setTimeDiff] = useState<number>(0)
|
||||||
const [candleCount, setCandleCount] = useState<number>(candles.length)
|
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(
|
function buildLine(
|
||||||
color: string,
|
color: string,
|
||||||
@@ -164,7 +240,10 @@ const TradeChart = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (chartRef.current) {
|
if (chartRef.current && containerRef.current) {
|
||||||
|
const initialDimensions = getResponsiveDimensions()
|
||||||
|
setChartDimensions(initialDimensions)
|
||||||
|
|
||||||
const lineColor = theme['base-100']
|
const lineColor = theme['base-100']
|
||||||
chart.current = createChart(chartRef.current, {
|
chart.current = createChart(chartRef.current, {
|
||||||
crosshair: {
|
crosshair: {
|
||||||
@@ -194,7 +273,7 @@ const TradeChart = ({
|
|||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
height: height,
|
height: initialDimensions.height,
|
||||||
layout: {
|
layout: {
|
||||||
background: {color: theme['base-300']},
|
background: {color: theme['base-300']},
|
||||||
textColor: theme.accent,
|
textColor: theme.accent,
|
||||||
@@ -213,7 +292,7 @@ const TradeChart = ({
|
|||||||
secondsVisible: true,
|
secondsVisible: true,
|
||||||
timeVisible: true,
|
timeVisible: true,
|
||||||
},
|
},
|
||||||
width: width,
|
width: initialDimensions.width,
|
||||||
})
|
})
|
||||||
|
|
||||||
prepareChart()
|
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
|
export default TradeChart
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
AccountClient,
|
AccountClient,
|
||||||
BacktestClient,
|
BacktestClient,
|
||||||
BotClient,
|
BotClient,
|
||||||
BotType,
|
|
||||||
DataClient,
|
DataClient,
|
||||||
MoneyManagement,
|
MoneyManagement,
|
||||||
MoneyManagementClient,
|
MoneyManagementClient,
|
||||||
@@ -202,7 +201,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setValue('timeframe', backtest.config.timeframe);
|
setValue('timeframe', backtest.config.timeframe);
|
||||||
setValue('botType', backtest.config.botType);
|
|
||||||
setValue('cooldownPeriod', backtest.config.cooldownPeriod);
|
setValue('cooldownPeriod', backtest.config.cooldownPeriod);
|
||||||
setValue('maxLossStreak', backtest.config.maxLossStreak);
|
setValue('maxLossStreak', backtest.config.maxLossStreak);
|
||||||
setValue('maxPositionTimeHours', backtest.config.maxPositionTimeHours);
|
setValue('maxPositionTimeHours', backtest.config.maxPositionTimeHours);
|
||||||
@@ -245,7 +243,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setValue('timeframe', backtest.config.timeframe);
|
setValue('timeframe', backtest.config.timeframe);
|
||||||
setValue('botType', backtest.config.botType);
|
|
||||||
setValue('cooldownPeriod', backtest.config.cooldownPeriod);
|
setValue('cooldownPeriod', backtest.config.cooldownPeriod);
|
||||||
setValue('maxLossStreak', backtest.config.maxLossStreak);
|
setValue('maxLossStreak', backtest.config.maxLossStreak);
|
||||||
setValue('maxPositionTimeHours', backtest.config.maxPositionTimeHours);
|
setValue('maxPositionTimeHours', backtest.config.maxPositionTimeHours);
|
||||||
@@ -305,7 +302,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
}
|
}
|
||||||
setValue('scenarioName', config.scenarioName || '');
|
setValue('scenarioName', config.scenarioName || '');
|
||||||
setValue('timeframe', config.timeframe);
|
setValue('timeframe', config.timeframe);
|
||||||
setValue('botType', config.botType);
|
|
||||||
setValue('cooldownPeriod', config.cooldownPeriod);
|
setValue('cooldownPeriod', config.cooldownPeriod);
|
||||||
setValue('maxLossStreak', config.maxLossStreak);
|
setValue('maxLossStreak', config.maxLossStreak);
|
||||||
setValue('maxPositionTimeHours', config.maxPositionTimeHours);
|
setValue('maxPositionTimeHours', config.maxPositionTimeHours);
|
||||||
@@ -443,13 +439,14 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
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);
|
||||||
|
setSelectedMoneyManagement('custom'); // Set selected to 'custom'
|
||||||
setCustomMoneyManagement(undefined);
|
setCustomMoneyManagement(undefined);
|
||||||
setGlobalCustomMoneyManagement(null); // Clear global store when creating new custom
|
setGlobalCustomMoneyManagement(null); // Clear global store when creating new custom
|
||||||
} else {
|
} else {
|
||||||
setShowCustomMoneyManagement(false);
|
setShowCustomMoneyManagement(false);
|
||||||
|
setSelectedMoneyManagement(e.target.value); // Update selected money management
|
||||||
setCustomMoneyManagement(undefined);
|
setCustomMoneyManagement(undefined);
|
||||||
setGlobalCustomMoneyManagement(null); // Clear global store when switching away from custom
|
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(form)
|
||||||
console.log(moneyManagement)
|
console.log(customMoneyManagement)
|
||||||
if (!moneyManagement) {
|
if (!moneyManagement) {
|
||||||
t.update('error', 'Money management is required');
|
t.update('error', 'Money management is required');
|
||||||
return;
|
return;
|
||||||
@@ -557,8 +554,8 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
scenario: customScenario ? convertScenarioToRequest(customScenario) : undefined,
|
scenario: customScenario ? convertScenarioToRequest(customScenario) : undefined,
|
||||||
scenarioName: customScenario ? undefined : form.scenarioName,
|
scenarioName: customScenario ? undefined : form.scenarioName,
|
||||||
timeframe: form.timeframe,
|
timeframe: form.timeframe,
|
||||||
botType: form.botType,
|
|
||||||
isForWatchingOnly: form.isForWatchingOnly || false,
|
isForWatchingOnly: form.isForWatchingOnly || false,
|
||||||
|
flipPosition: false, // Default to false since we're only using isForWatchingOnly checkbox
|
||||||
cooldownPeriod: form.cooldownPeriod,
|
cooldownPeriod: form.cooldownPeriod,
|
||||||
maxLossStreak: form.maxLossStreak,
|
maxLossStreak: form.maxLossStreak,
|
||||||
maxPositionTimeHours: form.maxPositionTimeHours,
|
maxPositionTimeHours: form.maxPositionTimeHours,
|
||||||
@@ -592,6 +589,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
|
|
||||||
closeModal();
|
closeModal();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
console.error(error);
|
||||||
t.update('error', `Error: ${error.message || error}`);
|
t.update('error', `Error: ${error.message || error}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -607,7 +605,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
scenario: customScenario ? convertScenarioToRequest(customScenario) : undefined,
|
scenario: customScenario ? convertScenarioToRequest(customScenario) : undefined,
|
||||||
scenarioName: customScenario ? undefined : form.scenarioName,
|
scenarioName: customScenario ? undefined : form.scenarioName,
|
||||||
timeframe: form.timeframe,
|
timeframe: form.timeframe,
|
||||||
botType: form.botType,
|
|
||||||
isForWatchingOnly: false,
|
isForWatchingOnly: false,
|
||||||
cooldownPeriod: form.cooldownPeriod,
|
cooldownPeriod: form.cooldownPeriod,
|
||||||
maxLossStreak: form.maxLossStreak,
|
maxLossStreak: form.maxLossStreak,
|
||||||
@@ -622,14 +619,13 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
useForDynamicStopLoss: form.useForDynamicStopLoss ?? true,
|
useForDynamicStopLoss: form.useForDynamicStopLoss ?? true,
|
||||||
moneyManagementName: showCustomMoneyManagement ? undefined : selectedMoneyManagement,
|
moneyManagementName: showCustomMoneyManagement ? undefined : selectedMoneyManagement,
|
||||||
moneyManagement: customMoneyManagement,
|
moneyManagement: customMoneyManagement,
|
||||||
|
flipPosition: form.isForWatchingOnly ?? false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const request: RunBacktestRequest = {
|
const request: RunBacktestRequest = {
|
||||||
config: tradingBotConfigRequest,
|
config: tradingBotConfigRequest,
|
||||||
startDate: new Date(form.startDate),
|
startDate: new Date(form.startDate),
|
||||||
endDate: new Date(form.endDate),
|
endDate: new Date(form.endDate),
|
||||||
balance: form.balance,
|
|
||||||
watchOnly: false,
|
|
||||||
save: form.save || false,
|
save: form.save || false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -724,8 +720,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
</FormInput>
|
</FormInput>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Second Row: Money Management & Bot Type */}
|
{/* Money Management */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
<FormInput label="Money Management" htmlFor="moneyManagement">
|
<FormInput label="Money Management" htmlFor="moneyManagement">
|
||||||
<select
|
<select
|
||||||
className="select select-bordered w-full"
|
className="select select-bordered w-full"
|
||||||
@@ -749,21 +744,6 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
|
|||||||
</select>
|
</select>
|
||||||
</FormInput>
|
</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 */}
|
{/* Custom Money Management */}
|
||||||
{showCustomMoneyManagement && (
|
{showCustomMoneyManagement && (
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
|
|||||||
@@ -504,12 +504,8 @@ export class BotClient extends AuthorizedApiBase {
|
|||||||
return Promise.resolve<string>(null as any);
|
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?";
|
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)
|
if (identifier !== undefined && identifier !== null)
|
||||||
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
|
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
|
||||||
url_ = url_.replace(/[?&]$/, "");
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
@@ -2868,7 +2864,6 @@ export interface TradingBotConfig {
|
|||||||
timeframe: Timeframe;
|
timeframe: Timeframe;
|
||||||
isForWatchingOnly: boolean;
|
isForWatchingOnly: boolean;
|
||||||
botTradingBalance: number;
|
botTradingBalance: number;
|
||||||
botType: BotType;
|
|
||||||
isForBacktest: boolean;
|
isForBacktest: boolean;
|
||||||
cooldownPeriod: number;
|
cooldownPeriod: number;
|
||||||
maxLossStreak: number;
|
maxLossStreak: number;
|
||||||
@@ -3014,12 +3009,6 @@ export enum Ticker {
|
|||||||
Unknown = "Unknown",
|
Unknown = "Unknown",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BotType {
|
|
||||||
SimpleBot = "SimpleBot",
|
|
||||||
ScalpingBot = "ScalpingBot",
|
|
||||||
FlippingBot = "FlippingBot",
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RiskManagement {
|
export interface RiskManagement {
|
||||||
adverseProbabilityThreshold: number;
|
adverseProbabilityThreshold: number;
|
||||||
favorableProbabilityThreshold: number;
|
favorableProbabilityThreshold: number;
|
||||||
@@ -3336,8 +3325,6 @@ export interface RunBacktestRequest {
|
|||||||
config?: TradingBotConfigRequest | null;
|
config?: TradingBotConfigRequest | null;
|
||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
endDate?: Date;
|
endDate?: Date;
|
||||||
balance?: number;
|
|
||||||
watchOnly?: boolean;
|
|
||||||
save?: boolean;
|
save?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3347,8 +3334,8 @@ export interface TradingBotConfigRequest {
|
|||||||
timeframe: Timeframe;
|
timeframe: Timeframe;
|
||||||
isForWatchingOnly: boolean;
|
isForWatchingOnly: boolean;
|
||||||
botTradingBalance: number;
|
botTradingBalance: number;
|
||||||
botType: BotType;
|
|
||||||
name: string;
|
name: string;
|
||||||
|
flipPosition: boolean;
|
||||||
cooldownPeriod: number;
|
cooldownPeriod: number;
|
||||||
maxLossStreak: number;
|
maxLossStreak: number;
|
||||||
scenario?: ScenarioRequest | null;
|
scenario?: ScenarioRequest | null;
|
||||||
@@ -3397,6 +3384,12 @@ export interface StartBotRequest {
|
|||||||
config?: TradingBotConfigRequest | null;
|
config?: TradingBotConfigRequest | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum BotType {
|
||||||
|
SimpleBot = "SimpleBot",
|
||||||
|
ScalpingBot = "ScalpingBot",
|
||||||
|
FlippingBot = "FlippingBot",
|
||||||
|
}
|
||||||
|
|
||||||
export interface TradingBotResponse {
|
export interface TradingBotResponse {
|
||||||
status: string;
|
status: string;
|
||||||
signals: Signal[];
|
signals: Signal[];
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ export interface TradingBotConfig {
|
|||||||
timeframe: Timeframe;
|
timeframe: Timeframe;
|
||||||
isForWatchingOnly: boolean;
|
isForWatchingOnly: boolean;
|
||||||
botTradingBalance: number;
|
botTradingBalance: number;
|
||||||
botType: BotType;
|
|
||||||
isForBacktest: boolean;
|
isForBacktest: boolean;
|
||||||
cooldownPeriod: number;
|
cooldownPeriod: number;
|
||||||
maxLossStreak: number;
|
maxLossStreak: number;
|
||||||
@@ -257,12 +256,6 @@ export enum Ticker {
|
|||||||
Unknown = "Unknown",
|
Unknown = "Unknown",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BotType {
|
|
||||||
SimpleBot = "SimpleBot",
|
|
||||||
ScalpingBot = "ScalpingBot",
|
|
||||||
FlippingBot = "FlippingBot",
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RiskManagement {
|
export interface RiskManagement {
|
||||||
adverseProbabilityThreshold: number;
|
adverseProbabilityThreshold: number;
|
||||||
favorableProbabilityThreshold: number;
|
favorableProbabilityThreshold: number;
|
||||||
@@ -579,8 +572,6 @@ export interface RunBacktestRequest {
|
|||||||
config?: TradingBotConfigRequest | null;
|
config?: TradingBotConfigRequest | null;
|
||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
endDate?: Date;
|
endDate?: Date;
|
||||||
balance?: number;
|
|
||||||
watchOnly?: boolean;
|
|
||||||
save?: boolean;
|
save?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,8 +581,8 @@ export interface TradingBotConfigRequest {
|
|||||||
timeframe: Timeframe;
|
timeframe: Timeframe;
|
||||||
isForWatchingOnly: boolean;
|
isForWatchingOnly: boolean;
|
||||||
botTradingBalance: number;
|
botTradingBalance: number;
|
||||||
botType: BotType;
|
|
||||||
name: string;
|
name: string;
|
||||||
|
flipPosition: boolean;
|
||||||
cooldownPeriod: number;
|
cooldownPeriod: number;
|
||||||
maxLossStreak: number;
|
maxLossStreak: number;
|
||||||
scenario?: ScenarioRequest | null;
|
scenario?: ScenarioRequest | null;
|
||||||
@@ -640,6 +631,12 @@ export interface StartBotRequest {
|
|||||||
config?: TradingBotConfigRequest | null;
|
config?: TradingBotConfigRequest | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum BotType {
|
||||||
|
SimpleBot = "SimpleBot",
|
||||||
|
ScalpingBot = "ScalpingBot",
|
||||||
|
FlippingBot = "FlippingBot",
|
||||||
|
}
|
||||||
|
|
||||||
export interface TradingBotResponse {
|
export interface TradingBotResponse {
|
||||||
status: string;
|
status: string;
|
||||||
signals: Signal[];
|
signals: Signal[];
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import {CardPosition, CardSignal, CardText, Toast,} from '../../components/molle
|
|||||||
import ManualPositionModal from '../../components/mollecules/ManualPositionModal'
|
import ManualPositionModal from '../../components/mollecules/ManualPositionModal'
|
||||||
import TradesModal from '../../components/mollecules/TradesModal/TradesModal'
|
import TradesModal from '../../components/mollecules/TradesModal/TradesModal'
|
||||||
import {TradeChart, UnifiedTradingModal} from '../../components/organism'
|
import {TradeChart, UnifiedTradingModal} from '../../components/organism'
|
||||||
import type {BotType, MoneyManagement, Position, TradingBotResponse} from '../../generated/ManagingApi'
|
import {BotClient, BotType, MoneyManagement, Position, TradingBotResponse} from '../../generated/ManagingApi'
|
||||||
import {BotClient} from '../../generated/ManagingApi'
|
|
||||||
import type {IBotList} from '../../global/type.tsx'
|
import type {IBotList} from '../../global/type.tsx'
|
||||||
import MoneyManagementModal from '../settingsPage/moneymanagement/moneyManagementModal'
|
import MoneyManagementModal from '../settingsPage/moneymanagement/moneyManagementModal'
|
||||||
|
|
||||||
@@ -162,7 +161,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
|
|
||||||
if (status == 'Up') {
|
if (status == 'Up') {
|
||||||
client
|
client
|
||||||
.bot_Stop(botType, identifier)
|
.bot_Stop(identifier)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
t.update('success', 'Bot stopped')
|
t.update('success', 'Bot stopped')
|
||||||
})
|
})
|
||||||
@@ -195,7 +194,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getUpdateBotBadge(bot: TradingBotResponse) {
|
function getUpdateBotBadge(bot: TradingBotResponse) {
|
||||||
const classes = baseBadgeClass() + ' bg-warning'
|
const classes = baseBadgeClass() + ' bg-orange-500'
|
||||||
return (
|
return (
|
||||||
<button className={classes} onClick={() => openUpdateBotModal(bot)}>
|
<button className={classes} onClick={() => openUpdateBotModal(bot)}>
|
||||||
<p className="text-primary-content flex">
|
<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"
|
className="sm:w-1 md:w-1/2 xl:w-1/2 w-full p-2"
|
||||||
>
|
>
|
||||||
<div className={cardClasses(bot.status)}>
|
<div className={cardClasses(bot.status)}>
|
||||||
<figure>
|
<figure className="w-full">
|
||||||
{
|
{
|
||||||
<TradeChart
|
<TradeChart
|
||||||
candles={bot.candles}
|
candles={bot.candles}
|
||||||
positions={bot.positions}
|
positions={bot.positions}
|
||||||
signals={bot.signals}
|
signals={bot.signals}
|
||||||
width={510}
|
|
||||||
height={300}
|
|
||||||
></TradeChart>
|
></TradeChart>
|
||||||
}
|
}
|
||||||
</figure>
|
</figure>
|
||||||
@@ -274,7 +271,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
|
|
||||||
{/* Action Badges */}
|
{/* Action Badges */}
|
||||||
<div className="flex flex-wrap gap-1">
|
<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)}
|
{getUpdateBotBadge(bot)}
|
||||||
{getManualPositionBadge(bot.identifier)}
|
{getManualPositionBadge(bot.identifier)}
|
||||||
{getDeleteBadge(bot.identifier)}
|
{getDeleteBadge(bot.identifier)}
|
||||||
@@ -294,7 +291,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="columns-2">
|
<div className="columns-2">
|
||||||
<CardSignal signals={bot.signals}></CardSignal>
|
<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>
|
||||||
<div className="columns-2">
|
<div className="columns-2">
|
||||||
<CardPosition
|
<CardPosition
|
||||||
|
|||||||
Reference in New Issue
Block a user