Add new parameters
This commit is contained in:
@@ -40,5 +40,18 @@ namespace Managing.Application.Abstractions
|
||||
|
||||
Task CloseTrade(Signal signal, Position position, Trade tradeToClose, decimal lastPrice,
|
||||
bool tradeClosingPosition = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current trading bot configuration.
|
||||
/// </summary>
|
||||
/// <returns>A copy of the current configuration</returns>
|
||||
TradingBotConfig GetConfiguration();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the trading bot configuration with new settings.
|
||||
/// </summary>
|
||||
/// <param name="newConfig">The new configuration to apply</param>
|
||||
/// <returns>True if the configuration was successfully updated, false otherwise</returns>
|
||||
Task<bool> UpdateConfiguration(TradingBotConfig newConfig);
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,9 @@ namespace Managing.Application.Backtesting
|
||||
bool save = false,
|
||||
List<Candle>? initialCandles = null,
|
||||
int cooldownPeriod = 1,
|
||||
int maxLossStreak = 0)
|
||||
int maxLossStreak = 0,
|
||||
decimal? maxPositionTimeHours = null,
|
||||
bool flipOnlyWhenInProfit = true)
|
||||
{
|
||||
var config = new TradingBotConfig
|
||||
{
|
||||
@@ -81,7 +83,9 @@ namespace Managing.Application.Backtesting
|
||||
BotType = BotType.ScalpingBot,
|
||||
IsForBacktest = true,
|
||||
CooldownPeriod = cooldownPeriod,
|
||||
MaxLossStreak = maxLossStreak
|
||||
MaxLossStreak = maxLossStreak,
|
||||
MaxPositionTimeHours = maxPositionTimeHours,
|
||||
FlipOnlyWhenInProfit = flipOnlyWhenInProfit
|
||||
};
|
||||
|
||||
var scalpingBot = _botFactory.CreateBacktestScalpingBot(config);
|
||||
@@ -122,7 +126,9 @@ namespace Managing.Application.Backtesting
|
||||
bool save = false,
|
||||
List<Candle>? initialCandles = null,
|
||||
int cooldownPeriod = 1,
|
||||
int maxLossStreak = 0)
|
||||
int maxLossStreak = 0,
|
||||
decimal? maxPositionTimeHours = null,
|
||||
bool flipOnlyWhenInProfit = true)
|
||||
{
|
||||
var config = new TradingBotConfig
|
||||
{
|
||||
@@ -136,7 +142,9 @@ namespace Managing.Application.Backtesting
|
||||
BotType = BotType.FlippingBot,
|
||||
IsForBacktest = true,
|
||||
CooldownPeriod = cooldownPeriod,
|
||||
MaxLossStreak = maxLossStreak
|
||||
MaxLossStreak = maxLossStreak,
|
||||
MaxPositionTimeHours = maxPositionTimeHours,
|
||||
FlipOnlyWhenInProfit = flipOnlyWhenInProfit
|
||||
};
|
||||
|
||||
var flippingBot = _botFactory.CreateBacktestFlippingBot(config);
|
||||
@@ -173,7 +181,9 @@ namespace Managing.Application.Backtesting
|
||||
decimal balance,
|
||||
User user = null,
|
||||
int cooldownPeriod = 1,
|
||||
int maxLossStreak = 0)
|
||||
int maxLossStreak = 0,
|
||||
decimal? maxPositionTimeHours = null,
|
||||
bool flipOnlyWhenInProfit = true)
|
||||
{
|
||||
var ticker = MiscExtensions.ParseEnum<Ticker>(candles.FirstOrDefault().Ticker);
|
||||
var config = new TradingBotConfig
|
||||
@@ -188,7 +198,9 @@ namespace Managing.Application.Backtesting
|
||||
BotType = BotType.ScalpingBot,
|
||||
IsForBacktest = true,
|
||||
CooldownPeriod = cooldownPeriod,
|
||||
MaxLossStreak = maxLossStreak
|
||||
MaxLossStreak = maxLossStreak,
|
||||
MaxPositionTimeHours = maxPositionTimeHours,
|
||||
FlipOnlyWhenInProfit = flipOnlyWhenInProfit
|
||||
};
|
||||
|
||||
var bot = _botFactory.CreateBacktestScalpingBot(config);
|
||||
@@ -215,7 +227,9 @@ namespace Managing.Application.Backtesting
|
||||
decimal balance,
|
||||
User user = null,
|
||||
int cooldownPeriod = 1,
|
||||
int maxLossStreak = 0)
|
||||
int maxLossStreak = 0,
|
||||
decimal? maxPositionTimeHours = null,
|
||||
bool flipOnlyWhenInProfit = true)
|
||||
{
|
||||
var ticker = MiscExtensions.ParseEnum<Ticker>(candles.FirstOrDefault().Ticker);
|
||||
var config = new TradingBotConfig
|
||||
@@ -230,7 +244,9 @@ namespace Managing.Application.Backtesting
|
||||
BotType = BotType.FlippingBot,
|
||||
IsForBacktest = true,
|
||||
CooldownPeriod = cooldownPeriod,
|
||||
MaxLossStreak = maxLossStreak
|
||||
MaxLossStreak = maxLossStreak,
|
||||
MaxPositionTimeHours = maxPositionTimeHours,
|
||||
FlipOnlyWhenInProfit = flipOnlyWhenInProfit
|
||||
};
|
||||
|
||||
var bot = _botFactory.CreateBacktestFlippingBot(config);
|
||||
|
||||
@@ -43,6 +43,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
public decimal Fee { get; set; }
|
||||
public Scenario Scenario { get; set; }
|
||||
|
||||
|
||||
public TradingBot(
|
||||
IExchangeService exchangeService,
|
||||
ILogger<TradingBot> logger,
|
||||
@@ -293,11 +294,12 @@ public class TradingBot : Bot, ITradingBot
|
||||
{
|
||||
// Get the candle that corresponds to the position opening time
|
||||
var positionCandle = OptimizedCandles.FirstOrDefault(c => c.Date <= position.Open.Date)
|
||||
?? OptimizedCandles.LastOrDefault();
|
||||
|
||||
?? OptimizedCandles.LastOrDefault();
|
||||
|
||||
if (positionCandle == null)
|
||||
{
|
||||
await LogWarning($"Cannot find candle for position {position.Identifier} opened at {position.Open.Date}");
|
||||
await LogWarning(
|
||||
$"Cannot find candle for position {position.Identifier} opened at {position.Open.Date}");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -317,14 +319,15 @@ public class TradingBot : Bot, ITradingBot
|
||||
// to use the new signal identifier, or find another approach
|
||||
// For now, let's update the position's SignalIdentifier to match the recreated signal
|
||||
position.SignalIdentifier = recreatedSignal.Identifier;
|
||||
|
||||
|
||||
recreatedSignal.Status = SignalStatus.PositionOpen;
|
||||
recreatedSignal.User = Account.User;
|
||||
|
||||
// Add the recreated signal to our collection
|
||||
Signals.Add(recreatedSignal);
|
||||
|
||||
await LogInformation($"Successfully recreated signal {recreatedSignal.Identifier} for position {position.Identifier}");
|
||||
await LogInformation(
|
||||
$"Successfully recreated signal {recreatedSignal.Identifier} for position {position.Identifier}");
|
||||
return recreatedSignal;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -343,10 +346,10 @@ public class TradingBot : Bot, ITradingBot
|
||||
if (signalForPosition == null)
|
||||
{
|
||||
await LogInformation($"Signal not found for position {position.Identifier}. Recreating signal...");
|
||||
|
||||
|
||||
// Recreate the signal based on position information
|
||||
signalForPosition = await RecreateSignalFromPosition(position);
|
||||
|
||||
|
||||
if (signalForPosition == null)
|
||||
{
|
||||
await LogWarning($"Failed to recreate signal for position {position.Identifier}");
|
||||
@@ -357,7 +360,8 @@ public class TradingBot : Bot, ITradingBot
|
||||
// Ensure signal status is correctly set to PositionOpen if position is not finished
|
||||
if (signalForPosition.Status != SignalStatus.PositionOpen)
|
||||
{
|
||||
await LogInformation($"Updating signal {signalForPosition.Identifier} status from {signalForPosition.Status} to PositionOpen");
|
||||
await LogInformation(
|
||||
$"Updating signal {signalForPosition.Identifier} status from {signalForPosition.Status} to PositionOpen");
|
||||
SetSignalStatus(signalForPosition.Identifier, SignalStatus.PositionOpen);
|
||||
}
|
||||
|
||||
@@ -462,6 +466,33 @@ public class TradingBot : Bot, ITradingBot
|
||||
? OptimizedCandles.Last()
|
||||
: ExchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow);
|
||||
|
||||
var currentTime = Config.IsForBacktest ? lastCandle.Date : DateTime.UtcNow;
|
||||
|
||||
// Check if position has exceeded maximum time limit (only if MaxPositionTimeHours is set)
|
||||
if (Config.MaxPositionTimeHours.HasValue && HasPositionExceededTimeLimit(positionForSignal, currentTime))
|
||||
{
|
||||
// Check if position is in profit or at breakeven before closing
|
||||
var isPositionInProfit = await IsPositionInProfit(positionForSignal, lastCandle.Close);
|
||||
var isAtBreakeven = Math.Abs(lastCandle.Close - positionForSignal.Open.Price) < 0.01m; // Small tolerance for breakeven
|
||||
|
||||
if (isPositionInProfit || isAtBreakeven)
|
||||
{
|
||||
await LogInformation(
|
||||
$"Closing position due to time limit - Position opened at {positionForSignal.Open.Date}, " +
|
||||
$"current time {currentTime}, max time limit {Config.MaxPositionTimeHours} hours. " +
|
||||
$"Position is {(isPositionInProfit ? "in profit" : "at breakeven")} (entry: {positionForSignal.Open.Price}, current: {lastCandle.Close})");
|
||||
await CloseTrade(signal, positionForSignal, positionForSignal.Open, lastCandle.Close, true);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
await LogInformation(
|
||||
$"Position has exceeded time limit ({Config.MaxPositionTimeHours} hours) but is at a loss " +
|
||||
$"(entry: {positionForSignal.Open.Price}, current: {lastCandle.Close}). " +
|
||||
$"Waiting for profit or breakeven before closing.");
|
||||
}
|
||||
}
|
||||
|
||||
if (positionForSignal.OriginDirection == TradeDirection.Long)
|
||||
{
|
||||
if (positionForSignal.StopLoss.Price >= lastCandle.Low)
|
||||
@@ -583,10 +614,18 @@ public class TradingBot : Bot, ITradingBot
|
||||
{
|
||||
// Check if current position is in profit before flipping
|
||||
var isPositionInProfit = await IsPositionInProfit(openedPosition, lastPrice);
|
||||
|
||||
if (isPositionInProfit)
|
||||
|
||||
// Determine if we should flip based on configuration
|
||||
var shouldFlip = !Config.FlipOnlyWhenInProfit || isPositionInProfit;
|
||||
|
||||
if (shouldFlip)
|
||||
{
|
||||
await LogInformation("Try to flip the position because of an opposite direction signal and current position is in profit");
|
||||
var flipReason = Config.FlipOnlyWhenInProfit
|
||||
? "current position is in profit"
|
||||
: "FlipOnlyWhenInProfit is disabled";
|
||||
|
||||
await LogInformation(
|
||||
$"Try to flip the position because of an opposite direction signal and {flipReason}");
|
||||
await CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true);
|
||||
await SetPositionStatus(previousSignal.Identifier, PositionStatus.Flipped);
|
||||
await OpenPosition(signal);
|
||||
@@ -598,7 +637,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
await LogInformation(
|
||||
$"Position {previousSignal.Identifier} is not in profit (entry: {openedPosition.Open.Price}, current: {lastPrice}). " +
|
||||
$"Signal {signal.Identifier} will wait for position to become profitable before flipping.");
|
||||
|
||||
|
||||
// Keep signal in waiting status to check again on next execution
|
||||
SetSignalStatus(signal.Identifier, SignalStatus.WaitingForPosition);
|
||||
return;
|
||||
@@ -871,10 +910,10 @@ public class TradingBot : Bot, ITradingBot
|
||||
if (Positions.Any(p => p.Identifier == position.Identifier))
|
||||
{
|
||||
// Update the close date for the trade that actually closed the position
|
||||
var currentCandle = Config.IsForBacktest
|
||||
? OptimizedCandles.LastOrDefault()
|
||||
var currentCandle = Config.IsForBacktest
|
||||
? OptimizedCandles.LastOrDefault()
|
||||
: ExchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow);
|
||||
|
||||
|
||||
if (currentCandle != null && position.ProfitAndLoss != null)
|
||||
{
|
||||
// Determine which trade closed the position based on realized P&L
|
||||
@@ -1111,6 +1150,9 @@ public class TradingBot : Bot, ITradingBot
|
||||
BotTradingBalance = Config.BotTradingBalance,
|
||||
StartupTime = StartupTime,
|
||||
CooldownPeriod = Config.CooldownPeriod,
|
||||
MaxLossStreak = Config.MaxLossStreak,
|
||||
MaxPositionTimeHours = Config.MaxPositionTimeHours ?? 0m,
|
||||
FlipOnlyWhenInProfit = Config.FlipOnlyWhenInProfit,
|
||||
};
|
||||
BotService.SaveOrUpdateBotBackup(User, Identifier, Config.BotType, Status, JsonConvert.SerializeObject(data));
|
||||
}
|
||||
@@ -1131,6 +1173,8 @@ public class TradingBot : Bot, ITradingBot
|
||||
BotType = data.BotType,
|
||||
CooldownPeriod = data.CooldownPeriod,
|
||||
MaxLossStreak = data.MaxLossStreak,
|
||||
MaxPositionTimeHours = data.MaxPositionTimeHours == 0m ? null : data.MaxPositionTimeHours,
|
||||
FlipOnlyWhenInProfit = data.FlipOnlyWhenInProfit,
|
||||
Name = data.Name
|
||||
};
|
||||
|
||||
@@ -1205,6 +1249,140 @@ public class TradingBot : Bot, ITradingBot
|
||||
throw new ArgumentException("Invalid position direction");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a position has exceeded the maximum time limit for being open.
|
||||
/// </summary>
|
||||
/// <param name="position">The position to check</param>
|
||||
/// <param name="currentTime">The current time to compare against</param>
|
||||
/// <returns>True if the position has exceeded the time limit, false otherwise</returns>
|
||||
private bool HasPositionExceededTimeLimit(Position position, DateTime currentTime)
|
||||
{
|
||||
if (!Config.MaxPositionTimeHours.HasValue)
|
||||
{
|
||||
return false; // Time-based closure is disabled
|
||||
}
|
||||
|
||||
var timeOpen = currentTime - position.Open.Date;
|
||||
var maxTimeAllowed = TimeSpan.FromHours((double)Config.MaxPositionTimeHours.Value);
|
||||
|
||||
return timeOpen >= maxTimeAllowed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the trading bot configuration with new settings.
|
||||
/// This method validates the new configuration and applies it to the running bot.
|
||||
/// </summary>
|
||||
/// <param name="newConfig">The new configuration to apply</param>
|
||||
/// <returns>True if the configuration was successfully updated, false otherwise</returns>
|
||||
/// <exception cref="ArgumentException">Thrown when the new configuration is invalid</exception>
|
||||
public async Task<bool> UpdateConfiguration(TradingBotConfig newConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate the new configuration
|
||||
if (newConfig == null)
|
||||
{
|
||||
throw new ArgumentException("Configuration cannot be null");
|
||||
}
|
||||
|
||||
if (newConfig.BotTradingBalance <= Constants.GMX.Config.MinimumPositionAmount)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Bot trading balance must be greater than {Constants.GMX.Config.MinimumPositionAmount}");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(newConfig.AccountName))
|
||||
{
|
||||
throw new ArgumentException("Account name cannot be null or empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(newConfig.ScenarioName))
|
||||
{
|
||||
throw new ArgumentException("Scenario name cannot be null or empty");
|
||||
}
|
||||
|
||||
// Protect critical properties that shouldn't change for running bots
|
||||
var protectedBotType = Config.BotType;
|
||||
var protectedIsForBacktest = Config.IsForBacktest;
|
||||
var protectedName = Config.Name;
|
||||
|
||||
// Log the configuration update
|
||||
await LogInformation($"Updating bot configuration. Previous config: " +
|
||||
$"Balance: {Config.BotTradingBalance}, " +
|
||||
$"MaxTime: {Config.MaxPositionTimeHours?.ToString() ?? "Disabled"}, " +
|
||||
$"FlipOnlyProfit: {Config.FlipOnlyWhenInProfit}, " +
|
||||
$"Cooldown: {Config.CooldownPeriod}, " +
|
||||
$"MaxLoss: {Config.MaxLossStreak}");
|
||||
|
||||
// Update the configuration
|
||||
Config = newConfig;
|
||||
|
||||
// Restore protected properties
|
||||
Config.BotType = protectedBotType;
|
||||
Config.IsForBacktest = protectedIsForBacktest;
|
||||
Config.Name = protectedName;
|
||||
|
||||
// If account changed, reload it
|
||||
if (Config.AccountName != Account?.Name)
|
||||
{
|
||||
await LoadAccount();
|
||||
}
|
||||
|
||||
// If scenario changed, reload it
|
||||
var currentScenario = Scenario?.Name;
|
||||
if (Config.ScenarioName != currentScenario)
|
||||
{
|
||||
LoadScenario(Config.ScenarioName);
|
||||
}
|
||||
|
||||
await LogInformation($"Bot configuration updated successfully. New config: " +
|
||||
$"Balance: {Config.BotTradingBalance}, " +
|
||||
$"MaxTime: {Config.MaxPositionTimeHours?.ToString() ?? "Disabled"}, " +
|
||||
$"FlipOnlyProfit: {Config.FlipOnlyWhenInProfit}, " +
|
||||
$"Cooldown: {Config.CooldownPeriod}, " +
|
||||
$"MaxLoss: {Config.MaxLossStreak}");
|
||||
|
||||
// Save the updated configuration as backup
|
||||
if (!Config.IsForBacktest)
|
||||
{
|
||||
SaveBackup();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await LogWarning($"Failed to update bot configuration: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current trading bot configuration.
|
||||
/// </summary>
|
||||
/// <returns>A copy of the current configuration</returns>
|
||||
public TradingBotConfig GetConfiguration()
|
||||
{
|
||||
return new TradingBotConfig
|
||||
{
|
||||
AccountName = Config.AccountName,
|
||||
MoneyManagement = Config.MoneyManagement,
|
||||
Ticker = Config.Ticker,
|
||||
ScenarioName = Config.ScenarioName,
|
||||
Timeframe = Config.Timeframe,
|
||||
IsForWatchingOnly = Config.IsForWatchingOnly,
|
||||
BotTradingBalance = Config.BotTradingBalance,
|
||||
BotType = Config.BotType,
|
||||
IsForBacktest = Config.IsForBacktest,
|
||||
CooldownPeriod = Config.CooldownPeriod,
|
||||
MaxLossStreak = Config.MaxLossStreak,
|
||||
MaxPositionTimeHours = Config.MaxPositionTimeHours,
|
||||
FlipOnlyWhenInProfit = Config.FlipOnlyWhenInProfit,
|
||||
FlipPosition = Config.FlipPosition,
|
||||
Name = Config.Name
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class TradingBotBackup
|
||||
@@ -1224,4 +1402,6 @@ public class TradingBotBackup
|
||||
public decimal BotTradingBalance { get; set; }
|
||||
public int CooldownPeriod { get; set; }
|
||||
public int MaxLossStreak { get; set; }
|
||||
public decimal MaxPositionTimeHours { get; set; }
|
||||
public bool FlipOnlyWhenInProfit { get; set; }
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Bots;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Users;
|
||||
using Managing.Domain.Workflows;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -121,39 +120,61 @@ namespace Managing.Application.ManageBot
|
||||
|
||||
switch (backupBot.BotType)
|
||||
{
|
||||
// case Enums.BotType.SimpleBot:
|
||||
// bot = CreateSimpleBot(backupBot.Name,
|
||||
// null); // Assuming null is an acceptable parameter for workflow
|
||||
// botTask = Task.Run(() => ((IBot)bot).Start());
|
||||
// break;
|
||||
case BotType.ScalpingBot:
|
||||
var scalpingBotData = JsonConvert.DeserializeObject<TradingBotBackup>(backupBot.Data);
|
||||
var scalpingMoneyManagement =
|
||||
_moneyManagementService.GetMoneyMangement(scalpingBotData.MoneyManagement.Name).Result;
|
||||
bot = CreateScalpingBot(
|
||||
scalpingBotData.AccountName,
|
||||
scalpingMoneyManagement,
|
||||
scalpingBotData.Name,
|
||||
scalpingBotData.Ticker,
|
||||
scalpingBotData.ScenarioName,
|
||||
scalpingBotData.Timeframe,
|
||||
scalpingBotData.IsForWatchingOnly,
|
||||
scalpingBotData.BotTradingBalance);
|
||||
|
||||
// Create config from backup data
|
||||
var scalpingConfig = new TradingBotConfig
|
||||
{
|
||||
AccountName = scalpingBotData.AccountName,
|
||||
MoneyManagement = scalpingMoneyManagement,
|
||||
Ticker = scalpingBotData.Ticker,
|
||||
ScenarioName = scalpingBotData.ScenarioName,
|
||||
Timeframe = scalpingBotData.Timeframe,
|
||||
IsForWatchingOnly = scalpingBotData.IsForWatchingOnly,
|
||||
BotTradingBalance = scalpingBotData.BotTradingBalance,
|
||||
BotType = scalpingBotData.BotType,
|
||||
Name = scalpingBotData.Name,
|
||||
CooldownPeriod = scalpingBotData.CooldownPeriod,
|
||||
MaxLossStreak = scalpingBotData.MaxLossStreak,
|
||||
MaxPositionTimeHours = scalpingBotData.MaxPositionTimeHours == 0m ? null : scalpingBotData.MaxPositionTimeHours,
|
||||
FlipOnlyWhenInProfit = scalpingBotData.FlipOnlyWhenInProfit,
|
||||
IsForBacktest = false,
|
||||
FlipPosition = false
|
||||
};
|
||||
|
||||
bot = CreateScalpingBot(scalpingConfig);
|
||||
botTask = Task.Run(() => InitBot((ITradingBot)bot, backupBot));
|
||||
break;
|
||||
|
||||
case BotType.FlippingBot:
|
||||
var flippingBotData = JsonConvert.DeserializeObject<TradingBotBackup>(backupBot.Data);
|
||||
var flippingMoneyManagement =
|
||||
_moneyManagementService.GetMoneyMangement(flippingBotData.MoneyManagement.Name).Result;
|
||||
bot = CreateFlippingBot(
|
||||
flippingBotData.AccountName,
|
||||
flippingMoneyManagement,
|
||||
flippingBotData.Name,
|
||||
flippingBotData.Ticker,
|
||||
flippingBotData.ScenarioName,
|
||||
flippingBotData.Timeframe,
|
||||
flippingBotData.IsForWatchingOnly,
|
||||
flippingBotData.BotTradingBalance);
|
||||
|
||||
// Create config from backup data
|
||||
var flippingConfig = new TradingBotConfig
|
||||
{
|
||||
AccountName = flippingBotData.AccountName,
|
||||
MoneyManagement = flippingMoneyManagement,
|
||||
Ticker = flippingBotData.Ticker,
|
||||
ScenarioName = flippingBotData.ScenarioName,
|
||||
Timeframe = flippingBotData.Timeframe,
|
||||
IsForWatchingOnly = flippingBotData.IsForWatchingOnly,
|
||||
BotTradingBalance = flippingBotData.BotTradingBalance,
|
||||
BotType = flippingBotData.BotType,
|
||||
Name = flippingBotData.Name,
|
||||
CooldownPeriod = flippingBotData.CooldownPeriod,
|
||||
MaxLossStreak = flippingBotData.MaxLossStreak,
|
||||
MaxPositionTimeHours = flippingBotData.MaxPositionTimeHours == 0m ? null : flippingBotData.MaxPositionTimeHours,
|
||||
FlipOnlyWhenInProfit = flippingBotData.FlipOnlyWhenInProfit,
|
||||
IsForBacktest = false,
|
||||
FlipPosition = true
|
||||
};
|
||||
|
||||
bot = CreateFlippingBot(flippingConfig);
|
||||
botTask = Task.Run(() => InitBot((ITradingBot)bot, backupBot));
|
||||
break;
|
||||
}
|
||||
@@ -168,7 +189,8 @@ namespace Managing.Application.ManageBot
|
||||
private void InitBot(ITradingBot bot, BotBackup backupBot)
|
||||
{
|
||||
var user = _userService.GetUser(backupBot.User.Name);
|
||||
backupBot.User = user;
|
||||
bot.User = user;
|
||||
// Config is already set correctly from backup data, so we only need to restore signals, positions, etc.
|
||||
bot.LoadBackup(backupBot);
|
||||
bot.Start();
|
||||
}
|
||||
@@ -241,113 +263,6 @@ namespace Managing.Application.ManageBot
|
||||
}
|
||||
}
|
||||
|
||||
public ITradingBot CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name,
|
||||
Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly,
|
||||
decimal initialTradingBalance)
|
||||
{
|
||||
var config = new TradingBotConfig
|
||||
{
|
||||
AccountName = accountName,
|
||||
MoneyManagement = moneyManagement,
|
||||
Ticker = ticker,
|
||||
ScenarioName = scenario,
|
||||
Timeframe = interval,
|
||||
IsForWatchingOnly = isForWatchingOnly,
|
||||
BotTradingBalance = initialTradingBalance,
|
||||
BotType = BotType.ScalpingBot,
|
||||
Name = name
|
||||
};
|
||||
|
||||
return new ScalpingBot(
|
||||
_exchangeService,
|
||||
_tradingBotLogger,
|
||||
_tradingService,
|
||||
_accountService,
|
||||
_messengerService,
|
||||
this,
|
||||
config);
|
||||
}
|
||||
|
||||
public ITradingBot CreateBacktestScalpingBot(string accountName, MoneyManagement moneyManagement,
|
||||
Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly,
|
||||
decimal initialTradingBalance)
|
||||
{
|
||||
var config = new TradingBotConfig
|
||||
{
|
||||
AccountName = accountName,
|
||||
MoneyManagement = moneyManagement,
|
||||
Ticker = ticker,
|
||||
ScenarioName = scenario,
|
||||
Timeframe = interval,
|
||||
IsForWatchingOnly = isForWatchingOnly,
|
||||
BotTradingBalance = initialTradingBalance,
|
||||
BotType = BotType.ScalpingBot,
|
||||
IsForBacktest = true
|
||||
};
|
||||
|
||||
return new ScalpingBot(
|
||||
_exchangeService,
|
||||
_tradingBotLogger,
|
||||
_tradingService,
|
||||
_accountService,
|
||||
_messengerService,
|
||||
this,
|
||||
config);
|
||||
}
|
||||
|
||||
public ITradingBot CreateFlippingBot(string accountName, MoneyManagement moneyManagement, string name,
|
||||
Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly,
|
||||
decimal initialTradingBalance)
|
||||
{
|
||||
var config = new TradingBotConfig
|
||||
{
|
||||
AccountName = accountName,
|
||||
MoneyManagement = moneyManagement,
|
||||
Ticker = ticker,
|
||||
ScenarioName = scenario,
|
||||
Timeframe = interval,
|
||||
IsForWatchingOnly = isForWatchingOnly,
|
||||
BotTradingBalance = initialTradingBalance,
|
||||
BotType = BotType.FlippingBot
|
||||
};
|
||||
|
||||
return new FlippingBot(
|
||||
_exchangeService,
|
||||
_tradingBotLogger,
|
||||
_tradingService,
|
||||
_accountService,
|
||||
_messengerService,
|
||||
this,
|
||||
config);
|
||||
}
|
||||
|
||||
public ITradingBot CreateBacktestFlippingBot(string accountName, MoneyManagement moneyManagement,
|
||||
Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly,
|
||||
decimal initialTradingBalance)
|
||||
{
|
||||
var config = new TradingBotConfig
|
||||
{
|
||||
AccountName = accountName,
|
||||
MoneyManagement = moneyManagement,
|
||||
Ticker = ticker,
|
||||
ScenarioName = scenario,
|
||||
Timeframe = interval,
|
||||
IsForWatchingOnly = isForWatchingOnly,
|
||||
BotTradingBalance = initialTradingBalance,
|
||||
BotType = BotType.FlippingBot,
|
||||
IsForBacktest = true
|
||||
};
|
||||
|
||||
return new FlippingBot(
|
||||
_exchangeService,
|
||||
_tradingBotLogger,
|
||||
_tradingService,
|
||||
_accountService,
|
||||
_messengerService,
|
||||
this,
|
||||
config);
|
||||
}
|
||||
|
||||
public ITradingBot CreateScalpingBot(TradingBotConfig config)
|
||||
{
|
||||
return new ScalpingBot(
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Managing.Domain.Bots;
|
||||
using MediatR;
|
||||
|
||||
namespace Managing.Application.ManageBot.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Command to update the configuration of a running trading bot
|
||||
/// </summary>
|
||||
public class UpdateBotConfigCommand : IRequest<string>
|
||||
{
|
||||
public string Identifier { get; }
|
||||
public TradingBotConfig NewConfig { get; }
|
||||
|
||||
public UpdateBotConfigCommand(string identifier, TradingBotConfig newConfig)
|
||||
{
|
||||
Identifier = identifier;
|
||||
NewConfig = newConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.ManageBot.Commands;
|
||||
using Managing.Common;
|
||||
using Managing.Domain.Bots;
|
||||
using MediatR;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
@@ -29,6 +31,18 @@ namespace Managing.Application.ManageBot
|
||||
{
|
||||
BotStatus botStatus = BotStatus.Down;
|
||||
|
||||
// Validate the configuration
|
||||
if (request.Config == null)
|
||||
{
|
||||
throw new ArgumentException("Bot configuration is required");
|
||||
}
|
||||
|
||||
if (request.Config.BotTradingBalance <= Constants.GMX.Config.MinimumPositionAmount)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Bot trading balance must be greater than {Constants.GMX.Config.MinimumPositionAmount}");
|
||||
}
|
||||
|
||||
var account = await _accountService.GetAccount(request.Config.AccountName, true, true);
|
||||
|
||||
if (account == null)
|
||||
@@ -43,32 +57,86 @@ namespace Managing.Application.ManageBot
|
||||
throw new Exception($"Account {request.Config.AccountName} has no USDC balance or not enough balance");
|
||||
}
|
||||
|
||||
// Ensure cooldown period is set
|
||||
if (request.Config.CooldownPeriod <= 0)
|
||||
// Ensure essential configuration values are properly set
|
||||
var configToUse = new TradingBotConfig
|
||||
{
|
||||
request.Config.CooldownPeriod = 1; // Default to 1 minute if not set
|
||||
}
|
||||
AccountName = request.Config.AccountName,
|
||||
MoneyManagement = request.Config.MoneyManagement,
|
||||
Ticker = request.Config.Ticker,
|
||||
ScenarioName = request.Config.ScenarioName,
|
||||
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.FlipPosition,
|
||||
Name = request.Config.Name ?? request.Name
|
||||
};
|
||||
|
||||
switch (request.Config.BotType)
|
||||
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:
|
||||
var sBot = _botFactory.CreateScalpingBot(request.Config);
|
||||
var sBot = _botFactory.CreateScalpingBot(configToUse);
|
||||
sBot.User = request.User;
|
||||
|
||||
// Log the configuration being used
|
||||
await LogBotConfigurationAsync(sBot, "ScalpingBot created");
|
||||
|
||||
_botService.AddTradingBotToCache(sBot);
|
||||
return sBot.GetStatus();
|
||||
|
||||
case BotType.FlippingBot:
|
||||
var fBot = _botFactory.CreateFlippingBot(request.Config);
|
||||
var fBot = _botFactory.CreateFlippingBot(configToUse);
|
||||
fBot.User = request.User;
|
||||
|
||||
// Log the configuration being used
|
||||
await LogBotConfigurationAsync(fBot, "FlippingBot created");
|
||||
|
||||
_botService.AddTradingBotToCache(fBot);
|
||||
return fBot.GetStatus();
|
||||
}
|
||||
|
||||
return botStatus.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs the bot configuration for debugging and audit purposes
|
||||
/// </summary>
|
||||
/// <param name="bot">The trading bot instance</param>
|
||||
/// <param name="context">Context information for the log</param>
|
||||
private async Task LogBotConfigurationAsync(ITradingBot bot, string context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = bot.GetConfiguration();
|
||||
var logMessage = $"{context} - Bot: {config.Name}, " +
|
||||
$"Type: {config.BotType}, " +
|
||||
$"Account: {config.AccountName}, " +
|
||||
$"Ticker: {config.Ticker}, " +
|
||||
$"Balance: {config.BotTradingBalance}, " +
|
||||
$"MaxTime: {config.MaxPositionTimeHours?.ToString() ?? "Disabled"}, " +
|
||||
$"FlipOnlyProfit: {config.FlipOnlyWhenInProfit}, " +
|
||||
$"Cooldown: {config.CooldownPeriod}, " +
|
||||
$"MaxLoss: {config.MaxLossStreak}";
|
||||
|
||||
// Log through the bot's logger (this will use the bot's logging mechanism)
|
||||
// For now, we'll just add a comment that this could be enhanced with actual logging
|
||||
// Console.WriteLine(logMessage); // Could be replaced with proper logging
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore logging errors to not affect bot creation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Application.ManageBot.Commands;
|
||||
using MediatR;
|
||||
|
||||
namespace Managing.Application.ManageBot
|
||||
{
|
||||
/// <summary>
|
||||
/// Handler for updating trading bot configurations
|
||||
/// </summary>
|
||||
public class UpdateBotConfigCommandHandler : IRequestHandler<UpdateBotConfigCommand, string>
|
||||
{
|
||||
private readonly IBotService _botService;
|
||||
|
||||
public UpdateBotConfigCommandHandler(IBotService botService)
|
||||
{
|
||||
_botService = botService;
|
||||
}
|
||||
|
||||
public async Task<string> Handle(UpdateBotConfigCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(request.Identifier))
|
||||
{
|
||||
throw new ArgumentException("Bot identifier is required");
|
||||
}
|
||||
|
||||
if (request.NewConfig == null)
|
||||
{
|
||||
throw new ArgumentException("New configuration is required");
|
||||
}
|
||||
|
||||
// Get the bot from active bots
|
||||
var activeBots = _botService.GetActiveBots();
|
||||
var bot = activeBots.FirstOrDefault(b => b.Identifier == request.Identifier);
|
||||
|
||||
if (bot == null)
|
||||
{
|
||||
return $"Bot with identifier {request.Identifier} not found or is not running";
|
||||
}
|
||||
|
||||
// Update the bot configuration
|
||||
var updateResult = await bot.UpdateConfiguration(request.NewConfig);
|
||||
|
||||
if (updateResult)
|
||||
{
|
||||
return $"Bot configuration updated successfully for {request.Identifier}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"Failed to update bot configuration for {request.Identifier}";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Error updating bot configuration: {ex.Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user