diff --git a/.DS_Store b/.DS_Store
index 2a2cb61..61f0536 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/src/Managing.Api/Controllers/BacktestController.cs b/src/Managing.Api/Controllers/BacktestController.cs
index d92f66e..3ad41e6 100644
--- a/src/Managing.Api/Controllers/BacktestController.cs
+++ b/src/Managing.Api/Controllers/BacktestController.cs
@@ -108,6 +108,8 @@ public class BacktestController : BaseController
/// The name of the money management strategy to use.
/// The money management strategy details, if not using a named strategy.
/// Whether to save the backtest results.
+ /// The cooldown period for the backtest.
+ /// The maximum loss streak for the backtest.
/// The result of the backtest.
[HttpPost]
[Route("Run")]
@@ -122,7 +124,9 @@ public class BacktestController : BaseController
DateTime startDate,
DateTime endDate,
MoneyManagement? moneyManagement = null,
- bool save = false)
+ bool save = false,
+ decimal cooldownPeriod = 1,
+ int maxLossStreak = 0)
{
if (string.IsNullOrEmpty(accountName))
{
@@ -174,7 +178,9 @@ public class BacktestController : BaseController
endDate,
user,
watchOnly,
- save);
+ save,
+ cooldownPeriod: cooldownPeriod,
+ maxLossStreak: maxLossStreak);
break;
case BotType.FlippingBot:
backtestResult = await _backtester.RunFlippingBotBacktest(
@@ -188,7 +194,9 @@ public class BacktestController : BaseController
endDate,
user,
watchOnly,
- save);
+ save,
+ cooldownPeriod: cooldownPeriod,
+ maxLossStreak: maxLossStreak);
break;
}
diff --git a/src/Managing.Api/Controllers/BaseController.cs b/src/Managing.Api/Controllers/BaseController.cs
index 70c6fdc..2421609 100644
--- a/src/Managing.Api/Controllers/BaseController.cs
+++ b/src/Managing.Api/Controllers/BaseController.cs
@@ -1,7 +1,7 @@
-using Managing.Application.Abstractions.Services;
+using System.Security.Claims;
+using Managing.Application.Abstractions.Services;
using Managing.Domain.Users;
using Microsoft.AspNetCore.Mvc;
-using System.Security.Claims;
namespace Managing.Api.Controllers;
@@ -10,7 +10,7 @@ namespace Managing.Api.Controllers;
[Produces("application/json")]
public abstract class BaseController : ControllerBase
{
- private readonly IUserService _userService;
+ public readonly IUserService _userService;
public BaseController(IUserService userService)
{
diff --git a/src/Managing.Api/Controllers/BotController.cs b/src/Managing.Api/Controllers/BotController.cs
index cb7a18b..e91d082 100644
--- a/src/Managing.Api/Controllers/BotController.cs
+++ b/src/Managing.Api/Controllers/BotController.cs
@@ -1,6 +1,6 @@
-using Managing.Api.Models.Requests;
-using Managing.Application.Abstractions;
+using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
+using Managing.Application.Bots;
using Managing.Application.Hubs;
using Managing.Application.ManageBot.Commands;
using Managing.Common;
@@ -56,10 +56,10 @@ public class BotController : BaseController
///
/// Checks if the current authenticated user owns the account associated with the specified bot or account name
///
- /// The name of the bot to check
+ /// The identifier of the bot to check
/// Optional account name to check when creating a new bot
/// True if the user owns the account, False otherwise
- private async Task UserOwnsBotAccount(string botName, string accountName = null)
+ private async Task UserOwnsBotAccount(string identifier, string accountName = null)
{
try
{
@@ -78,12 +78,12 @@ public class BotController : BaseController
// For existing bots, check if the user owns the bot's account
var activeBots = _botService.GetActiveBots();
- var bot = activeBots.FirstOrDefault(b => b.Name == botName);
+ var bot = activeBots.FirstOrDefault(b => b.Identifier == identifier);
if (bot == null)
return true; // Bot doesn't exist yet, so no ownership conflict
var botAccountService = HttpContext.RequestServices.GetRequiredService();
- var botAccount = await botAccountService.GetAccount(bot.AccountName, true, false);
+ var botAccount = await botAccountService.GetAccount(bot.Config.AccountName, true, false);
// Compare the user names
return botAccount != null && botAccount.User != null && botAccount.User.Name == user.Name;
}
@@ -106,7 +106,7 @@ public class BotController : BaseController
try
{
// Check if user owns the account
- if (!await UserOwnsBotAccount(request.BotName, request.AccountName))
+ if (!await UserOwnsBotAccount(request.Identifier, request.AccountName))
{
return Forbid("You don't have permission to start this bot");
}
@@ -131,9 +131,19 @@ public class BotController : BaseController
$"Initial trading balance must be greater than {Constants.GMX.Config.MinimumPositionAmount}");
}
- var result = await _mediator.Send(new StartBotCommand(request.BotType, request.BotName, request.Ticker,
- request.Scenario, request.Timeframe, request.AccountName, request.MoneyManagementName, user,
- request.IsForWatchOnly, request.InitialTradingBalance));
+ var config = new TradingBotConfig
+ {
+ AccountName = request.AccountName,
+ MoneyManagement = moneyManagement,
+ Ticker = request.Ticker,
+ ScenarioName = request.Scenario,
+ Timeframe = request.Timeframe,
+ IsForWatchingOnly = request.IsForWatchOnly,
+ BotTradingBalance = request.InitialTradingBalance,
+ BotType = request.BotType
+ };
+
+ var result = await _mediator.Send(new StartBotCommand(config, request.Identifier, user));
await NotifyBotSubscriberAsync();
return Ok(result);
@@ -149,22 +159,22 @@ public class BotController : BaseController
/// Stops a bot specified by type and name.
///
/// The type of the bot to stop.
- /// The name of the bot to stop.
+ /// The identifier of the bot to stop.
/// A string indicating the result of the stop operation.
[HttpGet]
[Route("Stop")]
- public async Task> Stop(BotType botType, string botName)
+ public async Task> Stop(BotType botType, string identifier)
{
try
{
// Check if user owns the account
- if (!await UserOwnsBotAccount(botName))
+ if (!await UserOwnsBotAccount(identifier))
{
return Forbid("You don't have permission to stop this bot");
}
- var result = await _mediator.Send(new StopBotCommand(botType, botName));
- _logger.LogInformation($"{botType} type called {botName} is now {result}");
+ var result = await _mediator.Send(new StopBotCommand(botType, identifier));
+ _logger.LogInformation($"{botType} type with identifier {identifier} is now {result}");
await NotifyBotSubscriberAsync();
@@ -180,21 +190,21 @@ public class BotController : BaseController
///
/// Deletes a bot specified by name.
///
- /// The name of the bot to delete.
+ /// The identifier of the bot to delete.
/// A boolean indicating the result of the delete operation.
[HttpDelete]
[Route("Delete")]
- public async Task> Delete(string botName)
+ public async Task> Delete(string identifier)
{
try
{
// Check if user owns the account
- if (!await UserOwnsBotAccount(botName))
+ if (!await UserOwnsBotAccount(identifier))
{
return Forbid("You don't have permission to delete this bot");
}
- var result = await _botService.DeleteBot(botName);
+ var result = await _botService.DeleteBot(identifier);
await NotifyBotSubscriberAsync();
return Ok(result);
}
@@ -235,9 +245,9 @@ public class BotController : BaseController
foreach (var bot in userBots)
{
- await _mediator.Send(new StopBotCommand(bot.BotType, bot.Name));
+ await _mediator.Send(new StopBotCommand(bot.BotType, bot.Identifier));
await _hubContext.Clients.All.SendAsync("SendNotification",
- $"Bot {bot.Name} paused by {user.Name}.", "Info");
+ $"Bot {bot.Identifier} paused by {user.Name}.", "Info");
}
await NotifyBotSubscriberAsync();
@@ -254,22 +264,22 @@ public class BotController : BaseController
/// Restarts a bot specified by type and name.
///
/// The type of the bot to restart.
- /// The name of the bot to restart.
+ /// The identifier of the bot to restart.
/// A string indicating the result of the restart operation.
[HttpGet]
[Route("Restart")]
- public async Task> Restart(BotType botType, string botName)
+ public async Task> Restart(BotType botType, string identifier)
{
try
{
// Check if user owns the account
- if (!await UserOwnsBotAccount(botName))
+ if (!await UserOwnsBotAccount(identifier))
{
return Forbid("You don't have permission to restart this bot");
}
- var result = await _mediator.Send(new RestartBotCommand(botType, botName));
- _logger.LogInformation($"{botType} type called {botName} is now {result}");
+ var result = await _mediator.Send(new RestartBotCommand(botType, identifier));
+ _logger.LogInformation($"{botType} type with identifier {identifier} is now {result}");
await NotifyBotSubscriberAsync();
@@ -314,15 +324,15 @@ public class BotController : BaseController
{
// We can't directly restart a bot with just BotType and Name
// Instead, stop the bot and then retrieve the backup to start it again
- await _mediator.Send(new StopBotCommand(bot.BotType, bot.Name));
+ await _mediator.Send(new StopBotCommand(bot.BotType, bot.Identifier));
// Get the saved bot backup
- var backup = _botService.GetBotBackup(bot.Name);
+ var backup = _botService.GetBotBackup(bot.Identifier);
if (backup != null)
{
_botService.StartBotFromBackup(backup);
await _hubContext.Clients.All.SendAsync("SendNotification",
- $"Bot {bot.Name} restarted by {user.Name}.", "Info");
+ $"Bot {bot.Identifier} restarted by {user.Name}.", "Info");
}
}
@@ -339,22 +349,22 @@ public class BotController : BaseController
///
/// Toggles the watching status of a bot specified by name.
///
- /// The name of the bot to toggle watching status.
+ /// The identifier of the bot to toggle watching status.
/// A string indicating the new watching status of the bot.
[HttpGet]
[Route("ToggleIsForWatching")]
- public async Task> ToggleIsForWatching(string botName)
+ public async Task> ToggleIsForWatching(string identifier)
{
try
{
// Check if user owns the account
- if (!await UserOwnsBotAccount(botName))
+ if (!await UserOwnsBotAccount(identifier))
{
return Forbid("You don't have permission to modify this bot");
}
- var result = await _mediator.Send(new ToggleIsForWatchingCommand(botName));
- _logger.LogInformation($"{botName} bot is now {result}");
+ var result = await _mediator.Send(new ToggleIsForWatchingCommand(identifier));
+ _logger.LogInformation($"Bot with identifier {identifier} is now {result}");
await NotifyBotSubscriberAsync();
@@ -397,13 +407,15 @@ public class BotController : BaseController
Candles = item.Candles.DistinctBy(c => c.Date).ToList(),
WinRate = item.GetWinRate(),
ProfitAndLoss = item.GetProfitAndLoss(),
- Timeframe = item.Timeframe,
- Ticker = item.Ticker,
- Scenario = item.ScenarioName,
- IsForWatchingOnly = item.IsForWatchingOnly,
- BotType = item.BotType,
- AccountName = item.AccountName,
- MoneyManagement = item.MoneyManagement
+ Timeframe = item.Config.Timeframe,
+ Ticker = item.Config.Ticker,
+ Scenario = item.Config.ScenarioName,
+ IsForWatchingOnly = item.Config.IsForWatchingOnly,
+ BotType = item.Config.BotType,
+ AccountName = item.Config.AccountName,
+ MoneyManagement = item.Config.MoneyManagement,
+ Identifier = item.Identifier,
+ AgentName = item.User.AgentName
});
}
@@ -431,22 +443,22 @@ public class BotController : BaseController
try
{
// Check if user owns the account
- if (!await UserOwnsBotAccount(request.BotName))
+ if (!await UserOwnsBotAccount(request.Identifier))
{
return Forbid("You don't have permission to open positions for this bot");
}
var activeBots = _botService.GetActiveBots();
- var bot = activeBots.FirstOrDefault(b => b.Name == request.BotName) as ApplicationTradingBot;
+ var bot = activeBots.FirstOrDefault(b => b.Identifier == request.Identifier) as ApplicationTradingBot;
if (bot == null)
{
- return NotFound($"Bot {request.BotName} not found or is not a trading bot");
+ return NotFound($"Bot with identifier {request.Identifier} not found or is not a trading bot");
}
if (bot.GetStatus() != BotStatus.Up.ToString())
{
- return BadRequest($"Bot {request.BotName} is not running");
+ return BadRequest($"Bot with identifier {request.Identifier} is not running");
}
var position = await bot.OpenPositionManually(
@@ -475,29 +487,30 @@ public class BotController : BaseController
try
{
// Check if user owns the account
- if (!await UserOwnsBotAccount(request.BotName))
+ if (!await UserOwnsBotAccount(request.Identifier))
{
return Forbid("You don't have permission to close positions for this bot");
}
var activeBots = _botService.GetActiveBots();
- var bot = activeBots.FirstOrDefault(b => b.Name == request.BotName) as ApplicationTradingBot;
+ var bot = activeBots.FirstOrDefault(b => b.Identifier == request.Identifier) as ApplicationTradingBot;
if (bot == null)
{
- return NotFound($"Bot {request.BotName} not found or is not a trading bot");
+ return NotFound($"Bot with identifier {request.Identifier} not found or is not a trading bot");
}
if (bot.GetStatus() != BotStatus.Up.ToString())
{
- return BadRequest($"Bot {request.BotName} is not running");
+ return BadRequest($"Bot with identifier {request.Identifier} is not running");
}
// Find the position to close
var position = bot.Positions.FirstOrDefault(p => p.Identifier == request.PositionId);
if (position == null)
{
- return NotFound($"Position with ID {request.PositionId} not found for bot {request.BotName}");
+ return NotFound(
+ $"Position with ID {request.PositionId} not found for bot with identifier {request.Identifier}");
}
// Find the signal associated with this position
@@ -528,18 +541,85 @@ public class BotController : BaseController
}
}
+///
+/// Request model for opening a position manually
+///
+public class OpenPositionManuallyRequest
+{
+ ///
+ /// The identifier of the bot
+ ///
+ public string Identifier { get; set; }
+
+ ///
+ /// The direction of the position
+ ///
+ public TradeDirection Direction { get; set; }
+}
+
///
/// Request model for closing a position
///
public class ClosePositionRequest
{
///
- /// The name of the bot
+ /// The identifier of the bot
///
- public string BotName { get; set; }
+ public string Identifier { get; set; }
///
/// The ID of the position to close
///
public string PositionId { get; set; }
+}
+
+///
+/// Request model for starting a bot
+///
+public class StartBotRequest
+{
+ ///
+ /// The type of bot to start
+ ///
+ public BotType BotType { get; set; }
+
+ ///
+ /// The identifier of the bot
+ ///
+ public string Identifier { get; set; }
+
+ ///
+ /// The ticker to trade
+ ///
+ public Ticker Ticker { get; set; }
+
+ ///
+ /// The scenario to use
+ ///
+ public string Scenario { get; set; }
+
+ ///
+ /// The timeframe to use
+ ///
+ public Timeframe Timeframe { get; set; }
+
+ ///
+ /// The account name to use
+ ///
+ public string AccountName { get; set; }
+
+ ///
+ /// The money management name to use
+ ///
+ public string MoneyManagementName { get; set; }
+
+ ///
+ /// Whether the bot is for watching only
+ ///
+ public bool IsForWatchOnly { get; set; }
+
+ ///
+ /// The initial trading balance
+ ///
+ public decimal InitialTradingBalance { get; set; }
}
\ No newline at end of file
diff --git a/src/Managing.Api/Controllers/DataController.cs b/src/Managing.Api/Controllers/DataController.cs
index d3d3ee9..1f685b5 100644
--- a/src/Managing.Api/Controllers/DataController.cs
+++ b/src/Managing.Api/Controllers/DataController.cs
@@ -1,20 +1,18 @@
-using Managing.Application.Abstractions;
+using Managing.Api.Models.Responses;
+using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Application.ManageBot.Commands;
using Managing.Application.Workers.Abstractions;
-using Managing.Api.Models.Responses;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Helpers;
using Managing.Domain.Statistics;
using Managing.Domain.Trades;
-using Managing.Domain.Users;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
-using System.Linq;
using static Managing.Common.Enums;
namespace Managing.Api.Controllers;
@@ -65,20 +63,114 @@ public class DataController : ControllerBase
/// The timeframe for which to retrieve tickers.
/// An array of tickers.
[HttpPost("GetTickers")]
- public async Task> GetTickers(Timeframe timeframe)
+ public async Task>> GetTickers(Timeframe timeframe)
{
var cacheKey = string.Concat(timeframe.ToString());
- var tickers = _cacheService.GetValue>(cacheKey);
+ var tickers = _cacheService.GetValue>(cacheKey);
if (tickers == null || tickers.Count == 0)
{
- tickers = await _exchangeService.GetTickers(timeframe);
+ var availableTicker = await _exchangeService.GetTickers(timeframe);
+
+ tickers = MapTickerToTickerInfos(availableTicker);
+
_cacheService.SaveValue(cacheKey, tickers, TimeSpan.FromHours(2));
}
return Ok(tickers);
}
+ private List MapTickerToTickerInfos(List availableTicker)
+ {
+ var tickerInfos = new List();
+ var tokens = new Dictionary
+ {
+ { "AAVE", "https://assets.coingecko.com/coins/images/12645/standard/AAVE.png?1696512452" },
+ { "ADA", "https://assets.coingecko.com/coins/images/975/standard/cardano.png?1696502090" },
+ { "APE", "https://assets.coingecko.com/coins/images/24383/standard/apecoin.jpg?1696523566" },
+ { "ARB", "https://assets.coingecko.com/coins/images/16547/small/photo_2023-03-29_21.47.00.jpeg?1680097630" },
+ { "ATOM", "https://assets.coingecko.com/coins/images/1481/standard/cosmos_hub.png?1696502525" },
+ { "AVAX", "https://assets.coingecko.com/coins/images/12559/small/coin-round-red.png?1604021818" },
+ { "BNB", "https://assets.coingecko.com/coins/images/825/standard/bnb-icon2_2x.png?1696501970" },
+ { "BTC", "https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579" },
+ { "DOGE", "https://assets.coingecko.com/coins/images/5/small/dogecoin.png?1547792256" },
+ { "DOT", "https://static.coingecko.com/s/polkadot-73b0c058cae10a2f076a82dcade5cbe38601fad05d5e6211188f09eb96fa4617.gif" },
+ { "ETH", "https://assets.coingecko.com/coins/images/279/small/ethereum.png?1595348880" },
+ { "FIL", "https://assets.coingecko.com/coins/images/12817/standard/filecoin.png?1696512609" },
+ { "GMX", "https://assets.coingecko.com/coins/images/18323/small/arbit.png?1631532468" },
+ { "LINK", "https://assets.coingecko.com/coins/images/877/thumb/chainlink-new-logo.png?1547034700" },
+ { "LTC", "https://assets.coingecko.com/coins/images/2/small/litecoin.png?1547033580" },
+ { "MATIC", "https://assets.coingecko.com/coins/images/32440/standard/polygon.png?1698233684" },
+ { "NEAR", "https://assets.coingecko.com/coins/images/10365/standard/near.jpg?1696510367" },
+ { "OP", "https://assets.coingecko.com/coins/images/25244/standard/Optimism.png?1696524385" },
+ { "PEPE", "https://assets.coingecko.com/coins/images/29850/standard/pepe-token.jpeg?1696528776" },
+ { "SOL", "https://assets.coingecko.com/coins/images/4128/small/solana.png?1640133422" },
+ { "UNI", "https://assets.coingecko.com/coins/images/12504/thumb/uniswap-uni.png?1600306604" },
+ { "USDC", "https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389" },
+ { "USDT", "https://assets.coingecko.com/coins/images/325/thumb/Tether-logo.png?1598003707" },
+ { "WIF", "https://assets.coingecko.com/coins/images/33566/standard/dogwifhat.jpg?1702499428" },
+ { "XRP", "https://assets.coingecko.com/coins/images/44/small/xrp-symbol-white-128.png?1605778731" },
+ { "SHIB", "https://assets.coingecko.com/coins/images/11939/standard/shiba.png?1696511800" },
+ { "STX", "https://assets.coingecko.com/coins/images/2069/standard/Stacks_Logo_png.png?1709979332" },
+ { "ORDI", "https://assets.coingecko.com/coins/images/30162/standard/ordi.png?1696529082" },
+ { "APT", "https://assets.coingecko.com/coins/images/26455/standard/aptos_round.png?1696525528" },
+ { "BOME", "https://assets.coingecko.com/coins/images/36071/standard/bome.png?1710407255" },
+ { "MEME", "https://assets.coingecko.com/coins/images/32528/standard/memecoin_%282%29.png?1698912168" },
+ { "FLOKI", "https://assets.coingecko.com/coins/images/16746/standard/PNG_image.png?1696516318" },
+ { "MEW", "https://assets.coingecko.com/coins/images/36440/standard/MEW.png?1711442286" },
+ { "TAO", "https://assets.coingecko.com/coins/images/28452/standard/ARUsPeNQ_400x400.jpeg?1696527447" },
+ { "BONK", "https://assets.coingecko.com/coins/images/28600/standard/bonk.jpg?1696527587" },
+ { "WLD", "https://assets.coingecko.com/coins/images/31069/standard/worldcoin.jpeg?1696529903" },
+ { "tBTC", "https://assets.coingecko.com/coins/images/11224/standard/0x18084fba666a33d37592fa2633fd49a74dd93a88.png?1696511155" },
+ { "EIGEN", "https://assets.coingecko.com/coins/images/37441/standard/eigen.jpg?1728023974" },
+ { "SUI", "https://assets.coingecko.com/coins/images/26375/standard/sui-ocean-square.png?1727791290" },
+ { "SEI", "https://assets.coingecko.com/coins/images/28205/standard/Sei_Logo_-_Transparent.png?1696527207" },
+ { "DAI", "https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734" },
+ { "TIA", "https://assets.coingecko.com/coins/images/31967/standard/tia.jpg?1696530772" },
+ { "TRX", "https://assets.coingecko.com/coins/images/1094/standard/tron-logo.png?1696502193" },
+ { "TON", "https://assets.coingecko.com/coins/images/17980/standard/photo_2024-09-10_17.09.00.jpeg?1725963446" },
+ { "PENDLE", "https://assets.coingecko.com/coins/images/15069/standard/Pendle_Logo_Normal-03.png?1696514728" },
+ { "wstETH", "https://assets.coingecko.com/coins/images/18834/standard/wstETH.png?1696518295" },
+ { "USDe", "https://assets.coingecko.com/coins/images/33613/standard/USDE.png?1716355685" },
+ { "SATS", "https://assets.coingecko.com/coins/images/30666/standard/_dD8qr3M_400x400.png?1702913020" },
+ { "POL", "https://assets.coingecko.com/coins/images/32440/standard/polygon.png?1698233684" },
+ { "XLM", "https://assets.coingecko.com/coins/images/100/standard/Stellar_symbol_black_RGB.png?1696501482" },
+ { "BCH", "https://assets.coingecko.com/coins/images/780/standard/bitcoin-cash-circle.png?1696501932" },
+ { "ICP", "https://assets.coingecko.com/coins/images/14495/standard/Internet_Computer_logo.png?1696514180" },
+ { "RENDER", "https://assets.coingecko.com/coins/images/11636/standard/rndr.png?1696511529" },
+ { "INJ", "https://assets.coingecko.com/coins/images/12882/standard/Secondary_Symbol.png?1696512670" },
+ { "TRUMP", "https://assets.coingecko.com/coins/images/53746/standard/trump.png?1737171561" },
+ { "MELANIA", "https://assets.coingecko.com/coins/images/53775/standard/melania-meme.png?1737329885" },
+ { "ENA", "https://assets.coingecko.com/coins/images/36530/standard/ethena.png?1711701436" },
+ { "FARTCOIN", "https://assets.coingecko.com/coins/images/50891/standard/fart.jpg?1729503972" },
+ { "AI16Z", "https://assets.coingecko.com/coins/images/51090/standard/AI16Z.jpg?1730027175" },
+ { "ANIME", "https://assets.coingecko.com/coins/images/53575/standard/anime.jpg?1736748703" },
+ { "BERA", "https://assets.coingecko.com/coins/images/25235/standard/BERA.png?1738822008" },
+ { "VIRTUAL", "https://assets.coingecko.com/coins/images/34057/standard/LOGOMARK.png?1708356054" },
+ { "PENGU", "https://assets.coingecko.com/coins/images/52622/standard/PUDGY_PENGUINS_PENGU_PFP.png?1733809110" },
+ { "FET", "https://assets.coingecko.com/coins/images/5681/standard/ASI.png?1719827289" },
+ { "ONDO", "https://assets.coingecko.com/coins/images/26580/standard/ONDO.png?1696525656" },
+ { "AIXBT", "https://assets.coingecko.com/coins/images/51784/standard/3.png?1731981138" },
+ { "CAKE", "https://assets.coingecko.com/coins/images/12632/standard/pancakeswap-cake-logo_%281%29.png?1696512440" },
+ { "S", "https://assets.coingecko.com/coins/images/38108/standard/200x200_Sonic_Logo.png?1734679256" },
+ { "JUP", "https://assets.coingecko.com/coins/images/34188/standard/jup.png?1704266489" },
+ { "HYPE", "https://assets.coingecko.com/coins/images/50882/standard/hyperliquid.jpg?1729431300" },
+ { "OM", "https://assets.coingecko.com/coins/images/12151/standard/OM_Token.png?1696511991" }
+ };
+
+ foreach (var ticker in availableTicker)
+ {
+ var tickerInfo = new TickerInfos
+ {
+ Ticker = ticker,
+ ImageUrl = tokens.GetValueOrDefault(ticker.ToString(), "https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579") // Default to BTC image if not found
+ };
+ tickerInfos.Add(tickerInfo);
+ }
+
+ return tickerInfos;
+ }
+
///
/// Retrieves the latest spotlight overview, using caching to enhance response times.
///
@@ -222,24 +314,24 @@ public class DataController : ControllerBase
public async Task>> GetUserStrategies(string agentName)
{
string cacheKey = $"UserStrategies_{agentName}";
-
+
// Check if the user strategy details are already cached
var cachedDetails = _cacheService.GetValue>(cacheKey);
-
+
if (cachedDetails != null && cachedDetails.Count > 0)
{
return Ok(cachedDetails);
}
-
+
// Get all strategies for the specified user
var userStrategies = await _mediator.Send(new GetUserStrategiesCommand(agentName));
-
+
// Convert to detailed view model with additional information
var result = userStrategies.Select(strategy => MapStrategyToViewModel(strategy)).ToList();
-
+
// Cache the results for 5 minutes
_cacheService.SaveValue(cacheKey, result, TimeSpan.FromMinutes(5));
-
+
return Ok(result);
}
@@ -253,32 +345,32 @@ public class DataController : ControllerBase
public async Task> GetUserStrategy(string agentName, string strategyName)
{
string cacheKey = $"UserStrategy_{agentName}_{strategyName}";
-
+
// Check if the user strategy details are already cached
var cachedDetails = _cacheService.GetValue(cacheKey);
-
+
if (cachedDetails != null)
{
return Ok(cachedDetails);
}
-
+
// Get the specific strategy for the user
var strategy = await _mediator.Send(new GetUserStrategyCommand(agentName, strategyName));
-
+
if (strategy == null)
{
return NotFound($"Strategy '{strategyName}' not found for user '{agentName}'");
}
-
+
// Map the strategy to a view model using the shared method
var result = MapStrategyToViewModel(strategy);
-
+
// Cache the results for 5 minutes
_cacheService.SaveValue(cacheKey, result, TimeSpan.FromMinutes(5));
-
+
return Ok(result);
}
-
+
///
/// Maps a trading bot to a strategy view model with detailed statistics
///
@@ -288,7 +380,7 @@ public class DataController : ControllerBase
{
// Get the runtime directly from the bot
TimeSpan runtimeSpan = strategy.GetRuntime();
-
+
// Get the startup time from the bot's internal property
// If bot is not running, we use MinValue as a placeholder
DateTime startupTime = DateTime.MinValue;
@@ -296,30 +388,30 @@ public class DataController : ControllerBase
{
startupTime = bot.StartupTime;
}
-
+
// Calculate ROI percentage based on PnL relative to account value
decimal pnl = strategy.GetProfitAndLoss();
-
+
// If we had initial investment amount, we could calculate ROI like:
decimal initialInvestment = 1000; // Example placeholder, ideally should come from the account
decimal roi = pnl != 0 ? (pnl / initialInvestment) * 100 : 0;
-
+
// Calculate volume statistics
decimal totalVolume = TradingBox.GetTotalVolumeTraded(strategy.Positions);
decimal volumeLast24h = TradingBox.GetLast24HVolumeTraded(strategy.Positions);
-
+
// Calculate win/loss statistics
(int wins, int losses) = TradingBox.GetWinLossCount(strategy.Positions);
-
+
// Calculate ROI for last 24h
decimal roiLast24h = TradingBox.GetLast24HROI(strategy.Positions);
-
+
return new UserStrategyDetailsViewModel
{
Name = strategy.Name,
- StrategyName = strategy.ScenarioName,
- State = strategy.GetStatus() == BotStatus.Up.ToString() ? "RUNNING" :
- strategy.GetStatus() == BotStatus.Down.ToString() ? "STOPPED" : "UNUSED",
+ StrategyName = strategy.Config.ScenarioName,
+ State = strategy.GetStatus() == BotStatus.Up.ToString() ? "RUNNING" :
+ strategy.GetStatus() == BotStatus.Down.ToString() ? "STOPPED" : "UNUSED",
PnL = pnl,
ROIPercentage = roi,
ROILast24H = roiLast24h,
@@ -329,7 +421,8 @@ public class DataController : ControllerBase
VolumeLast24H = volumeLast24h,
Wins = wins,
Losses = losses,
- Positions = strategy.Positions.OrderByDescending(p => p.Date).ToList() // Include sorted positions with most recent first
+ Positions = strategy.Positions.OrderByDescending(p => p.Date)
+ .ToList() // Include sorted positions with most recent first
};
}
@@ -347,20 +440,20 @@ public class DataController : ControllerBase
{
timeFilter = "Total"; // Default to Total if invalid
}
-
+
string cacheKey = $"PlatformSummary_{timeFilter}";
-
+
// Check if the platform summary is already cached
var cachedSummary = _cacheService.GetValue(cacheKey);
-
+
if (cachedSummary != null)
{
return Ok(cachedSummary);
}
-
+
// Get all agents and their strategies
var agentsWithStrategies = await _mediator.Send(new GetAllAgentsCommand(timeFilter));
-
+
// Create the platform summary
var summary = new PlatformSummaryViewModel
{
@@ -368,50 +461,50 @@ public class DataController : ControllerBase
TotalActiveStrategies = agentsWithStrategies.Values.Sum(list => list.Count),
TimeFilter = timeFilter
};
-
+
// Calculate total platform metrics
decimal totalPlatformPnL = 0;
decimal totalPlatformVolume = 0;
decimal totalPlatformVolumeLast24h = 0;
-
+
// Create summaries for each agent
foreach (var agent in agentsWithStrategies)
{
var user = agent.Key;
var strategies = agent.Value;
-
+
if (strategies.Count == 0)
{
continue; // Skip agents with no strategies
}
-
+
// Combine all positions from all strategies
var allPositions = strategies.SelectMany(s => s.Positions).ToList();
-
+
// Calculate agent metrics
decimal totalPnL = TradingBox.GetPnLInTimeRange(allPositions, timeFilter);
decimal pnlLast24h = TradingBox.GetPnLInTimeRange(allPositions, "24H");
-
+
decimal totalROI = TradingBox.GetROIInTimeRange(allPositions, timeFilter);
decimal roiLast24h = TradingBox.GetROIInTimeRange(allPositions, "24H");
-
+
(int wins, int losses) = TradingBox.GetWinLossCountInTimeRange(allPositions, timeFilter);
-
+
// Calculate trading volumes
decimal totalVolume = TradingBox.GetTotalVolumeTraded(allPositions);
decimal volumeLast24h = TradingBox.GetLast24HVolumeTraded(allPositions);
-
+
// Calculate win rate
int averageWinRate = 0;
if (wins + losses > 0)
{
averageWinRate = (wins * 100) / (wins + losses);
}
-
+
// Add to agent summaries
var agentSummary = new AgentSummaryViewModel
{
- Username = user.Name,
+ AgentName = user.AgentName,
TotalPnL = totalPnL,
PnLLast24h = pnlLast24h,
TotalROI = totalROI,
@@ -423,26 +516,26 @@ public class DataController : ControllerBase
TotalVolume = totalVolume,
VolumeLast24h = volumeLast24h
};
-
+
summary.AgentSummaries.Add(agentSummary);
-
+
// Add to platform totals
totalPlatformPnL += totalPnL;
totalPlatformVolume += totalVolume;
totalPlatformVolumeLast24h += volumeLast24h;
}
-
+
// Set the platform totals
summary.TotalPlatformPnL = totalPlatformPnL;
summary.TotalPlatformVolume = totalPlatformVolume;
summary.TotalPlatformVolumeLast24h = totalPlatformVolumeLast24h;
-
+
// Sort agent summaries by total PnL (highest first)
summary.AgentSummaries = summary.AgentSummaries.OrderByDescending(a => a.TotalPnL).ToList();
-
+
// Cache the results for 5 minutes
_cacheService.SaveValue(cacheKey, summary, TimeSpan.FromMinutes(5));
-
+
return Ok(summary);
}
}
\ No newline at end of file
diff --git a/src/Managing.Api/Controllers/UserController.cs b/src/Managing.Api/Controllers/UserController.cs
index 632269f..acbfcd0 100644
--- a/src/Managing.Api/Controllers/UserController.cs
+++ b/src/Managing.Api/Controllers/UserController.cs
@@ -1,6 +1,7 @@
using Managing.Api.Authorization;
using Managing.Api.Models.Requests;
using Managing.Application.Abstractions.Services;
+using Managing.Domain.Users;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -12,10 +13,9 @@ namespace Managing.Api.Controllers;
[ApiController]
[Route("[controller]")]
[Produces("application/json")]
-public class UserController : ControllerBase
+public class UserController : BaseController
{
private IConfiguration _config;
- private readonly IUserService _userService;
private readonly IJwtUtils _jwtUtils;
///
@@ -25,9 +25,9 @@ public class UserController : ControllerBase
/// Service for user-related operations.
/// Utility for JWT token operations.
public UserController(IConfiguration config, IUserService userService, IJwtUtils jwtUtils)
+ : base(userService)
{
_config = config;
- _userService = userService;
_jwtUtils = jwtUtils;
}
@@ -49,5 +49,30 @@ public class UserController : ControllerBase
}
return Unauthorized();
+ }
+
+ ///
+ /// Gets the current user's information.
+ ///
+ /// The current user's information.
+ [HttpGet]
+ public async Task> GetCurrentUser()
+ {
+ var user = await base.GetUser();
+ return Ok(user);
}
-}
\ No newline at end of file
+
+ ///
+ /// Updates the agent name for the current user.
+ ///
+ /// The new agent name to set.
+ /// The updated user with the new agent name.
+ [HttpPut("agent-name")]
+ public async Task> UpdateAgentName([FromBody] string agentName)
+ {
+ var user = await GetUser();
+ var updatedUser = await _userService.UpdateAgentName(user, agentName);
+ return Ok(updatedUser);
+ }
+}
+
\ No newline at end of file
diff --git a/src/Managing.Api/Models/Requests/StartBotRequest.cs b/src/Managing.Api/Models/Requests/StartBotRequest.cs
index e60de8a..ed714f1 100644
--- a/src/Managing.Api/Models/Requests/StartBotRequest.cs
+++ b/src/Managing.Api/Models/Requests/StartBotRequest.cs
@@ -20,5 +20,12 @@ namespace Managing.Api.Models.Requests
[Required]
[Range(10.00, double.MaxValue, ErrorMessage = "Initial trading balance must be greater than ten")]
public decimal InitialTradingBalance { get; set; }
+
+ ///
+ /// Cooldown period in minutes between trades
+ ///
+ [Required]
+ [Range(1, 1440, ErrorMessage = "Cooldown period must be between 1 and 1440 minutes (24 hours)")]
+ public decimal CooldownPeriod { get; set; } = 1; // Default to 1 minute if not specified
}
}
\ No newline at end of file
diff --git a/src/Managing.Api/Models/Responses/AgentSummaryViewModel.cs b/src/Managing.Api/Models/Responses/AgentSummaryViewModel.cs
index 590839d..f40c09c 100644
--- a/src/Managing.Api/Models/Responses/AgentSummaryViewModel.cs
+++ b/src/Managing.Api/Models/Responses/AgentSummaryViewModel.cs
@@ -6,9 +6,9 @@ namespace Managing.Api.Models.Responses
public class AgentSummaryViewModel
{
///
- /// Username of the agent
+ /// AgentName of the agent
///
- public string Username { get; set; }
+ public string AgentName { get; set; }
///
/// Total profit and loss in USD
diff --git a/src/Managing.Api/Models/Responses/TickerInfos.cs b/src/Managing.Api/Models/Responses/TickerInfos.cs
new file mode 100644
index 0000000..d0571a4
--- /dev/null
+++ b/src/Managing.Api/Models/Responses/TickerInfos.cs
@@ -0,0 +1,9 @@
+using Managing.Common;
+
+namespace Managing.Api.Models.Responses;
+
+public class TickerInfos
+{
+ public Enums.Ticker Ticker { get; set; }
+ public string ImageUrl { get; set; }
+}
\ No newline at end of file
diff --git a/src/Managing.Api/Models/Responses/TradingBot.cs b/src/Managing.Api/Models/Responses/TradingBot.cs
index a53d6f5..5ff58cb 100644
--- a/src/Managing.Api/Models/Responses/TradingBot.cs
+++ b/src/Managing.Api/Models/Responses/TradingBot.cs
@@ -1,8 +1,8 @@
-using Managing.Domain.Candles;
+using System.ComponentModel.DataAnnotations;
+using Managing.Domain.Candles;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Strategies;
using Managing.Domain.Trades;
-using System.ComponentModel.DataAnnotations;
using static Managing.Common.Enums;
namespace Managing.Api.Models.Responses
@@ -23,5 +23,7 @@ namespace Managing.Api.Models.Responses
[Required] public BotType BotType { get; internal set; }
[Required] public string AccountName { get; internal set; }
[Required] public MoneyManagement MoneyManagement { get; internal set; }
+ [Required] public string Identifier { get; set; }
+ [Required] public string AgentName { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Managing.Api/Program.cs b/src/Managing.Api/Program.cs
index a662a02..5113b21 100644
--- a/src/Managing.Api/Program.cs
+++ b/src/Managing.Api/Program.cs
@@ -4,6 +4,7 @@ using HealthChecks.UI.Client;
using Managing.Api.Authorization;
using Managing.Api.Filters;
using Managing.Api.HealthChecks;
+using Managing.Api.Workers;
using Managing.Application.Hubs;
using Managing.Bootstrap;
using Managing.Common;
@@ -195,7 +196,10 @@ builder.Services.AddSwaggerGen(options =>
});
builder.WebHost.SetupDiscordBot();
-// builder.Services.AddHostedService();
+if (builder.Configuration.GetValue("EnableBotManager", false))
+{
+ builder.Services.AddHostedService();
+}
// App
var app = builder.Build();
diff --git a/src/Managing.Api/appsettings.SandboxLocal.json b/src/Managing.Api/appsettings.SandboxLocal.json
index 8de4c89..3a889e5 100644
--- a/src/Managing.Api/appsettings.SandboxLocal.json
+++ b/src/Managing.Api/appsettings.SandboxLocal.json
@@ -24,5 +24,6 @@
"ElasticConfiguration": {
"Uri": "http://elasticsearch:9200"
},
- "AllowedHosts": "*"
+ "AllowedHosts": "*",
+ "EnableBotManager": true
}
\ No newline at end of file
diff --git a/src/Managing.Application.Abstractions/Repositories/IUserRepository.cs b/src/Managing.Application.Abstractions/Repositories/IUserRepository.cs
index 5cd6356..6876d6f 100644
--- a/src/Managing.Application.Abstractions/Repositories/IUserRepository.cs
+++ b/src/Managing.Application.Abstractions/Repositories/IUserRepository.cs
@@ -4,6 +4,7 @@ namespace Managing.Application.Abstractions.Repositories;
public interface IUserRepository
{
+ Task GetUserByAgentNameAsync(string agentName);
Task GetUserByNameAsync(string name);
Task InsertUserAsync(User user);
Task UpdateUser(User user);
diff --git a/src/Managing.Application.Abstractions/Services/IBacktester.cs b/src/Managing.Application.Abstractions/Services/IBacktester.cs
index 6666efa..6e0e64a 100644
--- a/src/Managing.Application.Abstractions/Services/IBacktester.cs
+++ b/src/Managing.Application.Abstractions/Services/IBacktester.cs
@@ -22,7 +22,9 @@ namespace Managing.Application.Abstractions.Services
User user = null,
bool isForWatchingOnly = false,
bool save = false,
- List? initialCandles = null);
+ List? initialCandles = null,
+ decimal cooldownPeriod = 1,
+ int maxLossStreak = 0);
Task RunFlippingBotBacktest(
Account account,
@@ -36,16 +38,34 @@ namespace Managing.Application.Abstractions.Services
User user = null,
bool isForWatchingOnly = false,
bool save = false,
- List? initialCandles = null);
+ List? initialCandles = null,
+ decimal cooldownPeriod = 1,
+ int maxLossStreak = 0);
bool DeleteBacktest(string id);
bool DeleteBacktests();
- Task RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario,
- Timeframe timeframe, List candles, decimal balance, User user = null);
+ Task RunScalpingBotBacktest(
+ Account account,
+ MoneyManagement moneyManagement,
+ Scenario scenario,
+ Timeframe timeframe,
+ List candles,
+ decimal balance,
+ User user = null,
+ decimal cooldownPeriod = 1,
+ int maxLossStreak = 0);
- Task RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario,
- Timeframe timeframe, List candles, decimal balance, User user = null);
+ Task RunFlippingBotBacktest(
+ Account account,
+ MoneyManagement moneyManagement,
+ Scenario scenario,
+ Timeframe timeframe,
+ List candles,
+ decimal balance,
+ User user = null,
+ decimal cooldownPeriod = 1,
+ int maxLossStreak = 0);
// User-specific operations
Task> GetBacktestsByUser(User user);
diff --git a/src/Managing.Application.Abstractions/Services/IUserService.cs b/src/Managing.Application.Abstractions/Services/IUserService.cs
index 36349a5..be8e8ad 100644
--- a/src/Managing.Application.Abstractions/Services/IUserService.cs
+++ b/src/Managing.Application.Abstractions/Services/IUserService.cs
@@ -6,4 +6,6 @@ public interface IUserService
{
Task Authenticate(string name, string address, string message, string signature);
Task GetUserByAddressAsync(string address);
+ Task UpdateAgentName(User user, string agentName);
+ User GetUser(string name);
}
diff --git a/src/Managing.Application.Workers/BaseWorker.cs b/src/Managing.Application.Workers/BaseWorker.cs
index ae83927..6f9e28f 100644
--- a/src/Managing.Application.Workers/BaseWorker.cs
+++ b/src/Managing.Application.Workers/BaseWorker.cs
@@ -50,7 +50,7 @@ public abstract class BaseWorker : BackgroundService where T : class
{
worker = await _workerService.GetWorker(_workerType);
- if (worker.IsActive)
+ if (worker.IsActive || worker.WorkerType.Equals(WorkerType.BotManager))
{
await Run(cancellationToken);
_executionCount++;
diff --git a/src/Managing.Application/Abstractions/IBotFactory.cs b/src/Managing.Application/Abstractions/IBotFactory.cs
index 9c91569..25a7f97 100644
--- a/src/Managing.Application/Abstractions/IBotFactory.cs
+++ b/src/Managing.Application/Abstractions/IBotFactory.cs
@@ -1,16 +1,15 @@
-using Managing.Domain.Bots;
-using Managing.Domain.MoneyManagements;
+using Managing.Application.Bots;
+using Managing.Domain.Bots;
using Managing.Domain.Workflows;
-using static Managing.Common.Enums;
namespace Managing.Application.Abstractions
{
public interface IBotFactory
{
- ITradingBot CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly, decimal initialTradingBalance);
- ITradingBot CreateBacktestScalpingBot(string accountName, MoneyManagement moneyManagement, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly, decimal initialTradingBalance);
- ITradingBot CreateFlippingBot(string accountName, MoneyManagement moneyManagement, string name, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly, decimal initialTradingBalance);
- ITradingBot CreateBacktestFlippingBot(string accountName, MoneyManagement moneyManagement, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly, decimal initialTradingBalance);
IBot CreateSimpleBot(string botName, Workflow workflow);
+ ITradingBot CreateScalpingBot(TradingBotConfig config);
+ ITradingBot CreateBacktestScalpingBot(TradingBotConfig config);
+ ITradingBot CreateFlippingBot(TradingBotConfig config);
+ ITradingBot CreateBacktestFlippingBot(TradingBotConfig config);
}
}
diff --git a/src/Managing.Application/Abstractions/IBotService.cs b/src/Managing.Application/Abstractions/IBotService.cs
index 8ad7358..93ede4b 100644
--- a/src/Managing.Application/Abstractions/IBotService.cs
+++ b/src/Managing.Application/Abstractions/IBotService.cs
@@ -1,38 +1,30 @@
-using Managing.Common;
+using Managing.Application.Bots;
using Managing.Domain.Bots;
-using Managing.Domain.MoneyManagements;
using Managing.Domain.Users;
using Managing.Domain.Workflows;
+using static Managing.Common.Enums;
namespace Managing.Application.Abstractions;
public interface IBotService
{
- void SaveOrUpdateBotBackup(BotBackup botBackup);
- void SaveOrUpdateBotBackup(User user, string identifier, Enums.BotType botType, string data);
+ void SaveOrUpdateBotBackup(User user, string identifier, BotType botType, BotStatus status, string data);
void AddSimpleBotToCache(IBot bot);
void AddTradingBotToCache(ITradingBot bot);
List GetActiveBots();
IEnumerable GetSavedBots();
void StartBotFromBackup(BotBackup backupBot);
- BotBackup GetBotBackup(string name);
+ BotBackup GetBotBackup(string identifier);
- ITradingBot CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name, Enums.Ticker ticker,
- string scenario, Enums.Timeframe interval, bool isForWatchingOnly, decimal initialTradingAmount);
-
- ITradingBot CreateBacktestScalpingBot(string accountName, MoneyManagement moneyManagement, Enums.Ticker ticker,
- string scenario, Enums.Timeframe interval, bool isForWatchingOnly, decimal initialTradingAmount);
-
- ITradingBot CreateFlippingBot(string accountName, MoneyManagement moneyManagement, string name, Enums.Ticker ticker,
- string scenario, Enums.Timeframe interval, bool isForWatchingOnly, decimal initialTradingAmount);
-
- ITradingBot CreateBacktestFlippingBot(string accountName, MoneyManagement moneyManagement, Enums.Ticker ticker,
- string scenario, Enums.Timeframe interval, bool isForWatchingOnly, decimal initialTradingAmount);
+ ITradingBot CreateScalpingBot(TradingBotConfig config);
+ ITradingBot CreateBacktestScalpingBot(TradingBotConfig config);
+ ITradingBot CreateFlippingBot(TradingBotConfig config);
+ ITradingBot CreateBacktestFlippingBot(TradingBotConfig config);
IBot CreateSimpleBot(string botName, Workflow workflow);
- Task StopBot(string requestName);
- Task DeleteBot(string requestName);
- Task RestartBot(string requestName);
+ Task StopBot(string botName);
+ Task DeleteBot(string botName);
+ Task RestartBot(string botName);
void DeleteBotBackup(string backupBotName);
void ToggleIsForWatchingOnly(string botName);
}
\ No newline at end of file
diff --git a/src/Managing.Application/Abstractions/ITradingBot.cs b/src/Managing.Application/Abstractions/ITradingBot.cs
index 3bc5ee6..c954780 100644
--- a/src/Managing.Application/Abstractions/ITradingBot.cs
+++ b/src/Managing.Application/Abstractions/ITradingBot.cs
@@ -1,33 +1,32 @@
-using Managing.Core.FixedSizedQueue;
+using Managing.Application.Bots;
+using Managing.Core.FixedSizedQueue;
+using Managing.Domain.Accounts;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
-using Managing.Domain.MoneyManagements;
+using Managing.Domain.Scenarios;
using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Base;
using Managing.Domain.Trades;
-using Managing.Domain.Users;
using static Managing.Common.Enums;
namespace Managing.Application.Abstractions
{
public interface ITradingBot : IBot
{
- HashSet Signals { get; set; }
- List Positions { get; set; }
+ TradingBotConfig Config { get; set; }
+ Account Account { get; set; }
+ HashSet Strategies { get; set; }
FixedSizeQueue OptimizedCandles { get; set; }
HashSet Candles { get; set; }
- Timeframe Timeframe { get; set; }
- HashSet Strategies { get; set; }
- Ticker Ticker { get; }
- string ScenarioName { get; }
- string AccountName { get; }
- bool IsForWatchingOnly { get; set; }
- MoneyManagement MoneyManagement { get; set; }
- BotType BotType { get; set; }
+ HashSet Signals { get; set; }
+ List Positions { get; set; }
Dictionary WalletBalances { get; set; }
Dictionary StrategiesValues { get; set; }
- User User { get; set; }
- string Identifier { get; set; }
+ DateTime StartupTime { get; set; }
+ DateTime PreloadSince { get; set; }
+ int PreloadedCandlesCount { get; set; }
+ decimal Fee { get; set; }
+ Scenario Scenario { get; set; }
Task Run();
Task ToggleIsForWatchOnly();
@@ -38,5 +37,6 @@ namespace Managing.Application.Abstractions
void LoadScenario(string scenarioName);
void UpdateStrategiesValues();
Task LoadAccount();
+ Task OpenPositionManually(TradeDirection direction);
}
}
\ No newline at end of file
diff --git a/src/Managing.Application/Backtesting/Backtester.cs b/src/Managing.Application/Backtesting/Backtester.cs
index 9a52523..72fb302 100644
--- a/src/Managing.Application/Backtesting/Backtester.cs
+++ b/src/Managing.Application/Backtesting/Backtester.cs
@@ -1,6 +1,7 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
+using Managing.Application.Bots;
using Managing.Core;
using Managing.Core.FixedSizedQueue;
using Managing.Domain.Accounts;
@@ -64,10 +65,26 @@ namespace Managing.Application.Backtesting
User user = null,
bool isForWatchingOnly = false,
bool save = false,
- List initialCandles = null)
+ List? initialCandles = null,
+ decimal cooldownPeriod = 1,
+ int maxLossStreak = 0)
{
- var scalpingBot = _botFactory.CreateBacktestScalpingBot(account.Name, moneyManagement, ticker, "scenario",
- timeframe, isForWatchingOnly, balance);
+ var config = new TradingBotConfig
+ {
+ AccountName = account.Name,
+ MoneyManagement = moneyManagement,
+ Ticker = ticker,
+ ScenarioName = scenario.Name,
+ Timeframe = timeframe,
+ IsForWatchingOnly = isForWatchingOnly,
+ BotTradingBalance = balance,
+ BotType = BotType.ScalpingBot,
+ IsForBacktest = true,
+ CooldownPeriod = cooldownPeriod,
+ MaxLossStreak = maxLossStreak
+ };
+
+ var scalpingBot = _botFactory.CreateBacktestScalpingBot(config);
scalpingBot.LoadScenario(scenario.Name);
await scalpingBot.LoadAccount();
var candles = initialCandles ?? GetCandles(account, ticker, timeframe, startDate, endDate);
@@ -91,21 +108,6 @@ namespace Managing.Application.Backtesting
return result;
}
- private List GetCandles(Account account, Ticker ticker, Timeframe timeframe,
- DateTime startDate, DateTime endDate)
- {
- List candles;
-
- // Use specific date range
- candles = _exchangeService.GetCandlesInflux(account.Exchange, ticker,
- startDate, timeframe, endDate).Result;
-
- if (candles == null || candles.Count == 0)
- throw new Exception($"No candles for {ticker} on {account.Exchange}");
-
- return candles;
- }
-
public async Task RunFlippingBotBacktest(
Account account,
MoneyManagement moneyManagement,
@@ -118,10 +120,26 @@ namespace Managing.Application.Backtesting
User user = null,
bool isForWatchingOnly = false,
bool save = false,
- List initialCandles = null)
+ List? initialCandles = null,
+ decimal cooldownPeriod = 1,
+ int maxLossStreak = 0)
{
- var flippingBot = _botFactory.CreateBacktestFlippingBot(account.Name, moneyManagement, ticker, "scenario",
- timeframe, false, balance);
+ var config = new TradingBotConfig
+ {
+ AccountName = account.Name,
+ MoneyManagement = moneyManagement,
+ Ticker = ticker,
+ ScenarioName = scenario.Name,
+ Timeframe = timeframe,
+ IsForWatchingOnly = isForWatchingOnly,
+ BotTradingBalance = balance,
+ BotType = BotType.FlippingBot,
+ IsForBacktest = true,
+ CooldownPeriod = cooldownPeriod,
+ MaxLossStreak = maxLossStreak
+ };
+
+ var flippingBot = _botFactory.CreateBacktestFlippingBot(config);
flippingBot.LoadScenario(scenario.Name);
await flippingBot.LoadAccount();
@@ -146,13 +164,34 @@ namespace Managing.Application.Backtesting
return result;
}
- public async Task RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement,
+ public async Task RunScalpingBotBacktest(
+ Account account,
+ MoneyManagement moneyManagement,
Scenario scenario,
- Timeframe timeframe, List candles, decimal balance, User user = null)
+ Timeframe timeframe,
+ List candles,
+ decimal balance,
+ User user = null,
+ decimal cooldownPeriod = 1,
+ int maxLossStreak = 0)
{
var ticker = MiscExtensions.ParseEnum(candles.FirstOrDefault().Ticker);
- var bot = _botFactory.CreateBacktestScalpingBot(account.Name, moneyManagement, ticker, "scenario",
- timeframe, false, balance);
+ var config = new TradingBotConfig
+ {
+ AccountName = account.Name,
+ MoneyManagement = moneyManagement,
+ Ticker = ticker,
+ ScenarioName = scenario.Name,
+ Timeframe = timeframe,
+ IsForWatchingOnly = false,
+ BotTradingBalance = balance,
+ BotType = BotType.ScalpingBot,
+ IsForBacktest = true,
+ CooldownPeriod = cooldownPeriod,
+ MaxLossStreak = maxLossStreak
+ };
+
+ var bot = _botFactory.CreateBacktestScalpingBot(config);
bot.LoadScenario(scenario.Name);
await bot.LoadAccount();
@@ -167,13 +206,34 @@ namespace Managing.Application.Backtesting
return result;
}
- public async Task RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement,
+ public async Task RunFlippingBotBacktest(
+ Account account,
+ MoneyManagement moneyManagement,
Scenario scenario,
- Timeframe timeframe, List candles, decimal balance, User user = null)
+ Timeframe timeframe,
+ List candles,
+ decimal balance,
+ User user = null,
+ decimal cooldownPeriod = 1,
+ int maxLossStreak = 0)
{
var ticker = MiscExtensions.ParseEnum(candles.FirstOrDefault().Ticker);
- var bot = _botFactory.CreateBacktestFlippingBot(account.Name, moneyManagement, ticker, "scenario",
- timeframe, false, balance);
+ var config = new TradingBotConfig
+ {
+ AccountName = account.Name,
+ MoneyManagement = moneyManagement,
+ Ticker = ticker,
+ ScenarioName = scenario.Name,
+ Timeframe = timeframe,
+ IsForWatchingOnly = false,
+ BotTradingBalance = balance,
+ BotType = BotType.FlippingBot,
+ IsForBacktest = true,
+ CooldownPeriod = cooldownPeriod,
+ MaxLossStreak = maxLossStreak
+ };
+
+ var bot = _botFactory.CreateBacktestFlippingBot(config);
bot.LoadScenario(scenario.Name);
await bot.LoadAccount();
@@ -188,6 +248,21 @@ namespace Managing.Application.Backtesting
return result;
}
+ private List GetCandles(Account account, Ticker ticker, Timeframe timeframe,
+ DateTime startDate, DateTime endDate)
+ {
+ List candles;
+
+ // Use specific date range
+ candles = _exchangeService.GetCandlesInflux(account.Exchange, ticker,
+ startDate, timeframe, endDate).Result;
+
+ if (candles == null || candles.Count == 0)
+ throw new Exception($"No candles for {ticker} on {account.Exchange}");
+
+ return candles;
+ }
+
private Backtest GetBacktestingResult(
Ticker ticker,
Scenario scenario,
@@ -239,7 +314,7 @@ namespace Managing.Application.Backtesting
var score = BacktestScorer.CalculateTotalScore(scoringParams);
var result = new Backtest(ticker, scenario.Name, bot.Positions, bot.Signals.ToList(), timeframe, candles,
- bot.BotType, account.Name)
+ bot.Config.BotType, account.Name)
{
FinalPnl = finalPnl,
WinRate = winRate,
diff --git a/src/Managing.Application/Bots/Base/BotFactory.cs b/src/Managing.Application/Bots/Base/BotFactory.cs
index 2cc03c2..fffa32d 100644
--- a/src/Managing.Application/Bots/Base/BotFactory.cs
+++ b/src/Managing.Application/Bots/Base/BotFactory.cs
@@ -1,7 +1,6 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Domain.Bots;
-using Managing.Domain.MoneyManagements;
using Managing.Domain.Workflows;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
@@ -38,82 +37,58 @@ namespace Managing.Application.Bots.Base
return new SimpleBot(botName, _tradingBotLogger, workflow, _botService);
}
- ITradingBot IBotFactory.CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly, decimal initialTradingBalance)
+ ITradingBot IBotFactory.CreateScalpingBot(TradingBotConfig config)
{
+ config.BotType = BotType.ScalpingBot;
return new ScalpingBot(
- accountName,
- moneyManagement,
- name,
- scenario,
_exchangeService,
- ticker,
- _tradingService,
_tradingBotLogger,
- interval,
+ _tradingService,
_accountService,
_messengerService,
_botService,
- initialTradingBalance,
- isForWatchingOnly: isForWatchingOnly);
+ config);
}
- ITradingBot IBotFactory.CreateBacktestScalpingBot(string accountName, MoneyManagement moneyManagement, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly, decimal initialTradingBalance)
+ ITradingBot IBotFactory.CreateBacktestScalpingBot(TradingBotConfig config)
{
+ config.BotType = BotType.ScalpingBot;
+ config.IsForBacktest = true;
return new ScalpingBot(
- accountName,
- moneyManagement,
- "BacktestBot",
- scenario,
_exchangeService,
- ticker,
- _tradingService,
_tradingBotLogger,
- interval,
+ _tradingService,
_accountService,
_messengerService,
_botService,
- initialTradingBalance,
- isForBacktest: true,
- isForWatchingOnly: isForWatchingOnly);
+ config);
}
- public ITradingBot CreateFlippingBot(string accountName, MoneyManagement moneyManagement, string name, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly, decimal initialTradingBalance)
+ public ITradingBot CreateFlippingBot(TradingBotConfig config)
{
+ config.BotType = BotType.FlippingBot;
return new FlippingBot(
- accountName,
- moneyManagement,
- name,
- scenario,
_exchangeService,
- ticker,
- _tradingService,
_tradingBotLogger,
- interval,
+ _tradingService,
_accountService,
_messengerService,
_botService,
- initialTradingBalance,
- isForWatchingOnly: isForWatchingOnly);
+ config);
}
- public ITradingBot CreateBacktestFlippingBot(string accountName, MoneyManagement moneyManagement, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly, decimal initialTradingBalance)
+ public ITradingBot CreateBacktestFlippingBot(TradingBotConfig config)
{
+ config.BotType = BotType.FlippingBot;
+ config.IsForBacktest = true;
return new FlippingBot(
- accountName,
- moneyManagement,
- "BacktestBot",
- scenario,
_exchangeService,
- ticker,
- _tradingService,
_tradingBotLogger,
- interval,
+ _tradingService,
_accountService,
_messengerService,
_botService,
- initialTradingBalance,
- isForBacktest: true,
- isForWatchingOnly: isForWatchingOnly);
+ config);
}
}
}
diff --git a/src/Managing.Application/Bots/FlippingBot.cs b/src/Managing.Application/Bots/FlippingBot.cs
index d740d96..7c2c2b8 100644
--- a/src/Managing.Application/Bots/FlippingBot.cs
+++ b/src/Managing.Application/Bots/FlippingBot.cs
@@ -1,6 +1,5 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
-using Managing.Domain.MoneyManagements;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
@@ -8,39 +7,18 @@ namespace Managing.Application.Bots
{
public class FlippingBot : TradingBot
{
- public FlippingBot(string accountName,
- MoneyManagement moneyManagement,
- string name,
- string scenarioName,
+ public FlippingBot(
IExchangeService exchangeService,
- Ticker ticker,
- ITradingService tradingService,
ILogger logger,
- Timeframe timeframe,
+ ITradingService tradingService,
IAccountService accountService,
IMessengerService messengerService,
IBotService botService,
- decimal initialTradingBalance,
- bool isForBacktest = false,
- bool isForWatchingOnly = false)
- : base(accountName,
- moneyManagement,
- name,
- ticker,
- scenarioName,
- exchangeService,
- logger,
- tradingService,
- timeframe,
- accountService,
- messengerService,
- botService,
- initialTradingBalance,
- isForBacktest,
- isForWatchingOnly,
- flipPosition: true)
+ TradingBotConfig config)
+ : base(exchangeService, logger, tradingService, accountService, messengerService, botService, config)
{
- BotType = BotType.FlippingBot;
+ Config.BotType = BotType.FlippingBot;
+ Config.FlipPosition = true;
}
public sealed override void Start()
diff --git a/src/Managing.Application/Bots/ScalpingBot.cs b/src/Managing.Application/Bots/ScalpingBot.cs
index a0611fd..f72e756 100644
--- a/src/Managing.Application/Bots/ScalpingBot.cs
+++ b/src/Managing.Application/Bots/ScalpingBot.cs
@@ -1,6 +1,5 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
-using Managing.Domain.MoneyManagements;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
@@ -8,38 +7,17 @@ namespace Managing.Application.Bots
{
public class ScalpingBot : TradingBot
{
- public ScalpingBot(string accountName,
- MoneyManagement moneyManagement,
- string name,
- string scenarioName,
+ public ScalpingBot(
IExchangeService exchangeService,
- Ticker ticker,
- ITradingService tradingService,
ILogger logger,
- Timeframe timeframe,
+ ITradingService tradingService,
IAccountService accountService,
IMessengerService messengerService,
IBotService botService,
- decimal initialTradingBalance,
- bool isForBacktest = false,
- bool isForWatchingOnly = false)
- : base(accountName,
- moneyManagement,
- name,
- ticker,
- scenarioName,
- exchangeService,
- logger,
- tradingService,
- timeframe,
- accountService,
- messengerService,
- botService,
- initialTradingBalance,
- isForBacktest,
- isForWatchingOnly)
+ TradingBotConfig config)
+ : base(exchangeService, logger, tradingService, accountService, messengerService, botService, config)
{
- BotType = BotType.ScalpingBot;
+ Config.BotType = BotType.ScalpingBot;
}
public sealed override void Start()
diff --git a/src/Managing.Application/Bots/SimpleBot.cs b/src/Managing.Application/Bots/SimpleBot.cs
index a392d25..e2e03a6 100644
--- a/src/Managing.Application/Bots/SimpleBot.cs
+++ b/src/Managing.Application/Bots/SimpleBot.cs
@@ -45,7 +45,7 @@ namespace Managing.Application.Bots
public override void SaveBackup()
{
var data = JsonConvert.SerializeObject(_workflow);
- _botService.SaveOrUpdateBotBackup(User, Identifier, BotType.SimpleBot, data);
+ _botService.SaveOrUpdateBotBackup(User, Identifier, BotType.SimpleBot, Status, data);
}
public override void LoadBackup(BotBackup backup)
diff --git a/src/Managing.Application/Bots/TradingBot.cs b/src/Managing.Application/Bots/TradingBot.cs
index f265309..8af7289 100644
--- a/src/Managing.Application/Bots/TradingBot.cs
+++ b/src/Managing.Application/Bots/TradingBot.cs
@@ -28,76 +28,47 @@ public class TradingBot : Bot, ITradingBot
private readonly ITradingService TradingService;
private readonly IBotService BotService;
+ public TradingBotConfig Config { get; set; }
public Account Account { get; set; }
public HashSet Strategies { get; set; }
public FixedSizeQueue OptimizedCandles { get; set; }
public HashSet Candles { get; set; }
public HashSet Signals { get; set; }
public List Positions { get; set; }
- public Ticker Ticker { get; set; }
- public string ScenarioName { get; set; }
- public string AccountName { get; set; }
- public MoneyManagement MoneyManagement { get; set; }
- public Timeframe Timeframe { get; set; }
- public bool IsForBacktest { get; set; }
- public DateTime PreloadSince { get; set; }
- public bool IsForWatchingOnly { get; set; }
- public bool FlipPosition { get; set; }
- public int PreloadedCandlesCount { get; set; }
- public BotType BotType { get; set; }
- public decimal Fee { get; set; }
- public Scenario Scenario { get; set; }
public Dictionary WalletBalances { get; set; }
public Dictionary StrategiesValues { get; set; }
public DateTime StartupTime { get; set; }
-
- ///
- /// The dedicated trading balance for this bot in USD
- ///
- public decimal BotTradingBalance { get; set; }
+ public DateTime PreloadSince { get; set; }
+ public int PreloadedCandlesCount { get; set; }
+ public decimal Fee { get; set; }
+ public Scenario Scenario { get; set; }
public TradingBot(
- string accountName,
- MoneyManagement moneyManagement,
- string name,
- Ticker ticker,
- string scenarioName,
IExchangeService exchangeService,
ILogger logger,
ITradingService tradingService,
- Timeframe timeframe,
IAccountService accountService,
IMessengerService messengerService,
IBotService botService,
- decimal initialTradingBalance,
- bool isForBacktest = false,
- bool isForWatchingOnly = false,
- bool flipPosition = false)
- : base(name)
+ TradingBotConfig config
+ )
+ : base(config.AccountName)
{
ExchangeService = exchangeService;
AccountService = accountService;
MessengerService = messengerService;
TradingService = tradingService;
BotService = botService;
+ Logger = logger;
- if (initialTradingBalance <= Constants.GMX.Config.MinimumPositionAmount)
+ if (config.BotTradingBalance <= Constants.GMX.Config.MinimumPositionAmount)
{
throw new ArgumentException(
$"Initial trading balance must be greater than {Constants.GMX.Config.MinimumPositionAmount}",
- nameof(initialTradingBalance));
+ nameof(config.BotTradingBalance));
}
- IsForWatchingOnly = isForWatchingOnly;
- FlipPosition = flipPosition;
- AccountName = accountName;
- MoneyManagement = moneyManagement;
- Ticker = ticker;
- ScenarioName = scenarioName;
- Timeframe = timeframe;
- IsForBacktest = isForBacktest;
- Logger = logger;
- BotTradingBalance = initialTradingBalance;
+ Config = config;
Strategies = new HashSet();
Signals = new HashSet();
@@ -107,10 +78,10 @@ public class TradingBot : Bot, ITradingBot
WalletBalances = new Dictionary();
StrategiesValues = new Dictionary();
- if (!isForBacktest)
+ if (!Config.IsForBacktest)
{
- Interval = CandleExtensions.GetIntervalFromTimeframe(timeframe);
- PreloadSince = CandleExtensions.GetBotPreloadSinceFromTimeframe(timeframe);
+ Interval = CandleExtensions.GetIntervalFromTimeframe(Config.Timeframe);
+ PreloadSince = CandleExtensions.GetBotPreloadSinceFromTimeframe(Config.Timeframe);
}
}
@@ -120,9 +91,9 @@ public class TradingBot : Bot, ITradingBot
// Load account synchronously
await LoadAccount();
- if (!IsForBacktest)
+ if (!Config.IsForBacktest)
{
- LoadScenario(ScenarioName);
+ LoadScenario(Config.ScenarioName);
await PreloadCandles();
await CancelAllOrders();
@@ -146,10 +117,10 @@ public class TradingBot : Bot, ITradingBot
public async Task LoadAccount()
{
- var account = await AccountService.GetAccount(AccountName, false, false);
+ var account = await AccountService.GetAccount(Config.AccountName, false, false);
if (account == null)
{
- Logger.LogWarning($"No account found for this {AccountName}");
+ Logger.LogWarning($"No account found for this {Config.AccountName}");
Stop();
}
else
@@ -185,7 +156,7 @@ public class TradingBot : Bot, ITradingBot
public async Task Run()
{
- if (!IsForBacktest)
+ if (!Config.IsForBacktest)
{
// Check broker balance before running
var balance = await ExchangeService.GetBalance(Account, false);
@@ -200,25 +171,25 @@ public class TradingBot : Bot, ITradingBot
Logger.LogInformation($"____________________{Name}____________________");
Logger.LogInformation(
- $"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Last candle : {OptimizedCandles.Last().Date} - Bot : {Name} - Type {BotType} - Ticker : {Ticker}");
+ $"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Last candle : {OptimizedCandles.Last().Date} - Bot : {Name} - Type {Config.BotType} - Ticker : {Config.Ticker}");
}
var previousLastCandle = OptimizedCandles.LastOrDefault();
- if (!IsForBacktest)
+ if (!Config.IsForBacktest)
await UpdateCandles();
var currentLastCandle = OptimizedCandles.LastOrDefault();
- if (currentLastCandle != previousLastCandle || IsForBacktest)
+ if (currentLastCandle != previousLastCandle || Config.IsForBacktest)
await UpdateSignals(OptimizedCandles);
else
- Logger.LogInformation($"No need to update signals for {Ticker}");
+ Logger.LogInformation($"No need to update signals for {Config.Ticker}");
- if (!IsForWatchingOnly)
+ if (!Config.IsForWatchingOnly)
await ManagePositions();
- if (!IsForBacktest)
+ if (!Config.IsForBacktest)
{
SaveBackup();
UpdateStrategiesValues();
@@ -248,7 +219,8 @@ public class TradingBot : Bot, ITradingBot
if (OptimizedCandles.Any())
return;
- var candles = await ExchangeService.GetCandlesInflux(Account.Exchange, Ticker, PreloadSince, Timeframe);
+ var candles =
+ await ExchangeService.GetCandlesInflux(Account.Exchange, Config.Ticker, PreloadSince, Config.Timeframe);
foreach (var candle in candles.Where(c => c.Date < DateTime.Now.ToUniversalTime()))
{
@@ -272,25 +244,22 @@ public class TradingBot : Bot, ITradingBot
await AddSignal(signal);
}
-
private async Task AddSignal(Signal signal)
{
- // if (!IsForBacktest)
- // TradingService.InsertSignal(signal);
-
- if (IsForWatchingOnly || (ExecutionCount < 1 && !IsForBacktest))
+ if (Config.IsForWatchingOnly || (ExecutionCount < 1 && !Config.IsForBacktest))
signal.Status = SignalStatus.Expired;
Signals.Add(signal);
- var signalText = $"{ScenarioName} trigger a signal. Signal told you " +
- $"to {signal.Direction} {Ticker} on {Timeframe}. The confidence in this signal is {signal.Confidence}. Identifier : {signal.Identifier}";
+ var signalText = $"{Config.ScenarioName} trigger a signal. Signal told you " +
+ $"to {signal.Direction} {Config.Ticker} on {Config.Timeframe}. The confidence in this signal is {signal.Confidence}. Identifier : {signal.Identifier}";
Logger.LogInformation(signalText);
- if (IsForWatchingOnly && !IsForBacktest && ExecutionCount > 0)
+ if (Config.IsForWatchingOnly && !Config.IsForBacktest && ExecutionCount > 0)
{
- await MessengerService.SendSignal(signalText, Account.Exchange, Ticker, signal.Direction, Timeframe);
+ await MessengerService.SendSignal(signalText, Account.Exchange, Config.Ticker, signal.Direction,
+ Config.Timeframe);
}
}
@@ -300,7 +269,8 @@ public class TradingBot : Bot, ITradingBot
return;
var lastCandle = OptimizedCandles.Last();
- var newCandle = await ExchangeService.GetCandlesInflux(Account.Exchange, Ticker, lastCandle.Date, Timeframe);
+ var newCandle =
+ await ExchangeService.GetCandlesInflux(Account.Exchange, Config.Ticker, lastCandle.Date, Config.Timeframe);
foreach (var candle in newCandle.Where(c => c.Date < DateTime.Now.ToUniversalTime()))
{
@@ -342,7 +312,7 @@ public class TradingBot : Bot, ITradingBot
if (WalletBalances.Count == 0)
{
// WalletBalances[date] = await ExchangeService.GetBalance(Account, IsForBacktest);
- WalletBalances[date] = BotTradingBalance;
+ WalletBalances[date] = Config.BotTradingBalance;
return;
}
@@ -353,24 +323,23 @@ public class TradingBot : Bot, ITradingBot
}
}
-
private async Task UpdatePosition(Signal signal, Position positionForSignal)
{
try
{
Logger.LogInformation($"Updating position {positionForSignal.SignalIdentifier}");
- var position = IsForBacktest
+ var position = Config.IsForBacktest
? positionForSignal
: TradingService.GetPositionByIdentifier(positionForSignal.Identifier);
- var positionsExchange = IsForBacktest
+ var positionsExchange = Config.IsForBacktest
? new List { position }
: await TradingService.GetBrokerPositions(Account);
- if (!IsForBacktest)
+ if (!Config.IsForBacktest)
{
- var brokerPosition = positionsExchange.FirstOrDefault(p => p.Ticker == Ticker);
+ var brokerPosition = positionsExchange.FirstOrDefault(p => p.Ticker == Config.Ticker);
if (brokerPosition != null)
{
UpdatePositionPnl(positionForSignal.Identifier, brokerPosition.ProfitAndLoss.Realized);
@@ -395,7 +364,7 @@ public class TradingBot : Bot, ITradingBot
if (position.Status == PositionStatus.New)
{
- var orders = await ExchangeService.GetOpenOrders(Account, Ticker);
+ var orders = await ExchangeService.GetOpenOrders(Account, Config.Ticker);
if (orders.Any())
{
await LogInformation(
@@ -420,9 +389,9 @@ public class TradingBot : Bot, ITradingBot
// Position might be partially filled, meaning that TPSL havent been sended yet
// But the position might already been closed by the exchange so we have to check should be closed
- var lastCandle = IsForBacktest
+ var lastCandle = Config.IsForBacktest
? OptimizedCandles.Last()
- : ExchangeService.GetCandle(Account, Ticker, DateTime.UtcNow);
+ : ExchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow);
if (positionForSignal.OriginDirection == TradeDirection.Long)
{
@@ -512,7 +481,6 @@ public class TradingBot : Bot, ITradingBot
}
}
-
private async Task OpenPosition(Signal signal)
{
// Check if a position is already open
@@ -521,9 +489,9 @@ public class TradingBot : Bot, ITradingBot
var openedPosition = Positions.FirstOrDefault(p => p.Status == PositionStatus.Filled
&& p.SignalIdentifier != signal.Identifier);
- var lastPrice = IsForBacktest
+ var lastPrice = Config.IsForBacktest
? OptimizedCandles.Last().Close
- : ExchangeService.GetPrice(Account, Ticker, DateTime.UtcNow);
+ : ExchangeService.GetPrice(Account, Config.Ticker, DateTime.UtcNow);
// If position open
if (openedPosition != null)
@@ -542,7 +510,7 @@ public class TradingBot : Bot, ITradingBot
{
// An operation is already open for the opposite direction
// ==> Flip the position
- if (FlipPosition)
+ if (Config.FlipPosition)
{
await LogInformation("Try to flip the position because of an opposite direction signal");
await CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true);
@@ -561,10 +529,8 @@ public class TradingBot : Bot, ITradingBot
}
else
{
- if (!CanOpenPosition(signal))
+ if (!(await CanOpenPosition(signal)))
{
- await LogInformation(
- "Tried to open position but last position was a loss. Wait for an opposition direction side or wait x candles to open a new position");
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
return;
}
@@ -575,15 +541,15 @@ public class TradingBot : Bot, ITradingBot
try
{
var command = new OpenPositionRequest(
- AccountName,
- MoneyManagement,
+ Config.AccountName,
+ Config.MoneyManagement,
signal.Direction,
- Ticker,
+ Config.Ticker,
PositionInitiator.Bot,
signal.Date,
User,
- BotTradingBalance,
- IsForBacktest,
+ Config.BotTradingBalance,
+ Config.IsForBacktest,
lastPrice,
signalIdentifier: signal.Identifier);
@@ -598,7 +564,7 @@ public class TradingBot : Bot, ITradingBot
{
SetSignalStatus(signal.Identifier, SignalStatus.PositionOpen);
- if (!IsForBacktest)
+ if (!Config.IsForBacktest)
{
await MessengerService.SendPosition(position);
}
@@ -622,25 +588,142 @@ public class TradingBot : Bot, ITradingBot
}
}
- private bool CanOpenPosition(Signal signal)
+ private async Task CanOpenPosition(Signal signal)
{
- if (!IsForBacktest && ExecutionCount < 1)
+ // Early return if we're in backtest mode and haven't executed yet
+ if (!Config.IsForBacktest && ExecutionCount < 1)
+ {
+ await LogInformation("Cannot open position: Bot hasn't executed yet");
return false;
+ }
- if (Positions.Count == 0)
+ // Check if we're in backtest mode
+ if (Config.IsForBacktest)
+ {
+ return await CheckCooldownPeriod(signal) && await CheckLossStreak(signal);
+ }
+
+ // Check broker positions for live trading
+ var canOpenPosition = await CheckBrokerPositions();
+ if (!canOpenPosition)
+ {
+ return false;
+ }
+
+ // Check cooldown period and loss streak
+ return await CheckCooldownPeriod(signal) && await CheckLossStreak(signal);
+ }
+
+ private async Task CheckLossStreak(Signal signal)
+ {
+ // If MaxLossStreak is 0, there's no limit
+ if (Config.MaxLossStreak <= 0)
+ {
return true;
+ }
+ // Get the last N finished positions regardless of direction
+ var recentPositions = Positions
+ .Where(p => p.IsFinished())
+ .OrderByDescending(p => p.Open.Date)
+ .Take(Config.MaxLossStreak)
+ .ToList();
+
+ // If we don't have enough positions to form a streak, we can open
+ if (recentPositions.Count < Config.MaxLossStreak)
+ {
+ return true;
+ }
+
+ // Check if all recent positions were losses
+ var allLosses = recentPositions.All(p => p.ProfitAndLoss?.Realized < 0);
+ if (!allLosses)
+ {
+ return true;
+ }
+
+ // If we have a loss streak, check if the last position was in the same direction as the signal
+ var lastPosition = recentPositions.First();
+ if (lastPosition.OriginDirection == signal.Direction)
+ {
+ await LogWarning($"Cannot open position: Max loss streak ({Config.MaxLossStreak}) reached. " +
+ $"Last {recentPositions.Count} trades were losses. " +
+ $"Last position was {lastPosition.OriginDirection}, waiting for a signal in the opposite direction.");
+ return false;
+ }
+
+ return true;
+ }
+
+ private async Task CheckBrokerPositions()
+ {
+ try
+ {
+ var positions = await ExchangeService.GetBrokerPositions(Account);
+ if (!positions.Any(p => p.Ticker == Config.Ticker))
+ {
+ return true;
+ }
+
+ // Handle existing position on broker
+ var previousPosition = Positions.LastOrDefault();
+ var orders = await ExchangeService.GetOpenOrders(Account, Config.Ticker);
+
+ var reason = $"Cannot open position. There is already a position open for {Config.Ticker} on the broker.";
+
+ if (previousPosition != null && orders.Count >= 2)
+ {
+ await SetPositionStatus(previousPosition.SignalIdentifier, PositionStatus.Filled);
+ }
+ else
+ {
+ reason +=
+ " Position open on broker but not enough orders or no previous position internally saved by the bot";
+ }
+
+ await LogWarning(reason);
+ return false;
+ }
+ catch (Exception ex)
+ {
+ await LogWarning($"Error checking broker positions: {ex.Message}");
+ return false;
+ }
+ }
+
+ private async Task CheckCooldownPeriod(Signal signal)
+ {
var lastPosition = Positions.LastOrDefault(p => p.IsFinished()
&& p.SignalIdentifier != signal.Identifier
&& p.OriginDirection == signal.Direction);
if (lastPosition == null)
+ {
return true;
+ }
+
+ var cooldownCandle = OptimizedCandles.TakeLast((int)Config.CooldownPeriod).FirstOrDefault();
+ if (cooldownCandle == null)
+ {
+ await LogWarning("Cannot check cooldown period: Not enough candles available");
+ return false;
+ }
- var tenCandleAgo = OptimizedCandles.TakeLast(10).First();
var positionSignal = Signals.FirstOrDefault(s => s.Identifier == lastPosition.SignalIdentifier);
+ if (positionSignal == null)
+ {
+ await LogWarning($"Cannot find signal for last position {lastPosition.Identifier}");
+ return false;
+ }
- return positionSignal.Date < tenCandleAgo.Date;
+ var canOpenPosition = positionSignal.Date < cooldownCandle.Date;
+ if (!canOpenPosition)
+ {
+ await LogInformation(
+ $"Position cannot be opened: Cooldown period not elapsed. Last position date: {positionSignal.Date}, Cooldown candle date: {cooldownCandle.Date}");
+ }
+
+ return canOpenPosition;
}
public async Task CloseTrade(Signal signal, Position position, Trade tradeToClose, decimal lastPrice,
@@ -654,18 +737,18 @@ public class TradingBot : Bot, ITradingBot
}
await LogInformation(
- $"Trying to close trade {Ticker} at {lastPrice} - Type : {tradeToClose.TradeType} - Quantity : {tradeToClose.Quantity} " +
+ $"Trying to close trade {Config.Ticker} at {lastPrice} - Type : {tradeToClose.TradeType} - Quantity : {tradeToClose.Quantity} " +
$"- Closing Position : {tradeClosingPosition}");
// Get status of position before closing it. The position might be already close by the exchange
- if (!IsForBacktest && await ExchangeService.GetQuantityInPosition(Account, Ticker) == 0)
+ if (!Config.IsForBacktest && await ExchangeService.GetQuantityInPosition(Account, Config.Ticker) == 0)
{
Logger.LogInformation($"Trade already close on exchange");
await HandleClosedPosition(position);
}
else
{
- var command = new ClosePositionCommand(position, lastPrice, isForBacktest: IsForBacktest);
+ var command = new ClosePositionCommand(position, lastPrice, isForBacktest: Config.IsForBacktest);
try
{
var closedPosition = (new ClosePositionCommandHandler(ExchangeService, AccountService, TradingService)
@@ -710,12 +793,12 @@ public class TradingBot : Bot, ITradingBot
if (position.ProfitAndLoss != null)
{
// Add PnL (could be positive or negative)
- BotTradingBalance += position.ProfitAndLoss.Realized;
+ Config.BotTradingBalance += position.ProfitAndLoss.Realized;
// Subtract fees
- BotTradingBalance -= GetPositionFees(position);
+ Config.BotTradingBalance -= GetPositionFees(position);
- Logger.LogInformation($"Updated bot trading balance to: {BotTradingBalance}");
+ Logger.LogInformation($"Updated bot trading balance to: {Config.BotTradingBalance}");
}
}
else
@@ -723,7 +806,7 @@ public class TradingBot : Bot, ITradingBot
await LogWarning("Weird things happen - Trying to update position status, but no position found");
}
- if (!IsForBacktest)
+ if (!Config.IsForBacktest)
{
await MessengerService.SendClosingPosition(position);
}
@@ -733,15 +816,15 @@ public class TradingBot : Bot, ITradingBot
private async Task CancelAllOrders()
{
- if (!IsForBacktest && !IsForWatchingOnly)
+ if (!Config.IsForBacktest && !Config.IsForWatchingOnly)
{
try
{
- var openOrders = await ExchangeService.GetOpenOrders(Account, Ticker);
+ var openOrders = await ExchangeService.GetOpenOrders(Account, Config.Ticker);
if (openOrders.Any())
{
var openPositions = (await ExchangeService.GetBrokerPositions(Account))
- .Where(p => p.Ticker == Ticker);
+ .Where(p => p.Ticker == Config.Ticker);
var cancelClose = openPositions.Any();
if (cancelClose)
@@ -750,15 +833,15 @@ public class TradingBot : Bot, ITradingBot
}
else
{
- Logger.LogInformation($"Canceling all orders for {Ticker}");
- await ExchangeService.CancelOrder(Account, Ticker);
- var closePendingOrderStatus = await ExchangeService.CancelOrder(Account, Ticker);
- Logger.LogInformation($"Closing all {Ticker} orders status : {closePendingOrderStatus}");
+ Logger.LogInformation($"Canceling all orders for {Config.Ticker}");
+ await ExchangeService.CancelOrder(Account, Config.Ticker);
+ var closePendingOrderStatus = await ExchangeService.CancelOrder(Account, Config.Ticker);
+ Logger.LogInformation($"Closing all {Config.Ticker} orders status : {closePendingOrderStatus}");
}
}
else
{
- Logger.LogInformation($"No need to cancel orders for {Ticker}");
+ Logger.LogInformation($"No need to cancel orders for {Config.Ticker}");
}
}
catch (Exception ex)
@@ -771,10 +854,11 @@ public class TradingBot : Bot, ITradingBot
private async Task SetPositionStatus(string signalIdentifier, PositionStatus positionStatus)
{
- if (!Positions.First(p => p.SignalIdentifier == signalIdentifier).Status.Equals(positionStatus))
+ var position = Positions.First(p => p.SignalIdentifier == signalIdentifier);
+ if (!position.Status.Equals(positionStatus))
{
- await LogInformation($"Position {signalIdentifier} is now {positionStatus}");
Positions.First(p => p.SignalIdentifier == signalIdentifier).Status = positionStatus;
+ await LogInformation($"Position {signalIdentifier} new status {position.Status} => {positionStatus}");
}
SetSignalStatus(signalIdentifier,
@@ -864,8 +948,8 @@ public class TradingBot : Bot, ITradingBot
public async Task ToggleIsForWatchOnly()
{
- IsForWatchingOnly = (!IsForWatchingOnly);
- await LogInformation($"Watch only toggle for bot : {Name} - Watch only : {IsForWatchingOnly}");
+ Config.IsForWatchingOnly = !Config.IsForWatchingOnly;
+ await LogInformation($"Watch only toggle for bot : {Name} - Watch only : {Config.IsForWatchingOnly}");
}
private async Task LogInformation(string message)
@@ -883,7 +967,7 @@ public class TradingBot : Bot, ITradingBot
private async Task SendTradeMessage(string message, bool isBadBehavior = false)
{
- if (!IsForBacktest)
+ if (!Config.IsForBacktest)
{
await MessengerService.SendTradeMessage(message, isBadBehavior);
}
@@ -894,43 +978,47 @@ public class TradingBot : Bot, ITradingBot
var data = new TradingBotBackup
{
Name = Name,
- BotType = BotType,
+ BotType = Config.BotType,
Signals = Signals,
Positions = Positions,
- Timeframe = Timeframe,
- Ticker = Ticker,
- ScenarioName = ScenarioName,
- AccountName = AccountName,
- IsForWatchingOnly = IsForWatchingOnly,
+ Timeframe = Config.Timeframe,
+ Ticker = Config.Ticker,
+ ScenarioName = Config.ScenarioName,
+ AccountName = Config.AccountName,
+ IsForWatchingOnly = Config.IsForWatchingOnly,
WalletBalances = WalletBalances,
- MoneyManagement = MoneyManagement,
- BotTradingBalance = BotTradingBalance,
+ MoneyManagement = Config.MoneyManagement,
+ BotTradingBalance = Config.BotTradingBalance,
StartupTime = StartupTime,
+ CooldownPeriod = Config.CooldownPeriod,
};
- BotService.SaveOrUpdateBotBackup(User, Identifier, BotType, JsonConvert.SerializeObject(data));
+ BotService.SaveOrUpdateBotBackup(User, Identifier, Config.BotType, Status, JsonConvert.SerializeObject(data));
}
public override void LoadBackup(BotBackup backup)
{
var data = JsonConvert.DeserializeObject(backup.Data);
+ Config = new TradingBotConfig
+ {
+ AccountName = data.AccountName,
+ MoneyManagement = data.MoneyManagement,
+ Ticker = data.Ticker,
+ ScenarioName = data.ScenarioName,
+ Timeframe = data.Timeframe,
+ IsForBacktest = false, // Always false when loading from backup
+ IsForWatchingOnly = data.IsForWatchingOnly,
+ BotTradingBalance = data.BotTradingBalance,
+ BotType = data.BotType,
+ CooldownPeriod = data.CooldownPeriod,
+ };
+
Signals = data.Signals;
Positions = data.Positions;
WalletBalances = data.WalletBalances;
- // MoneyManagement = data.MoneyManagement; => loaded from database
- Timeframe = data.Timeframe;
- Ticker = data.Ticker;
- ScenarioName = data.ScenarioName;
- AccountName = data.AccountName;
- IsForWatchingOnly = data.IsForWatchingOnly;
- BotTradingBalance = data.BotTradingBalance;
+ PreloadSince = data.StartupTime;
Identifier = backup.Identifier;
User = backup.User;
-
- // Restore the startup time if it was previously saved
- if (data.StartupTime != DateTime.MinValue)
- {
- StartupTime = data.StartupTime;
- }
+ Status = backup.LastStatus;
}
///
@@ -949,7 +1037,8 @@ public class TradingBot : Bot, ITradingBot
}
// Create a fake signal for manual position opening
- var signal = new Signal(Ticker, direction, Confidence.Low, lastCandle, lastCandle.Date, TradingExchanges.GmxV2,
+ var signal = new Signal(Config.Ticker, direction, Confidence.Low, lastCandle, lastCandle.Date,
+ TradingExchanges.GmxV2,
StrategyType.Stc, SignalType.Signal);
signal.Status = SignalStatus.WaitingForPosition; // Ensure status is correct
signal.User = Account.User; // Assign user
@@ -989,4 +1078,5 @@ public class TradingBotBackup
public MoneyManagement MoneyManagement { get; set; }
public DateTime StartupTime { get; set; }
public decimal BotTradingBalance { get; set; }
+ public decimal CooldownPeriod { get; set; }
}
\ No newline at end of file
diff --git a/src/Managing.Application/Bots/TradingBotConfig.cs b/src/Managing.Application/Bots/TradingBotConfig.cs
new file mode 100644
index 0000000..8c6bb95
--- /dev/null
+++ b/src/Managing.Application/Bots/TradingBotConfig.cs
@@ -0,0 +1,20 @@
+using Managing.Domain.MoneyManagements;
+using static Managing.Common.Enums;
+
+namespace Managing.Application.Bots;
+
+public class TradingBotConfig
+{
+ public string AccountName { get; set; }
+ public MoneyManagement MoneyManagement { get; set; }
+ public Ticker Ticker { get; set; }
+ public string ScenarioName { get; set; }
+ public Timeframe Timeframe { get; set; }
+ public bool IsForBacktest { get; set; }
+ public bool IsForWatchingOnly { get; set; }
+ public bool FlipPosition { get; set; }
+ public BotType BotType { get; set; }
+ public decimal BotTradingBalance { get; set; }
+ public decimal CooldownPeriod { get; set; } = 1;
+ public int MaxLossStreak { get; set; } = 0; // 0 means no limit
+}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/BotService.cs b/src/Managing.Application/ManageBot/BotService.cs
index 6590e18..e28c6bb 100644
--- a/src/Managing.Application/ManageBot/BotService.cs
+++ b/src/Managing.Application/ManageBot/BotService.cs
@@ -3,13 +3,13 @@ using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Application.Bots;
-using Managing.Common;
using Managing.Domain.Bots;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Users;
using Managing.Domain.Workflows;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
+using static Managing.Common.Enums;
namespace Managing.Application.ManageBot
{
@@ -22,13 +22,14 @@ namespace Managing.Application.ManageBot
private readonly ILogger _tradingBotLogger;
private readonly ITradingService _tradingService;
private readonly IMoneyManagementService _moneyManagementService;
+ private readonly IUserService _userService;
private ConcurrentDictionary _botTasks =
new ConcurrentDictionary();
public BotService(IBotRepository botRepository, IExchangeService exchangeService,
IMessengerService messengerService, IAccountService accountService, ILogger tradingBotLogger,
- ITradingService tradingService, IMoneyManagementService moneyManagementService)
+ ITradingService tradingService, IMoneyManagementService moneyManagementService, IUserService userService)
{
_botRepository = botRepository;
_exchangeService = exchangeService;
@@ -37,11 +38,7 @@ namespace Managing.Application.ManageBot
_tradingBotLogger = tradingBotLogger;
_tradingService = tradingService;
_moneyManagementService = moneyManagementService;
- }
-
- public async void SaveOrUpdateBotBackup(BotBackup botBackup)
- {
- await _botRepository.InsertBotAsync(botBackup);
+ _userService = userService;
}
public BotBackup GetBotBackup(string identifier)
@@ -49,12 +46,13 @@ namespace Managing.Application.ManageBot
return _botRepository.GetBots().FirstOrDefault(b => b.Identifier == identifier);
}
- public void SaveOrUpdateBotBackup(User user, string identifier, Enums.BotType botType, string data)
+ public void SaveOrUpdateBotBackup(User user, string identifier, BotType botType, BotStatus status, string data)
{
var backup = GetBotBackup(identifier);
if (backup != null)
{
+ backup.LastStatus = status;
backup.Data = data;
_botRepository.UpdateBackupBot(backup);
}
@@ -62,6 +60,7 @@ namespace Managing.Application.ManageBot
{
var botBackup = new BotBackup
{
+ LastStatus = status,
User = user,
Identifier = identifier,
BotType = botType,
@@ -127,7 +126,7 @@ namespace Managing.Application.ManageBot
// null); // Assuming null is an acceptable parameter for workflow
// botTask = Task.Run(() => ((IBot)bot).Start());
// break;
- case Enums.BotType.ScalpingBot:
+ case BotType.ScalpingBot:
var scalpingBotData = JsonConvert.DeserializeObject(backupBot.Data);
var scalpingMoneyManagement =
_moneyManagementService.GetMoneyMangement(scalpingBotData.MoneyManagement.Name).Result;
@@ -142,7 +141,7 @@ namespace Managing.Application.ManageBot
scalpingBotData.BotTradingBalance);
botTask = Task.Run(() => InitBot((ITradingBot)bot, backupBot));
break;
- case Enums.BotType.FlippingBot:
+ case BotType.FlippingBot:
var flippingBotData = JsonConvert.DeserializeObject(backupBot.Data);
var flippingMoneyManagement =
_moneyManagementService.GetMoneyMangement(flippingBotData.MoneyManagement.Name).Result;
@@ -166,9 +165,12 @@ namespace Managing.Application.ManageBot
}
}
- private static Action InitBot(ITradingBot bot, BotBackup backupBot)
+ private Action InitBot(ITradingBot bot, BotBackup backupBot)
{
bot.Start();
+
+ var user = _userService.GetUser(backupBot.User.Name);
+ backupBot.User = user;
bot.LoadBackup(backupBot);
return () => { };
}
@@ -178,9 +180,9 @@ namespace Managing.Application.ManageBot
return new SimpleBot(botName, _tradingBotLogger, workflow, this);
}
- public async Task StopBot(string botName)
+ public async Task StopBot(string identifier)
{
- if (_botTasks.TryGetValue(botName, out var botWrapper))
+ if (_botTasks.TryGetValue(identifier, out var botWrapper))
{
if (botWrapper.BotInstance is IBot bot)
{
@@ -190,12 +192,12 @@ namespace Managing.Application.ManageBot
}
}
- return Enums.BotStatus.Down.ToString();
+ return BotStatus.Down.ToString();
}
- public async Task DeleteBot(string botName)
+ public async Task DeleteBot(string identifier)
{
- if (_botTasks.TryRemove(botName, out var botWrapper))
+ if (_botTasks.TryRemove(identifier, out var botWrapper))
{
try
{
@@ -205,7 +207,7 @@ namespace Managing.Application.ManageBot
bot.Stop()); // Assuming Stop is an asynchronous process wrapped in Task.Run for synchronous methods
}
- await _botRepository.DeleteBotBackup(botName);
+ await _botRepository.DeleteBotBackup(identifier);
return true;
}
catch (Exception e)
@@ -218,9 +220,9 @@ namespace Managing.Application.ManageBot
return false;
}
- public Task RestartBot(string botName)
+ public Task RestartBot(string identifier)
{
- if (_botTasks.TryGetValue(botName, out var botWrapper))
+ if (_botTasks.TryGetValue(identifier, out var botWrapper))
{
if (botWrapper.BotInstance is IBot bot)
{
@@ -229,17 +231,17 @@ namespace Managing.Application.ManageBot
}
}
- return Task.FromResult(Enums.BotStatus.Down.ToString());
+ return Task.FromResult(BotStatus.Down.ToString());
}
- public void DeleteBotBackup(string backupBotName)
+ public void DeleteBotBackup(string identifier)
{
- _botRepository.DeleteBotBackup(backupBotName);
+ _botRepository.DeleteBotBackup(identifier);
}
- public void ToggleIsForWatchingOnly(string botName)
+ public void ToggleIsForWatchingOnly(string identifier)
{
- if (_botTasks.TryGetValue(botName, out var botTaskWrapper) &&
+ if (_botTasks.TryGetValue(identifier, out var botTaskWrapper) &&
botTaskWrapper.BotInstance is ITradingBot tradingBot)
{
tradingBot.ToggleIsForWatchOnly().Wait();
@@ -247,89 +249,159 @@ namespace Managing.Application.ManageBot
}
public ITradingBot CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name,
- Enums.Ticker ticker, string scenario, Enums.Timeframe interval, bool isForWatchingOnly,
+ 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
+ };
+
return new ScalpingBot(
- accountName,
- moneyManagement,
- name,
- scenario,
_exchangeService,
- ticker,
- _tradingService,
_tradingBotLogger,
- interval,
+ _tradingService,
_accountService,
_messengerService,
this,
- initialTradingBalance,
- isForWatchingOnly: isForWatchingOnly);
+ config);
}
public ITradingBot CreateBacktestScalpingBot(string accountName, MoneyManagement moneyManagement,
- Enums.Ticker ticker, string scenario, Enums.Timeframe interval, bool isForWatchingOnly,
+ 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(
- accountName,
- moneyManagement,
- "BacktestBot",
- scenario,
_exchangeService,
- ticker,
- _tradingService,
_tradingBotLogger,
- interval,
+ _tradingService,
_accountService,
_messengerService,
this,
- initialTradingBalance,
- isForBacktest: true,
- isForWatchingOnly: isForWatchingOnly);
+ config);
}
public ITradingBot CreateFlippingBot(string accountName, MoneyManagement moneyManagement, string name,
- Enums.Ticker ticker, string scenario, Enums.Timeframe interval, bool isForWatchingOnly,
+ 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(
- accountName,
- moneyManagement,
- name,
- scenario,
_exchangeService,
- ticker,
- _tradingService,
_tradingBotLogger,
- interval,
+ _tradingService,
_accountService,
_messengerService,
this,
- initialTradingBalance,
- isForWatchingOnly: isForWatchingOnly);
+ config);
}
public ITradingBot CreateBacktestFlippingBot(string accountName, MoneyManagement moneyManagement,
- Enums.Ticker ticker, string scenario, Enums.Timeframe interval, bool isForWatchingOnly,
+ 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(
- accountName,
- moneyManagement,
- "BacktestBot",
- scenario,
_exchangeService,
- ticker,
- _tradingService,
_tradingBotLogger,
- interval,
+ _tradingService,
_accountService,
_messengerService,
this,
- initialTradingBalance,
- isForBacktest: true,
- isForWatchingOnly: isForWatchingOnly);
+ config);
+ }
+
+ public ITradingBot CreateScalpingBot(TradingBotConfig config)
+ {
+ return new ScalpingBot(
+ _exchangeService,
+ _tradingBotLogger,
+ _tradingService,
+ _accountService,
+ _messengerService,
+ this,
+ config);
+ }
+
+ public ITradingBot CreateBacktestScalpingBot(TradingBotConfig config)
+ {
+ config.IsForBacktest = true;
+ return new ScalpingBot(
+ _exchangeService,
+ _tradingBotLogger,
+ _tradingService,
+ _accountService,
+ _messengerService,
+ this,
+ config);
+ }
+
+ public ITradingBot CreateFlippingBot(TradingBotConfig config)
+ {
+ return new FlippingBot(
+ _exchangeService,
+ _tradingBotLogger,
+ _tradingService,
+ _accountService,
+ _messengerService,
+ this,
+ config);
+ }
+
+ public ITradingBot CreateBacktestFlippingBot(TradingBotConfig config)
+ {
+ config.IsForBacktest = true;
+ return new FlippingBot(
+ _exchangeService,
+ _tradingBotLogger,
+ _tradingService,
+ _accountService,
+ _messengerService,
+ this,
+ config);
}
}
}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/Commands/StartBotCommand.cs b/src/Managing.Application/ManageBot/Commands/StartBotCommand.cs
index a536997..3e1d856 100644
--- a/src/Managing.Application/ManageBot/Commands/StartBotCommand.cs
+++ b/src/Managing.Application/ManageBot/Commands/StartBotCommand.cs
@@ -1,44 +1,20 @@
-using Managing.Domain.Users;
+using Managing.Application.Bots;
+using Managing.Domain.Users;
using MediatR;
-using static Managing.Common.Enums;
namespace Managing.Application.ManageBot.Commands
{
public class StartBotCommand : IRequest
{
public string Name { get; }
- public BotType BotType { get; }
- public Ticker Ticker { get; internal set; }
- public Timeframe Timeframe { get; internal set; }
- public bool IsForWatchingOnly { get; internal set; }
- public string Scenario { get; internal set; }
- public string AccountName { get; internal set; }
- public string MoneyManagementName { get; internal set; }
- public User User { get; internal set; }
- public decimal InitialTradingBalance { get; internal set; }
+ public TradingBotConfig Config { get; }
+ public User User { get; }
- public StartBotCommand(BotType botType,
- string name,
- Ticker ticker,
- string scenario,
- Timeframe timeframe,
- string accountName,
- string moneyManagementName,
- User user,
- bool isForWatchingOnly = false,
- decimal initialTradingBalance = 0)
+ public StartBotCommand(TradingBotConfig config, string name, User user)
{
- BotType = botType;
+ Config = config;
Name = name;
- Scenario = scenario;
- Ticker = ticker;
- Timeframe = timeframe;
- IsForWatchingOnly = isForWatchingOnly;
- AccountName = accountName;
- MoneyManagementName = moneyManagementName;
User = user;
- InitialTradingBalance = initialTradingBalance > 0 ? initialTradingBalance :
- throw new ArgumentException("Initial trading balance must be greater than zero", nameof(initialTradingBalance));
}
}
}
diff --git a/src/Managing.Application/ManageBot/GetUserStrategiesCommandHandler.cs b/src/Managing.Application/ManageBot/GetUserStrategiesCommandHandler.cs
index a4f2796..0fbbc01 100644
--- a/src/Managing.Application/ManageBot/GetUserStrategiesCommandHandler.cs
+++ b/src/Managing.Application/ManageBot/GetUserStrategiesCommandHandler.cs
@@ -17,7 +17,7 @@ namespace Managing.Application.ManageBot
{
var allActiveBots = _botService.GetActiveBots();
var userBots = allActiveBots
- .Where(bot => bot.User != null && bot.User.Name == request.UserName)
+ .Where(bot => bot.User != null && bot.User.AgentName == request.UserName)
.ToList();
return Task.FromResult(userBots);
diff --git a/src/Managing.Application/ManageBot/GetUserStrategyCommandHandler.cs b/src/Managing.Application/ManageBot/GetUserStrategyCommandHandler.cs
index 640ad86..1afefba 100644
--- a/src/Managing.Application/ManageBot/GetUserStrategyCommandHandler.cs
+++ b/src/Managing.Application/ManageBot/GetUserStrategyCommandHandler.cs
@@ -19,15 +19,14 @@ namespace Managing.Application.ManageBot
public Task Handle(GetUserStrategyCommand request, CancellationToken cancellationToken)
{
var allActiveBots = _botService.GetActiveBots();
-
+
// Find the specific strategy that matches both user and strategy name
var strategy = allActiveBots
- .FirstOrDefault(bot =>
- bot.User != null &&
- bot.User.Name == request.AgentName &&
- bot.Name == request.StrategyName);
+ .FirstOrDefault(bot =>
+ bot.User.AgentName == request.AgentName &&
+ bot.Identifier == request.StrategyName);
return Task.FromResult(strategy);
}
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/LoadBackupBotCommandHandler.cs b/src/Managing.Application/ManageBot/LoadBackupBotCommandHandler.cs
index ba88a1a..fd88313 100644
--- a/src/Managing.Application/ManageBot/LoadBackupBotCommandHandler.cs
+++ b/src/Managing.Application/ManageBot/LoadBackupBotCommandHandler.cs
@@ -31,6 +31,13 @@ public class LoadBackupBotCommandHandler : IRequestHandler b.Identifier == backupBot.Identifier);
if (activeBot == null)
diff --git a/src/Managing.Application/ManageBot/StartBotCommandHandler.cs b/src/Managing.Application/ManageBot/StartBotCommandHandler.cs
index 3daa702..7b4349d 100644
--- a/src/Managing.Application/ManageBot/StartBotCommandHandler.cs
+++ b/src/Managing.Application/ManageBot/StartBotCommandHandler.cs
@@ -29,38 +29,38 @@ namespace Managing.Application.ManageBot
{
BotStatus botStatus = BotStatus.Down;
- var account = await _accountService.GetAccount(request.AccountName, true, true);
+ var account = await _accountService.GetAccount(request.Config.AccountName, true, true);
if (account == null)
{
- throw new Exception($"Account {request.AccountName} not found");
+ throw new Exception($"Account {request.Config.AccountName} not found");
}
var usdcBalance = account.Balances.FirstOrDefault(b => b.TokenName == Ticker.USDC.ToString());
- if (usdcBalance == null || usdcBalance.Value < request.InitialTradingBalance)
+ if (usdcBalance == null || usdcBalance.Value < request.Config.BotTradingBalance)
{
- throw new Exception($"Account {request.AccountName} has no USDC balance or not enough balance");
+ throw new Exception($"Account {request.Config.AccountName} has no USDC balance or not enough balance");
}
- var moneyManagement =
- await _moneyManagementService.GetMoneyMangement(request.User, request.MoneyManagementName);
- switch (request.BotType)
+ // Ensure cooldown period is set
+ if (request.Config.CooldownPeriod <= 0)
+ {
+ request.Config.CooldownPeriod = 1; // Default to 1 minute if not set
+ }
+
+ switch (request.Config.BotType)
{
case BotType.SimpleBot:
var bot = _botFactory.CreateSimpleBot(request.Name, null);
_botService.AddSimpleBotToCache(bot);
return bot.GetStatus();
case BotType.ScalpingBot:
- var sBot = _botFactory.CreateScalpingBot(request.AccountName, moneyManagement, request.Name,
- request.Ticker, request.Scenario, request.Timeframe, request.IsForWatchingOnly,
- request.InitialTradingBalance);
+ var sBot = _botFactory.CreateScalpingBot(request.Config);
_botService.AddTradingBotToCache(sBot);
return sBot.GetStatus();
case BotType.FlippingBot:
- var fBot = _botFactory.CreateFlippingBot(request.AccountName, moneyManagement, request.Name,
- request.Ticker, request.Scenario, request.Timeframe, request.IsForWatchingOnly,
- request.InitialTradingBalance);
+ var fBot = _botFactory.CreateFlippingBot(request.Config);
_botService.AddTradingBotToCache(fBot);
return fBot.GetStatus();
}
diff --git a/src/Managing.Application/ManageBot/ToggleIsForWatchingCommandHandler.cs b/src/Managing.Application/ManageBot/ToggleIsForWatchingCommandHandler.cs
index a213c18..4b25eb9 100644
--- a/src/Managing.Application/ManageBot/ToggleIsForWatchingCommandHandler.cs
+++ b/src/Managing.Application/ManageBot/ToggleIsForWatchingCommandHandler.cs
@@ -17,7 +17,7 @@ namespace Managing.Application.ManageBot
{
_botService.ToggleIsForWatchingOnly(request.Name);
var bot = _botService.GetActiveBots().FirstOrDefault(b => b.Name == request.Name);
- return Task.FromResult(bot?.IsForWatchingOnly.ToString());
+ return Task.FromResult(bot?.Config.IsForWatchingOnly.ToString());
}
}
}
\ No newline at end of file
diff --git a/src/Managing.Application/Users/UserService.cs b/src/Managing.Application/Users/UserService.cs
index 2b9a0ce..6314e42 100644
--- a/src/Managing.Application/Users/UserService.cs
+++ b/src/Managing.Application/Users/UserService.cs
@@ -24,7 +24,7 @@ public class UserService : IUserService
"0x23AA99254cfaA2c374bE2bA5B55C68018cCdFCb3", // Local optiflex
"0x932167388dD9aad41149b3cA23eBD489E2E2DD78", // Embedded wallet
"0x66CB57Fe3f53cE57376421106dFDa2D39186cBd0", // Embedded wallet optiflex
- "0x7baBf95621f22bEf2DB67E500D022Ca110722FaD", // DevCowchain
+ "0x7baBf95621f22bEf2DB67E500D022Ca110722FaD", // DevCowchain
"0xc8bC497534d0A43bAb2BBA9BA94d46D9Ddfaea6B" // DevCowchain2
];
@@ -47,7 +47,8 @@ public class UserService : IUserService
if (!message.Equals("KaigenTeamXCowchain"))
{
_logger.LogWarning($"Message {message} not starting with KaigenTeamXCowchain");
- throw new Exception($"Message not good : {message} - Address : {address} - User : {name} - Signature : {signature}");
+ throw new Exception(
+ $"Message not good : {message} - Address : {address} - User : {name} - Signature : {signature}");
}
// if (!authorizedAddresses.Contains(recoveredAddress))
@@ -106,11 +107,19 @@ public class UserService : IUserService
{
account
};
+
+ // Update user with the new account
+ await _userRepository.UpdateUser(user);
}
return user;
}
+ public User GetUser(string name)
+ {
+ return _userRepository.GetUserByNameAsync(name).Result;
+ }
+
public async Task GetUserByAddressAsync(string address)
{
var account = await _accountService.GetAccountByKey(address, true, false);
@@ -118,4 +127,18 @@ public class UserService : IUserService
user.Accounts = _accountService.GetAccountsByUser(user).ToList();
return user;
}
-}
+
+ public async Task UpdateAgentName(User user, string agentName)
+ {
+ // Check if agent name is already used
+ var existingUser = await _userRepository.GetUserByAgentNameAsync(agentName);
+ if (existingUser != null)
+ {
+ throw new Exception("Agent name already used");
+ }
+
+ user.AgentName = agentName;
+ await _userRepository.UpdateUser(user);
+ return user;
+ }
+}
\ No newline at end of file
diff --git a/src/Managing.Bootstrap/ApiBootstrap.cs b/src/Managing.Bootstrap/ApiBootstrap.cs
index 69bd8fb..5f82c2c 100644
--- a/src/Managing.Bootstrap/ApiBootstrap.cs
+++ b/src/Managing.Bootstrap/ApiBootstrap.cs
@@ -115,6 +115,7 @@ public static class ApiBootstrap
services.AddTransient();
services.AddTransient();
+
// Cache
services.AddDistributedMemoryCache();
services.AddTransient();
@@ -135,7 +136,7 @@ public static class ApiBootstrap
services.AddSingleton();
services.AddSingleton();
services.AddTransient();
-
+
// Web3Proxy Configuration
services.Configure(configuration.GetSection("Web3Proxy"));
services.AddTransient();
diff --git a/src/Managing.Bootstrap/WorkersBootstrap.cs b/src/Managing.Bootstrap/WorkersBootstrap.cs
index 63c2b30..f226b64 100644
--- a/src/Managing.Bootstrap/WorkersBootstrap.cs
+++ b/src/Managing.Bootstrap/WorkersBootstrap.cs
@@ -14,6 +14,7 @@ using Managing.Application.Scenarios;
using Managing.Application.Shared;
using Managing.Application.Trading;
using Managing.Application.Trading.Commands;
+using Managing.Application.Users;
using Managing.Application.Workers;
using Managing.Application.Workers.Abstractions;
using Managing.Domain.Trades;
@@ -52,6 +53,7 @@ public static class WorkersBootstrap
private static IServiceCollection AddApplication(this IServiceCollection services)
{
+ services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
@@ -102,6 +104,7 @@ public static class WorkersBootstrap
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
// Cache
services.AddDistributedMemoryCache();
diff --git a/src/Managing.Domain/Bots/BotBackup.cs b/src/Managing.Domain/Bots/BotBackup.cs
index 63313b3..8e84300 100644
--- a/src/Managing.Domain/Bots/BotBackup.cs
+++ b/src/Managing.Domain/Bots/BotBackup.cs
@@ -9,4 +9,5 @@ public class BotBackup
public string Identifier { get; set; }
public User User { get; set; }
public string Data { get; set; }
+ public BotStatus LastStatus { get; set; }
}
\ No newline at end of file
diff --git a/src/Managing.Domain/Bots/IBot.cs b/src/Managing.Domain/Bots/IBot.cs
index 679efb0..d821d0d 100644
--- a/src/Managing.Domain/Bots/IBot.cs
+++ b/src/Managing.Domain/Bots/IBot.cs
@@ -1,7 +1,10 @@
-namespace Managing.Domain.Bots
+using Managing.Domain.Users;
+
+namespace Managing.Domain.Bots
{
public interface IBot
{
+ User User { get; set; }
string Name { get; set; }
void Start();
void Stop();
diff --git a/src/Managing.Domain/Users/User.cs b/src/Managing.Domain/Users/User.cs
index e27bb5c..9d01d24 100644
--- a/src/Managing.Domain/Users/User.cs
+++ b/src/Managing.Domain/Users/User.cs
@@ -6,4 +6,5 @@ public class User
{
public string Name { get; set; }
public List Accounts { get; set; }
+ public string AgentName { get; set; }
}
diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/BotDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/BotDto.cs
index 51ad164..f92328c 100644
--- a/src/Managing.Infrastructure.Database/MongoDb/Collections/BotDto.cs
+++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/BotDto.cs
@@ -12,4 +12,5 @@ public class BotDto : Document
public BotType BotType { get; set; }
public string Identifier { get; set; }
public UserDto User { get; set; }
+ public BotStatus LastStatus { get; set; }
}
\ No newline at end of file
diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/UserDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/UserDto.cs
index 4f94b3b..c0a81c3 100644
--- a/src/Managing.Infrastructure.Database/MongoDb/Collections/UserDto.cs
+++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/UserDto.cs
@@ -7,4 +7,5 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections;
public class UserDto : Document
{
public string Name { get; set; }
+ public string AgentName { get; set; }
}
\ No newline at end of file
diff --git a/src/Managing.Infrastructure.Database/MongoDb/MongoMappers.cs b/src/Managing.Infrastructure.Database/MongoDb/MongoMappers.cs
index 9c941bf..1616209 100644
--- a/src/Managing.Infrastructure.Database/MongoDb/MongoMappers.cs
+++ b/src/Managing.Infrastructure.Database/MongoDb/MongoMappers.cs
@@ -516,6 +516,7 @@ public static class MongoMappers
return new User
{
Name = user.Name,
+ AgentName = user.AgentName
};
}
@@ -523,7 +524,8 @@ public static class MongoMappers
{
return new UserDto
{
- Name = user.Name
+ Name = user.Name,
+ AgentName = user.AgentName
};
}
@@ -724,6 +726,7 @@ public static class MongoMappers
Identifier = bot.Identifier,
BotType = bot.BotType,
Data = bot.Data,
+ LastStatus = bot.LastStatus
};
}
@@ -736,7 +739,8 @@ public static class MongoMappers
User = Map(b.User),
Identifier = b.Identifier,
BotType = b.BotType,
- Data = b.Data
+ Data = b.Data,
+ LastStatus = b.LastStatus
};
}
diff --git a/src/Managing.Infrastructure.Database/UserRepository.cs b/src/Managing.Infrastructure.Database/UserRepository.cs
index 35f4633..6e17c47 100644
--- a/src/Managing.Infrastructure.Database/UserRepository.cs
+++ b/src/Managing.Infrastructure.Database/UserRepository.cs
@@ -15,6 +15,12 @@ public class UserRepository : IUserRepository
_userRepository = userRepository;
}
+ public async Task GetUserByAgentNameAsync(string agentName)
+ {
+ var user = await _userRepository.FindOneAsync(u => u.AgentName == agentName);
+ return MongoMappers.Map(user);
+ }
+
public async Task GetUserByNameAsync(string name)
{
var user = await _userRepository.FindOneAsync(u => u.Name == name);
@@ -31,7 +37,7 @@ public class UserRepository : IUserRepository
try
{
var dto = await _userRepository.FindOneAsync(u => u.Name == user.Name);
- dto.Name = user.Name;
+ dto.AgentName = user.AgentName;
_userRepository.Update(dto);
}
catch (Exception e)
diff --git a/src/Managing.Nswag/.DS_Store b/src/Managing.Nswag/.DS_Store
index 2126008..cebf5cc 100644
Binary files a/src/Managing.Nswag/.DS_Store and b/src/Managing.Nswag/.DS_Store differ
diff --git a/src/Managing.Nswag/ManagingApi.ts b/src/Managing.Nswag/ManagingApi.ts
new file mode 100644
index 0000000..d634f7b
--- /dev/null
+++ b/src/Managing.Nswag/ManagingApi.ts
@@ -0,0 +1,3135 @@
+//----------------------
+//
+// Generated using the NSwag toolchain v14.3.0.0 (NJsonSchema v11.2.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)
+//
+//----------------------
+
+/* tslint:disable */
+/* eslint-disable */
+// ReSharper disable InconsistentNaming
+
+export class AccountClient extends AuthorizedApiBase {
+ private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };
+ private baseUrl: string;
+ protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
+
+ constructor(configuration: IConfig, baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {
+ super(configuration);
+ this.http = http ? http : window as any;
+ this.baseUrl = baseUrl ?? "http://localhost:5000";
+ }
+
+ account_PostAccount(account: Account): Promise {
+ let url_ = this.baseUrl + "/Account";
+ url_ = url_.replace(/[?&]$/, "");
+
+ const content_ = JSON.stringify(account);
+
+ let options_: RequestInit = {
+ body: content_,
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processAccount_PostAccount(_response);
+ });
+ }
+
+ protected processAccount_PostAccount(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Account;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ account_GetAccount(name: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/Account?";
+ if (name !== undefined && name !== null)
+ url_ += "name=" + encodeURIComponent("" + name) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processAccount_GetAccount(_response);
+ });
+ }
+
+ protected processAccount_GetAccount(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Account;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ account_DeleteAccount(name: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/Account?";
+ if (name !== undefined && name !== null)
+ url_ += "name=" + encodeURIComponent("" + name) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "DELETE",
+ headers: {
+ "Accept": "application/octet-stream"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processAccount_DeleteAccount(_response);
+ });
+ }
+
+ protected processAccount_DeleteAccount(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200 || status === 206) {
+ const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
+ let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
+ let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
+ if (fileName) {
+ fileName = decodeURIComponent(fileName);
+ } else {
+ fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
+ fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
+ }
+ return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ account_GetAccounts(): Promise {
+ let url_ = this.baseUrl + "/Account/accounts";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processAccount_GetAccounts(_response);
+ });
+ }
+
+ protected processAccount_GetAccounts(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Account[];
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ account_GetAccountsBalances(): Promise {
+ let url_ = this.baseUrl + "/Account/balances";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processAccount_GetAccountsBalances(_response);
+ });
+ }
+
+ protected processAccount_GetAccountsBalances(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Account[];
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+}
+
+export class BacktestClient extends AuthorizedApiBase {
+ private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };
+ private baseUrl: string;
+ protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
+
+ constructor(configuration: IConfig, baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {
+ super(configuration);
+ this.http = http ? http : window as any;
+ this.baseUrl = baseUrl ?? "http://localhost:5000";
+ }
+
+ backtest_Backtests(): Promise {
+ let url_ = this.baseUrl + "/Backtest";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBacktest_Backtests(_response);
+ });
+ }
+
+ protected processBacktest_Backtests(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Backtest[];
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ backtest_DeleteBacktest(id: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/Backtest?";
+ if (id !== undefined && id !== null)
+ url_ += "id=" + encodeURIComponent("" + id) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "DELETE",
+ headers: {
+ "Accept": "application/octet-stream"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBacktest_DeleteBacktest(_response);
+ });
+ }
+
+ protected processBacktest_DeleteBacktest(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200 || status === 206) {
+ const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
+ let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
+ let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
+ if (fileName) {
+ fileName = decodeURIComponent(fileName);
+ } else {
+ fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
+ fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
+ }
+ return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ backtest_Backtest(id: string): Promise {
+ let url_ = this.baseUrl + "/Backtest/{id}";
+ if (id === undefined || id === null)
+ throw new Error("The parameter 'id' must be defined.");
+ url_ = url_.replace("{id}", encodeURIComponent("" + id));
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBacktest_Backtest(_response);
+ });
+ }
+
+ protected processBacktest_Backtest(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Backtest;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ backtest_Run(accountName: string | null | undefined, botType: BotType | undefined, ticker: Ticker | undefined, scenarioName: string | null | undefined, timeframe: Timeframe | undefined, watchOnly: boolean | undefined, balance: number | undefined, moneyManagementName: string | null | undefined, startDate: Date | undefined, endDate: Date | undefined, save: boolean | undefined, cooldownPeriod: number | undefined, maxLossStreak: number | undefined, moneyManagement: MoneyManagement | undefined): Promise {
+ let url_ = this.baseUrl + "/Backtest/Run?";
+ if (accountName !== undefined && accountName !== null)
+ url_ += "accountName=" + encodeURIComponent("" + accountName) + "&";
+ if (botType === null)
+ throw new Error("The parameter 'botType' cannot be null.");
+ else if (botType !== undefined)
+ url_ += "botType=" + encodeURIComponent("" + botType) + "&";
+ if (ticker === null)
+ throw new Error("The parameter 'ticker' cannot be null.");
+ else if (ticker !== undefined)
+ url_ += "ticker=" + encodeURIComponent("" + ticker) + "&";
+ if (scenarioName !== undefined && scenarioName !== null)
+ url_ += "scenarioName=" + encodeURIComponent("" + scenarioName) + "&";
+ if (timeframe === null)
+ throw new Error("The parameter 'timeframe' cannot be null.");
+ else if (timeframe !== undefined)
+ url_ += "timeframe=" + encodeURIComponent("" + timeframe) + "&";
+ if (watchOnly === null)
+ throw new Error("The parameter 'watchOnly' cannot be null.");
+ else if (watchOnly !== undefined)
+ url_ += "watchOnly=" + encodeURIComponent("" + watchOnly) + "&";
+ if (balance === null)
+ throw new Error("The parameter 'balance' cannot be null.");
+ else if (balance !== undefined)
+ url_ += "balance=" + encodeURIComponent("" + balance) + "&";
+ if (moneyManagementName !== undefined && moneyManagementName !== null)
+ url_ += "moneyManagementName=" + encodeURIComponent("" + moneyManagementName) + "&";
+ if (startDate === null)
+ throw new Error("The parameter 'startDate' cannot be null.");
+ else if (startDate !== undefined)
+ url_ += "startDate=" + encodeURIComponent(startDate ? "" + startDate.toISOString() : "") + "&";
+ if (endDate === null)
+ throw new Error("The parameter 'endDate' cannot be null.");
+ else if (endDate !== undefined)
+ url_ += "endDate=" + encodeURIComponent(endDate ? "" + endDate.toISOString() : "") + "&";
+ if (save === null)
+ throw new Error("The parameter 'save' cannot be null.");
+ else if (save !== undefined)
+ url_ += "save=" + encodeURIComponent("" + save) + "&";
+ if (cooldownPeriod === null)
+ throw new Error("The parameter 'cooldownPeriod' cannot be null.");
+ else if (cooldownPeriod !== undefined)
+ url_ += "cooldownPeriod=" + encodeURIComponent("" + cooldownPeriod) + "&";
+ if (maxLossStreak === null)
+ throw new Error("The parameter 'maxLossStreak' cannot be null.");
+ else if (maxLossStreak !== undefined)
+ url_ += "maxLossStreak=" + encodeURIComponent("" + maxLossStreak) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ const content_ = JSON.stringify(moneyManagement);
+
+ let options_: RequestInit = {
+ body: content_,
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBacktest_Run(_response);
+ });
+ }
+
+ protected processBacktest_Run(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Backtest;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+}
+
+export class BotClient extends AuthorizedApiBase {
+ private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };
+ private baseUrl: string;
+ protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
+
+ constructor(configuration: IConfig, baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {
+ super(configuration);
+ this.http = http ? http : window as any;
+ this.baseUrl = baseUrl ?? "http://localhost:5000";
+ }
+
+ bot_Start(request: StartBotRequest): Promise {
+ let url_ = this.baseUrl + "/Bot/Start";
+ url_ = url_.replace(/[?&]$/, "");
+
+ const content_ = JSON.stringify(request);
+
+ let options_: RequestInit = {
+ body: content_,
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBot_Start(_response);
+ });
+ }
+
+ protected processBot_Start(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as string;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ bot_Stop(botType: BotType | undefined, botName: string | null | undefined): Promise {
+ 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 (botName !== undefined && botName !== null)
+ url_ += "botName=" + encodeURIComponent("" + botName) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBot_Stop(_response);
+ });
+ }
+
+ protected processBot_Stop(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as string;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ bot_Delete(botName: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/Bot/Delete?";
+ if (botName !== undefined && botName !== null)
+ url_ += "botName=" + encodeURIComponent("" + botName) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "DELETE",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBot_Delete(_response);
+ });
+ }
+
+ protected processBot_Delete(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as boolean;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ bot_StopAll(): Promise {
+ let url_ = this.baseUrl + "/Bot/stop-all";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "POST",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBot_StopAll(_response);
+ });
+ }
+
+ protected processBot_StopAll(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as string;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ bot_Restart(botType: BotType | undefined, botName: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/Bot/Restart?";
+ if (botType === null)
+ throw new Error("The parameter 'botType' cannot be null.");
+ else if (botType !== undefined)
+ url_ += "botType=" + encodeURIComponent("" + botType) + "&";
+ if (botName !== undefined && botName !== null)
+ url_ += "botName=" + encodeURIComponent("" + botName) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBot_Restart(_response);
+ });
+ }
+
+ protected processBot_Restart(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as string;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ bot_RestartAll(): Promise {
+ let url_ = this.baseUrl + "/Bot/restart-all";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "POST",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBot_RestartAll(_response);
+ });
+ }
+
+ protected processBot_RestartAll(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as string;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ bot_ToggleIsForWatching(botName: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/Bot/ToggleIsForWatching?";
+ if (botName !== undefined && botName !== null)
+ url_ += "botName=" + encodeURIComponent("" + botName) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBot_ToggleIsForWatching(_response);
+ });
+ }
+
+ protected processBot_ToggleIsForWatching(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as string;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ bot_GetActiveBots(): Promise {
+ let url_ = this.baseUrl + "/Bot";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBot_GetActiveBots(_response);
+ });
+ }
+
+ protected processBot_GetActiveBots(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as TradingBot[];
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ bot_OpenPositionManually(request: OpenPositionManuallyRequest): Promise {
+ let url_ = this.baseUrl + "/Bot/OpenPosition";
+ url_ = url_.replace(/[?&]$/, "");
+
+ const content_ = JSON.stringify(request);
+
+ let options_: RequestInit = {
+ body: content_,
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBot_OpenPositionManually(_response);
+ });
+ }
+
+ protected processBot_OpenPositionManually(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Position;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ bot_ClosePosition(request: ClosePositionRequest): Promise {
+ let url_ = this.baseUrl + "/Bot/ClosePosition";
+ url_ = url_.replace(/[?&]$/, "");
+
+ const content_ = JSON.stringify(request);
+
+ let options_: RequestInit = {
+ body: content_,
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processBot_ClosePosition(_response);
+ });
+ }
+
+ protected processBot_ClosePosition(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Position;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+}
+
+export class DataClient extends AuthorizedApiBase {
+ private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };
+ private baseUrl: string;
+ protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
+
+ constructor(configuration: IConfig, baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {
+ super(configuration);
+ this.http = http ? http : window as any;
+ this.baseUrl = baseUrl ?? "http://localhost:5000";
+ }
+
+ data_GetTickers(timeframe: Timeframe | undefined): Promise {
+ let url_ = this.baseUrl + "/Data/GetTickers?";
+ if (timeframe === null)
+ throw new Error("The parameter 'timeframe' cannot be null.");
+ else if (timeframe !== undefined)
+ url_ += "timeframe=" + encodeURIComponent("" + timeframe) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "POST",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processData_GetTickers(_response);
+ });
+ }
+
+ protected processData_GetTickers(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Ticker[];
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ data_GetSpotlight(): Promise {
+ let url_ = this.baseUrl + "/Data/Spotlight";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processData_GetSpotlight(_response);
+ });
+ }
+
+ protected processData_GetSpotlight(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as SpotlightOverview;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ data_GetCandles(exchange: TradingExchanges | undefined, ticker: Ticker | undefined, startDate: Date | undefined, timeframe: Timeframe | undefined): Promise {
+ let url_ = this.baseUrl + "/Data/GetCandles?";
+ if (exchange === null)
+ throw new Error("The parameter 'exchange' cannot be null.");
+ else if (exchange !== undefined)
+ url_ += "exchange=" + encodeURIComponent("" + exchange) + "&";
+ if (ticker === null)
+ throw new Error("The parameter 'ticker' cannot be null.");
+ else if (ticker !== undefined)
+ url_ += "ticker=" + encodeURIComponent("" + ticker) + "&";
+ if (startDate === null)
+ throw new Error("The parameter 'startDate' cannot be null.");
+ else if (startDate !== undefined)
+ url_ += "startDate=" + encodeURIComponent(startDate ? "" + startDate.toISOString() : "") + "&";
+ if (timeframe === null)
+ throw new Error("The parameter 'timeframe' cannot be null.");
+ else if (timeframe !== undefined)
+ url_ += "timeframe=" + encodeURIComponent("" + timeframe) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processData_GetCandles(_response);
+ });
+ }
+
+ protected processData_GetCandles(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Candle[];
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ data_GetStrategiesStatistics(): Promise {
+ let url_ = this.baseUrl + "/Data/GetStrategiesStatistics";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processData_GetStrategiesStatistics(_response);
+ });
+ }
+
+ protected processData_GetStrategiesStatistics(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as StrategiesStatisticsViewModel;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ data_GetTopStrategies(): Promise {
+ let url_ = this.baseUrl + "/Data/GetTopStrategies";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processData_GetTopStrategies(_response);
+ });
+ }
+
+ protected processData_GetTopStrategies(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as TopStrategiesViewModel;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ data_GetUserStrategies(agentName: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/Data/GetUserStrategies?";
+ if (agentName !== undefined && agentName !== null)
+ url_ += "agentName=" + encodeURIComponent("" + agentName) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processData_GetUserStrategies(_response);
+ });
+ }
+
+ protected processData_GetUserStrategies(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UserStrategyDetailsViewModel[];
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ data_GetUserStrategy(agentName: string | null | undefined, strategyName: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/Data/GetUserStrategy?";
+ if (agentName !== undefined && agentName !== null)
+ url_ += "agentName=" + encodeURIComponent("" + agentName) + "&";
+ if (strategyName !== undefined && strategyName !== null)
+ url_ += "strategyName=" + encodeURIComponent("" + strategyName) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processData_GetUserStrategy(_response);
+ });
+ }
+
+ protected processData_GetUserStrategy(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UserStrategyDetailsViewModel;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ data_GetPlatformSummary(timeFilter: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/Data/GetPlatformSummary?";
+ if (timeFilter !== undefined && timeFilter !== null)
+ url_ += "timeFilter=" + encodeURIComponent("" + timeFilter) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processData_GetPlatformSummary(_response);
+ });
+ }
+
+ protected processData_GetPlatformSummary(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as PlatformSummaryViewModel;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+}
+
+export class MoneyManagementClient extends AuthorizedApiBase {
+ private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };
+ private baseUrl: string;
+ protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
+
+ constructor(configuration: IConfig, baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {
+ super(configuration);
+ this.http = http ? http : window as any;
+ this.baseUrl = baseUrl ?? "http://localhost:5000";
+ }
+
+ moneyManagement_PostMoneyManagement(moneyManagement: MoneyManagement): Promise {
+ let url_ = this.baseUrl + "/MoneyManagement";
+ url_ = url_.replace(/[?&]$/, "");
+
+ const content_ = JSON.stringify(moneyManagement);
+
+ let options_: RequestInit = {
+ body: content_,
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processMoneyManagement_PostMoneyManagement(_response);
+ });
+ }
+
+ protected processMoneyManagement_PostMoneyManagement(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as MoneyManagement;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ moneyManagement_GetMoneyManagement(name: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/MoneyManagement?";
+ if (name !== undefined && name !== null)
+ url_ += "name=" + encodeURIComponent("" + name) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processMoneyManagement_GetMoneyManagement(_response);
+ });
+ }
+
+ protected processMoneyManagement_GetMoneyManagement(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as MoneyManagement;
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ moneyManagement_DeleteMoneyManagement(name: string | null | undefined): Promise {
+ let url_ = this.baseUrl + "/MoneyManagement?";
+ if (name !== undefined && name !== null)
+ url_ += "name=" + encodeURIComponent("" + name) + "&";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "DELETE",
+ headers: {
+ "Accept": "application/octet-stream"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processMoneyManagement_DeleteMoneyManagement(_response);
+ });
+ }
+
+ protected processMoneyManagement_DeleteMoneyManagement(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200 || status === 206) {
+ const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
+ let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
+ let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
+ if (fileName) {
+ fileName = decodeURIComponent(fileName);
+ } else {
+ fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
+ fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
+ }
+ return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+
+ moneyManagement_GetMoneyManagements(): Promise {
+ let url_ = this.baseUrl + "/MoneyManagement/moneymanagements";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processMoneyManagement_GetMoneyManagements(_response);
+ });
+ }
+
+ protected processMoneyManagement_GetMoneyManagements(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as MoneyManagement[];
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve(null as any);
+ }
+}
+
+export class ScenarioClient extends AuthorizedApiBase {
+ private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };
+ private baseUrl: string;
+ protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
+
+ constructor(configuration: IConfig, baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {
+ super(configuration);
+ this.http = http ? http : window as any;
+ this.baseUrl = baseUrl ?? "http://localhost:5000";
+ }
+
+ scenario_GetScenarios(): Promise {
+ let url_ = this.baseUrl + "/Scenario";
+ url_ = url_.replace(/[?&]$/, "");
+
+ let options_: RequestInit = {
+ method: "GET",
+ headers: {
+ "Accept": "application/json"
+ }
+ };
+
+ return this.transformOptions(options_).then(transformedOptions_ => {
+ return this.http.fetch(url_, transformedOptions_);
+ }).then((_response: Response) => {
+ return this.processScenario_GetScenarios(_response);
+ });
+ }
+
+ protected processScenario_GetScenarios(response: Response): Promise {
+ const status = response.status;
+ let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
+ if (status === 200) {
+ return response.text().then((_responseText) => {
+ let result200: any = null;
+ result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Scenario[];
+ return result200;
+ });
+ } else if (status !== 200 && status !== 204) {
+ return response.text().then((_responseText) => {
+ return throwException("An unexpected server error occurred.", status, _responseText, _headers);
+ });
+ }
+ return Promise.resolve