diff --git a/assets/Todo-v2.md b/assets/Todo-v2.md
index 8f4c220..e6e2068 100644
--- a/assets/Todo-v2.md
+++ b/assets/Todo-v2.md
@@ -151,6 +151,8 @@ ________________________________________________________________________________
- [ ] Create method to update the money management use by the bot
- [ ] Implement from/to tickers array pattern
- [ ] Extract all managing trade method into a TradingBox class => Create composable trading bot type easily
+- [ ] Bot backup worker: Every x, get saved bots and check if still running. If not running call api to reboot bot.
+- [ ] Create worker to fetch the biggest spread between long\short funding rate and send alert when most profitable delta neutral position is found
# Front-end
## Improve Account page
diff --git a/scripts/docker-deploy-sandbox - Copy.cmd b/scripts/docker-deploy-local.cmd
similarity index 84%
rename from scripts/docker-deploy-sandbox - Copy.cmd
rename to scripts/docker-deploy-local.cmd
index e554517..cc62723 100644
--- a/scripts/docker-deploy-sandbox - Copy.cmd
+++ b/scripts/docker-deploy-local.cmd
@@ -2,4 +2,4 @@ cd ..
cd .\src\
docker build -t managing.api -f ./Managing.Api/Dockerfile . --no-cache
docker build -t managing.api.workers -f ./Managing.Api.Workers/Dockerfile . --no-cache
-docker-compose -f ./Managing.Docker/docker-compose.yml -f ./Managing.Docker/docker-compose.sandbox.yml up -d
\ No newline at end of file
+docker-compose -f ./Managing.Docker/docker-compose.yml -f ./Managing.Docker/docker-compose.local.yml up -d
\ No newline at end of file
diff --git a/src/Managing.Api.Workers/Managing.Api.Workers.csproj b/src/Managing.Api.Workers/Managing.Api.Workers.csproj
index c046e54..2e99aee 100644
--- a/src/Managing.Api.Workers/Managing.Api.Workers.csproj
+++ b/src/Managing.Api.Workers/Managing.Api.Workers.csproj
@@ -32,13 +32,13 @@
-
+
Always
Always
-
+
Always
diff --git a/src/Managing.Api.Workers/Program.cs b/src/Managing.Api.Workers/Program.cs
index a951a82..80d871e 100644
--- a/src/Managing.Api.Workers/Program.cs
+++ b/src/Managing.Api.Workers/Program.cs
@@ -14,8 +14,9 @@ using Serilog.Sinks.Elasticsearch;
// Builder
var builder = WebApplication.CreateBuilder(args);
-builder.Configuration
- .AddEnvironmentVariables();
+builder.Configuration.SetBasePath(System.AppContext.BaseDirectory);
+builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+ .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json");
builder.Host.UseSerilog((hostBuilder, loggerConfiguration) =>
{
diff --git a/src/Managing.Api.Workers/Workers/FeeWorker.cs b/src/Managing.Api.Workers/Workers/FeeWorker.cs
index a0aa579..8ea937b 100644
--- a/src/Managing.Api.Workers/Workers/FeeWorker.cs
+++ b/src/Managing.Api.Workers/Workers/FeeWorker.cs
@@ -1,4 +1,5 @@
using Managing.Application.Abstractions.Services;
+using Managing.Application.Workers;
using Managing.Application.Workers.Abstractions;
using static Managing.Common.Enums;
diff --git a/src/Managing.Api.Workers/Workers/LeaderboardWorker.cs b/src/Managing.Api.Workers/Workers/LeaderboardWorker.cs
index 96feb0e..f85bf6c 100644
--- a/src/Managing.Api.Workers/Workers/LeaderboardWorker.cs
+++ b/src/Managing.Api.Workers/Workers/LeaderboardWorker.cs
@@ -1,4 +1,5 @@
-using Managing.Application.Workers.Abstractions;
+using Managing.Application.Workers;
+using Managing.Application.Workers.Abstractions;
using static Managing.Common.Enums;
namespace Managing.Api.Workers.Workers;
diff --git a/src/Managing.Api.Workers/Workers/NoobiesboardWorker.cs b/src/Managing.Api.Workers/Workers/NoobiesboardWorker.cs
index 13aa38a..0ca651a 100644
--- a/src/Managing.Api.Workers/Workers/NoobiesboardWorker.cs
+++ b/src/Managing.Api.Workers/Workers/NoobiesboardWorker.cs
@@ -1,4 +1,5 @@
-using Managing.Application.Workers.Abstractions;
+using Managing.Application.Workers;
+using Managing.Application.Workers.Abstractions;
using static Managing.Common.Enums;
namespace Managing.Api.Workers.Workers;
diff --git a/src/Managing.Api.Workers/Workers/PositionFetcher.cs b/src/Managing.Api.Workers/Workers/PositionFetcher.cs
index 4e66188..59664d8 100644
--- a/src/Managing.Api.Workers/Workers/PositionFetcher.cs
+++ b/src/Managing.Api.Workers/Workers/PositionFetcher.cs
@@ -1,5 +1,6 @@
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
+using Managing.Application.Workers;
using Managing.Application.Workers.Abstractions;
using Microsoft.AspNetCore.SignalR;
using static Managing.Common.Enums;
diff --git a/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs b/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs
index 61811e5..f9fa8f7 100644
--- a/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs
+++ b/src/Managing.Api.Workers/Workers/PositionManagerWorker.cs
@@ -1,4 +1,5 @@
using Managing.Application.Abstractions.Services;
+using Managing.Application.Workers;
using Managing.Application.Workers.Abstractions;
using Managing.Domain.Trades;
using static Managing.Common.Enums;
diff --git a/src/Managing.Api.Workers/Workers/PricesBaseWorker.cs b/src/Managing.Api.Workers/Workers/PricesBaseWorker.cs
index d3816e2..c247fca 100644
--- a/src/Managing.Api.Workers/Workers/PricesBaseWorker.cs
+++ b/src/Managing.Api.Workers/Workers/PricesBaseWorker.cs
@@ -1,4 +1,5 @@
-using Managing.Application.Workers.Abstractions;
+using Managing.Application.Workers;
+using Managing.Application.Workers.Abstractions;
using static Managing.Common.Enums;
namespace Managing.Api.Workers.Workers;
diff --git a/src/Managing.Api.Workers/Workers/SpotlightWorker.cs b/src/Managing.Api.Workers/Workers/SpotlightWorker.cs
index 91e550e..0a23db8 100644
--- a/src/Managing.Api.Workers/Workers/SpotlightWorker.cs
+++ b/src/Managing.Api.Workers/Workers/SpotlightWorker.cs
@@ -1,4 +1,5 @@
-using Managing.Application.Workers.Abstractions;
+using Managing.Application.Workers;
+using Managing.Application.Workers.Abstractions;
using Managing.Common;
namespace Managing.Api.Workers.Workers;
diff --git a/src/Managing.Api.Workers/Workers/TopVolumeTickerWorker.cs b/src/Managing.Api.Workers/Workers/TopVolumeTickerWorker.cs
index e103728..602f584 100644
--- a/src/Managing.Api.Workers/Workers/TopVolumeTickerWorker.cs
+++ b/src/Managing.Api.Workers/Workers/TopVolumeTickerWorker.cs
@@ -1,4 +1,5 @@
-using Managing.Application.Workers.Abstractions;
+using Managing.Application.Workers;
+using Managing.Application.Workers.Abstractions;
using static Managing.Common.Enums;
namespace Managing.Api.Workers.Workers;
diff --git a/src/Managing.Api.Workers/Workers/TraderWatcher.cs b/src/Managing.Api.Workers/Workers/TraderWatcher.cs
index 90895a5..b9f2132 100644
--- a/src/Managing.Api.Workers/Workers/TraderWatcher.cs
+++ b/src/Managing.Api.Workers/Workers/TraderWatcher.cs
@@ -1,4 +1,5 @@
using Managing.Application.Abstractions.Services;
+using Managing.Application.Workers;
using Managing.Application.Workers.Abstractions;
using static Managing.Common.Enums;
diff --git a/src/Managing.Api.Workers/appsettings.Oda-docker.json b/src/Managing.Api.Workers/appsettings.Oda-docker.json
index e81d760..efe660f 100644
--- a/src/Managing.Api.Workers/appsettings.Oda-docker.json
+++ b/src/Managing.Api.Workers/appsettings.Oda-docker.json
@@ -5,8 +5,8 @@
},
"InfluxDb": {
"Url": "http://influxdb:8086/",
- "Organization": "",
- "Token": ""
+ "Organization": "managing-org",
+ "Token": "Fw2FPL2OwTzDHzSbR2Sd5xs0EKQYy00Q-hYKYAhr9cC1_q5YySONpxuf_Ck0PTjyUiF13xXmi__bu_pXH-H9zA=="
},
"Serilog": {
"MinimumLevel": {
diff --git a/src/Managing.Api.Workers/appsettings.Oda.json b/src/Managing.Api.Workers/appsettings.Oda.json
index d9754cb..643d440 100644
--- a/src/Managing.Api.Workers/appsettings.Oda.json
+++ b/src/Managing.Api.Workers/appsettings.Oda.json
@@ -1,7 +1,7 @@
{
"ManagingDatabase": {
"ConnectionString": "mongodb://localhost:27017",
- "DatabaseName": "ManagingDb",
+ "DatabaseName": "ManagingDb"
},
"InfluxDb": {
"Url": "http://localhost:8086/",
diff --git a/src/Managing.Api/Managing.Api.csproj b/src/Managing.Api/Managing.Api.csproj
index be05f0e..843053a 100644
--- a/src/Managing.Api/Managing.Api.csproj
+++ b/src/Managing.Api/Managing.Api.csproj
@@ -36,6 +36,9 @@
Always
+
+ Always
+
Always
diff --git a/src/Managing.Api/Program.cs b/src/Managing.Api/Program.cs
index 7d8eba9..0ad26cd 100644
--- a/src/Managing.Api/Program.cs
+++ b/src/Managing.Api/Program.cs
@@ -3,6 +3,7 @@ using System.Text.Json.Serialization;
using Managing.Api.Authorization;
using Managing.Api.Exceptions;
using Managing.Api.Filters;
+using Managing.Api.Workers;
using Managing.Application.Hubs;
using Managing.Bootstrap;
using Managing.Common;
@@ -17,12 +18,15 @@ using Serilog.Sinks.Elasticsearch;
// Builder
var builder = WebApplication.CreateBuilder(args);
-builder.Configuration.AddJsonFile("appsettings.Lowpro.json", optional: true, reloadOnChange: true)
+builder.Configuration.SetBasePath(System.AppContext.BaseDirectory);
+builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+ .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json")
.AddJsonFile($"config.{builder.Environment.EnvironmentName}.json",
optional: true, reloadOnChange: true);
builder.Configuration.AddEnvironmentVariables();
builder.Configuration.AddUserSecrets();
+
builder.Host.UseSerilog((hostBuilder, loggerConfiguration) =>
{
var envName = builder.Environment.EnvironmentName.ToLower().Replace(".", "-");
@@ -123,6 +127,7 @@ builder.Services.AddSwaggerGen(options =>
});
builder.WebHost.SetupDiscordBot();
+builder.Services.AddHostedService();
// App
var app = builder.Build();
diff --git a/src/Managing.Api/Workers/BotManagerWorker.cs b/src/Managing.Api/Workers/BotManagerWorker.cs
new file mode 100644
index 0000000..b58aa6f
--- /dev/null
+++ b/src/Managing.Api/Workers/BotManagerWorker.cs
@@ -0,0 +1,23 @@
+using Managing.Application.ManageBot;
+using Managing.Application.Workers;
+using Managing.Application.Workers.Abstractions;
+using MediatR;
+using static Managing.Common.Enums;
+
+namespace Managing.Api.Workers;
+
+public class BotManagerWorker(
+ ILogger logger,
+ IMediator mediadior,
+ IWorkerService workerService)
+ : BaseWorker(WorkerType.BotManager,
+ logger,
+ TimeSpan.FromMinutes(1),
+ workerService)
+{
+ protected override async Task Run(CancellationToken cancellationToken)
+ {
+ var loadBackupBotCommand = new LoadBackupBotCommand();
+ await mediadior.Send(loadBackupBotCommand, cancellationToken);
+ }
+}
diff --git a/src/Managing.Api/appsettings.Oda-docker.json b/src/Managing.Api/appsettings.Oda-docker.json
index e59c1c4..9a8a6a3 100644
--- a/src/Managing.Api/appsettings.Oda-docker.json
+++ b/src/Managing.Api/appsettings.Oda-docker.json
@@ -8,7 +8,7 @@
"InfluxDb": {
"Url": "http://influxdb:8086/",
"Organization": "managing-org",
- "Token": ""
+ "Token": "Fw2FPL2OwTzDHzSbR2Sd5xs0EKQYy00Q-hYKYAhr9cC1_q5YySONpxuf_Ck0PTjyUiF13xXmi__bu_pXH-H9zA=="
},
"Serilog": {
"MinimumLevel": {
diff --git a/src/Managing.Api/appsettings.Oda.json b/src/Managing.Api/appsettings.Oda.json
index e9b2a8d..bc7818a 100644
--- a/src/Managing.Api/appsettings.Oda.json
+++ b/src/Managing.Api/appsettings.Oda.json
@@ -5,8 +5,8 @@
},
"InfluxDb": {
"Url": "http://localhost:8086/",
- "Organization": "",
- "Token": ""
+ "Organization": "managing-org",
+ "Token": "Fw2FPL2OwTzDHzSbR2Sd5xs0EKQYy00Q-hYKYAhr9cC1_q5YySONpxuf_Ck0PTjyUiF13xXmi__bu_pXH-H9zA=="
},
"Serilog": {
"MinimumLevel": {
diff --git a/src/Managing.Application.Abstractions/Repositories/IBotRepository.cs b/src/Managing.Application.Abstractions/Repositories/IBotRepository.cs
new file mode 100644
index 0000000..7af45de
--- /dev/null
+++ b/src/Managing.Application.Abstractions/Repositories/IBotRepository.cs
@@ -0,0 +1,7 @@
+public interface IBotRepository
+{
+ Task InsertBotAsync(BotBackup bot);
+ IEnumerable GetBots();
+ Task UpdateBackupBot(BotBackup bot);
+ void DeleteBotBackup(string botName);
+}
\ No newline at end of file
diff --git a/src/Managing.Application.Tests/BotsTests.cs b/src/Managing.Application.Tests/BotsTests.cs
index 3f7400d..e95c049 100644
--- a/src/Managing.Application.Tests/BotsTests.cs
+++ b/src/Managing.Application.Tests/BotsTests.cs
@@ -28,13 +28,14 @@ namespace Managing.Application.Tests
var discordService = new Mock().Object;
var tradingBotLogger = TradingBaseTests.CreateTradingBotLogger();
var backtestLogger = TradingBaseTests.CreateBacktesterLogger();
+ var botService = new Mock().Object;
_botFactory = new BotFactory(
_exchangeService,
tradingBotLogger,
- _moneyManagementService.Object,
discordService,
_accountService.Object,
- _tradingService.Object);
+ _tradingService.Object,
+ botService);
_backtester = new Backtester(_exchangeService, _botFactory, backtestRepository, backtestLogger);
_elapsedTimes = new List();
}
diff --git a/src/Managing.Application.Tests/WorkflowTests.cs b/src/Managing.Application.Tests/WorkflowTests.cs
index 8961bc2..893a221 100644
--- a/src/Managing.Application.Tests/WorkflowTests.cs
+++ b/src/Managing.Application.Tests/WorkflowTests.cs
@@ -1,5 +1,4 @@
-using Managing.Application.Workflows.Flows.Feeds;
-using Managing.Domain.Workflows;
+using Managing.Domain.Workflows;
using Xunit;
using static Managing.Common.Enums;
diff --git a/src/Managing.Api.Workers/Workers/BaseWorker.cs b/src/Managing.Application.Workers/BaseWorker.cs
similarity index 95%
rename from src/Managing.Api.Workers/Workers/BaseWorker.cs
rename to src/Managing.Application.Workers/BaseWorker.cs
index cc11430..04856ff 100644
--- a/src/Managing.Api.Workers/Workers/BaseWorker.cs
+++ b/src/Managing.Application.Workers/BaseWorker.cs
@@ -1,8 +1,10 @@
using Managing.Application.Workers.Abstractions;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
-namespace Managing.Api.Workers;
+namespace Managing.Application.Workers;
public abstract class BaseWorker : BackgroundService where T : class
{
diff --git a/src/Managing.Application.Workers/Managing.Application.Workers.csproj b/src/Managing.Application.Workers/Managing.Application.Workers.csproj
index 3a4c3ec..dfb4696 100644
--- a/src/Managing.Application.Workers/Managing.Application.Workers.csproj
+++ b/src/Managing.Application.Workers/Managing.Application.Workers.csproj
@@ -9,6 +9,7 @@
+
diff --git a/src/Managing.Application/Abstractions/IBotService.cs b/src/Managing.Application/Abstractions/IBotService.cs
new file mode 100644
index 0000000..55eee9d
--- /dev/null
+++ b/src/Managing.Application/Abstractions/IBotService.cs
@@ -0,0 +1,34 @@
+using Managing.Common;
+using Managing.Domain.Bots;
+using Managing.Domain.MoneyManagements;
+using Managing.Domain.Workflows;
+
+namespace Managing.Application.Abstractions;
+
+public interface IBotService
+{
+ void SaveBotBackup(BotBackup botBackup);
+ void SaveBotBackup(string name, Enums.BotType botType, string data);
+ void AddSimpleBotToCache(IBot bot);
+ void AddTradingBotToCache(ITradingBot bot);
+ List GetActiveBots();
+ IEnumerable GetSavedBots();
+ void StartBotFromBackup(BotBackup backupBot);
+
+ ITradingBot CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name, Enums.Ticker ticker,
+ string scenario, Enums.Timeframe interval, bool isForWatchingOnly);
+
+ ITradingBot CreateBacktestScalpingBot(string accountName, MoneyManagement moneyManagement, Enums.Ticker ticker,
+ string scenario, Enums.Timeframe interval, bool isForWatchingOnly);
+
+ ITradingBot CreateFlippingBot(string accountName, MoneyManagement moneyManagement, string name, Enums.Ticker ticker,
+ string scenario, Enums.Timeframe interval, bool isForWatchingOnly);
+
+ ITradingBot CreateBacktestFlippingBot(string accountName, MoneyManagement moneyManagement, Enums.Ticker ticker,
+ string scenario, Enums.Timeframe interval, bool isForWatchingOnly);
+
+ IBot CreateSimpleBot(string botName, Workflow workflow);
+ Task StopBot(string requestName);
+ Task DeleteBot(string requestName);
+ Task RestartBot(string requestName);
+}
\ No newline at end of file
diff --git a/src/Managing.Application/Bots/Base/BotFactory.cs b/src/Managing.Application/Bots/Base/BotFactory.cs
index 539f3f5..ea49f14 100644
--- a/src/Managing.Application/Bots/Base/BotFactory.cs
+++ b/src/Managing.Application/Bots/Base/BotFactory.cs
@@ -10,32 +10,32 @@ namespace Managing.Application.Bots.Base
{
public class BotFactory : IBotFactory
{
- private readonly IMoneyManagementService _moneyManagementService;
private readonly IExchangeService _exchangeService;
private readonly IMessengerService _messengerService;
private readonly IAccountService _accountService;
private readonly ILogger _tradingBotLogger;
private readonly ITradingService _tradingService;
+ private readonly IBotService _botService;
public BotFactory(
IExchangeService exchangeService,
ILogger tradingBotLogger,
- IMoneyManagementService moneyManagementService,
IMessengerService messengerService,
IAccountService accountService,
- ITradingService tradingService)
+ ITradingService tradingService,
+ IBotService botService)
{
_tradingBotLogger = tradingBotLogger;
_exchangeService = exchangeService;
- _moneyManagementService = moneyManagementService;
_messengerService = messengerService;
_accountService = accountService;
_tradingService = tradingService;
+ _botService = botService;
}
IBot IBotFactory.CreateSimpleBot(string botName, Workflow workflow)
{
- return new SimpleBot(botName, _tradingBotLogger, workflow);
+ return new SimpleBot(botName, _tradingBotLogger, workflow, _botService);
}
ITradingBot IBotFactory.CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name, Ticker ticker, string scenario, Timeframe interval, bool isForWatchingOnly)
@@ -52,6 +52,7 @@ namespace Managing.Application.Bots.Base
interval,
_accountService,
_messengerService,
+ _botService,
isForWatchingOnly: isForWatchingOnly);
}
@@ -69,6 +70,7 @@ namespace Managing.Application.Bots.Base
interval,
_accountService,
_messengerService,
+ _botService,
true,
isForWatchingOnly);
}
@@ -87,6 +89,7 @@ namespace Managing.Application.Bots.Base
interval,
_accountService,
_messengerService,
+ _botService,
isForWatchingOnly: isForWatchingOnly);
}
@@ -104,6 +107,7 @@ namespace Managing.Application.Bots.Base
interval,
_accountService,
_messengerService,
+ _botService,
true,
isForWatchingOnly);
}
diff --git a/src/Managing.Application/Bots/FlippingBot.cs b/src/Managing.Application/Bots/FlippingBot.cs
index 058a910..9bf2270 100644
--- a/src/Managing.Application/Bots/FlippingBot.cs
+++ b/src/Managing.Application/Bots/FlippingBot.cs
@@ -1,4 +1,5 @@
-using Microsoft.Extensions.Logging;
+using Managing.Application.Abstractions;
+using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
using Managing.Application.Abstractions.Services;
using Managing.Domain.MoneyManagements;
@@ -18,25 +19,26 @@ namespace Managing.Application.Bots
Timeframe timeframe,
IAccountService accountService,
IMessengerService messengerService,
+ IBotService botService,
bool isForBacktest = false,
bool isForWatchingOnly = false)
: base(accountName,
- moneyManagement,
- name,
- ticker,
- scenario,
- exchangeService,
- logger,
- tradingService,
- timeframe,
- accountService,
- messengerService,
- isForBacktest,
- isForWatchingOnly,
- flipPosition: true)
+ moneyManagement,
+ name,
+ ticker,
+ scenario,
+ exchangeService,
+ logger,
+ tradingService,
+ timeframe,
+ accountService,
+ messengerService,
+ botService,
+ isForBacktest,
+ isForWatchingOnly,
+ flipPosition: true)
{
BotType = BotType.FlippingBot;
- Start();
}
public sealed override void Start()
@@ -46,4 +48,4 @@ namespace Managing.Application.Bots
Logger.LogInformation($"Starting {Name} bot - Status : {Status}");
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Application/Bots/ScalpingBot.cs b/src/Managing.Application/Bots/ScalpingBot.cs
index f770c15..f69c336 100644
--- a/src/Managing.Application/Bots/ScalpingBot.cs
+++ b/src/Managing.Application/Bots/ScalpingBot.cs
@@ -1,4 +1,5 @@
-using Microsoft.Extensions.Logging;
+using Managing.Application.Abstractions;
+using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
using Managing.Application.Abstractions.Services;
using Managing.Domain.MoneyManagements;
@@ -18,6 +19,7 @@ namespace Managing.Application.Bots
Timeframe timeframe,
IAccountService accountService,
IMessengerService messengerService,
+ IBotService botService,
bool isForBacktest = false,
bool isForWatchingOnly = false)
: base(accountName,
@@ -31,11 +33,11 @@ namespace Managing.Application.Bots
timeframe,
accountService,
messengerService,
+ botService,
isForBacktest,
isForWatchingOnly)
{
BotType = BotType.ScalpingBot;
- Start();
}
public sealed override void Start()
diff --git a/src/Managing.Application/Bots/SimpleBot.cs b/src/Managing.Application/Bots/SimpleBot.cs
index ee8feb8..c22f3d4 100644
--- a/src/Managing.Application/Bots/SimpleBot.cs
+++ b/src/Managing.Application/Bots/SimpleBot.cs
@@ -1,20 +1,25 @@
-using Managing.Domain.Bots;
+using Managing.Application.Abstractions;
+using Managing.Domain.Bots;
using Managing.Domain.Workflows;
using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using static Managing.Common.Enums;
namespace Managing.Application.Bots
{
public class SimpleBot : Bot
{
public readonly ILogger Logger;
- private readonly Workflow _workflow;
+ private readonly IBotService _botService;
+ private Workflow _workflow;
- public SimpleBot(string name, ILogger logger, Workflow workflow) : base(name)
+ public SimpleBot(string name, ILogger logger, Workflow workflow, IBotService botService) :
+ base(name)
{
Logger = logger;
+ _botService = botService;
_workflow = workflow;
Interval = 100;
- Start();
}
public override void Start()
@@ -32,8 +37,20 @@ namespace Managing.Application.Bots
Logger.LogInformation(Identifier);
Logger.LogInformation(DateTime.Now.ToString());
await _workflow.Execute();
+ SaveBackup();
Logger.LogInformation("__________________________________________________");
});
}
+
+ public override void SaveBackup()
+ {
+ var data = JsonConvert.SerializeObject(_workflow);
+ _botService.SaveBotBackup(Name, BotType.SimpleBot, data);
+ }
+
+ public override void LoadBackup(BotBackup backup)
+ {
+ _workflow = JsonConvert.DeserializeObject(backup.Data);
+ }
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Application/Bots/TradingBot.cs b/src/Managing.Application/Bots/TradingBot.cs
index 1dde4ab..21c1f80 100644
--- a/src/Managing.Application/Bots/TradingBot.cs
+++ b/src/Managing.Application/Bots/TradingBot.cs
@@ -11,6 +11,7 @@ using Managing.Domain.Shared.Helpers;
using Managing.Domain.Strategies;
using Managing.Domain.Trades;
using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
using static Managing.Common.Enums;
namespace Managing.Application.Bots;
@@ -22,6 +23,7 @@ public class TradingBot : Bot, ITradingBot
public readonly IMessengerService MessengerService;
public readonly IAccountService AccountService;
private readonly ITradingService TradingService;
+ private readonly IBotService BotService;
public Account Account { get; set; }
public HashSet Strategies { get; set; }
@@ -54,6 +56,7 @@ public class TradingBot : Bot, ITradingBot
Timeframe timeframe,
IAccountService accountService,
IMessengerService messengerService,
+ IBotService botService,
bool isForBacktest = false,
bool isForWatchingOnly = false,
bool flipPosition = false)
@@ -63,6 +66,7 @@ public class TradingBot : Bot, ITradingBot
AccountService = accountService;
MessengerService = messengerService;
TradingService = tradingService;
+ BotService = botService;
IsForWatchingOnly = isForWatchingOnly;
FlipPosition = flipPosition;
@@ -97,7 +101,17 @@ public class TradingBot : Bot, ITradingBot
LoadScenario();
await PreloadCandles();
await CancelAllOrders();
- await MessengerService.SendMessage($"Hi everyone, I'm going to run {Name}. \nI will send a message here everytime a signal is triggered by the {string.Join(",", Strategies.Select(s => s.Name))} strategies.");
+
+ try
+ {
+ await MessengerService.SendMessage(
+ $"Hi everyone, I'm going to run {Name}. \nI will send a message here everytime a signal is triggered by the {string.Join(",", Strategies.Select(s => s.Name))} strategies.");
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex, ex.Message);
+ }
+
await InitWorker(Run);
}
@@ -143,7 +157,8 @@ public class TradingBot : Bot, ITradingBot
public async Task Run()
{
Logger.LogInformation($"____________________{Name}____________________");
- Logger.LogInformation($"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Bot : {Name} - Type {BotType} - Ticker : {Ticker}");
+ Logger.LogInformation(
+ $"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Bot : {Name} - Type {BotType} - Ticker : {Ticker}");
var previousCandleCount = Candles.Count;
@@ -158,6 +173,9 @@ public class TradingBot : Bot, ITradingBot
if (!IsForWatchingOnly)
await ManagePositions();
+ if (!IsForBacktest)
+ SaveBackup();
+
await UpdateWalletBalances();
Logger.LogInformation($"Candles : {Candles.Count}");
Logger.LogInformation($"Signals : {Signals.Count}");
@@ -168,6 +186,9 @@ public class TradingBot : Bot, ITradingBot
private async Task PreloadCandles()
{
+ if (Candles.Any())
+ return;
+
var candles = await ExchangeService.GetCandlesInflux(Account.Exchange, Ticker, PreloadSince, Timeframe);
foreach (var candle in candles.Where(c => c.Date < DateTime.Now.ToUniversalTime()))
@@ -203,7 +224,7 @@ public class TradingBot : Bot, ITradingBot
signal.Status = SignalStatus.Expired;
var signalText = $"{Scenario} trigger a signal. Signal told you " +
- $"to {signal.Direction} {Ticker} on {Timeframe}. The confidence in this signal is {signal.Confidence}. Identifier : {signal.Identifier}";
+ $"to {signal.Direction} {Ticker} on {Timeframe}. The confidence in this signal is {signal.Confidence}. Identifier : {signal.Identifier}";
Logger.LogInformation(signalText);
@@ -265,7 +286,9 @@ public class TradingBot : Bot, ITradingBot
{
Logger.LogInformation($"Updating position {positionForSignal.SignalIdentifier}");
- var position = IsForBacktest ? positionForSignal : TradingService.GetPositionByIdentifier(positionForSignal.Identifier);
+ var position = IsForBacktest
+ ? positionForSignal
+ : TradingService.GetPositionByIdentifier(positionForSignal.Identifier);
if (position.Status == (PositionStatus.Finished | PositionStatus.Flipped))
{
@@ -277,32 +300,41 @@ public class TradingBot : Bot, ITradingBot
// For backtesting or force close if not executed on exchange :
// check if position is still open
// Check status, if still open update the status of the position
- var lastCandle = IsForBacktest ? Candles.Last() : ExchangeService.GetCandle(Account, Ticker, DateTime.UtcNow);
+ var lastCandle = IsForBacktest
+ ? Candles.Last()
+ : ExchangeService.GetCandle(Account, Ticker, DateTime.UtcNow);
if (positionForSignal.OriginDirection == TradeDirection.Long)
{
if (positionForSignal.StopLoss.Price >= lastCandle.Low)
{
- await LogInformation($"Closing position - SL {positionForSignal.StopLoss.Price} >= Price {lastCandle.Low}");
- await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss, positionForSignal.StopLoss.Price, true);
+ await LogInformation(
+ $"Closing position - SL {positionForSignal.StopLoss.Price} >= Price {lastCandle.Low}");
+ await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
+ positionForSignal.StopLoss.Price, true);
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
}
else if (positionForSignal.TakeProfit1.Price <= lastCandle.High
- && positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
+ && positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
{
- await LogInformation($"Closing position - TP1 {positionForSignal.TakeProfit1.Price} <= Price {lastCandle.High}");
- await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1, positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
+ await LogInformation(
+ $"Closing position - TP1 {positionForSignal.TakeProfit1.Price} <= Price {lastCandle.High}");
+ await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
+ positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
}
else if (positionForSignal.TakeProfit2?.Price <= lastCandle.High)
{
- await LogInformation($"Closing position - TP2 {positionForSignal.TakeProfit2.Price} <= Price {lastCandle.High}");
- await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2, positionForSignal.TakeProfit2.Price, true);
+ await LogInformation(
+ $"Closing position - TP2 {positionForSignal.TakeProfit2.Price} <= Price {lastCandle.High}");
+ await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
+ positionForSignal.TakeProfit2.Price, true);
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
}
else
{
- Logger.LogInformation($"Position {signal.Identifier} don't need to be update. Position still opened");
+ Logger.LogInformation(
+ $"Position {signal.Identifier} don't need to be update. Position still opened");
}
}
@@ -310,26 +342,33 @@ public class TradingBot : Bot, ITradingBot
{
if (positionForSignal.StopLoss.Price <= lastCandle.High)
{
- await LogInformation($"Closing position - SL {positionForSignal.StopLoss.Price} <= Price {lastCandle.High}");
- await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss, positionForSignal.StopLoss.Price, true);
+ await LogInformation(
+ $"Closing position - SL {positionForSignal.StopLoss.Price} <= Price {lastCandle.High}");
+ await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
+ positionForSignal.StopLoss.Price, true);
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
}
else if (positionForSignal.TakeProfit1.Price >= lastCandle.Low
- && positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
+ && positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
{
- await LogInformation($"Closing position - TP1 {positionForSignal.TakeProfit1.Price} >= Price {lastCandle.Low}");
- await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1, positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
+ await LogInformation(
+ $"Closing position - TP1 {positionForSignal.TakeProfit1.Price} >= Price {lastCandle.Low}");
+ await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
+ positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
}
else if (positionForSignal.TakeProfit2?.Price >= lastCandle.Low)
{
- await LogInformation($"Closing position - TP2 {positionForSignal.TakeProfit2.Price} >= Price {lastCandle.Low}");
- await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2, positionForSignal.TakeProfit2.Price, true);
+ await LogInformation(
+ $"Closing position - TP2 {positionForSignal.TakeProfit2.Price} >= Price {lastCandle.Low}");
+ await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
+ positionForSignal.TakeProfit2.Price, true);
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
}
else
{
- Logger.LogInformation($"Position {signal.Identifier} don't need to be update. Position still opened");
+ Logger.LogInformation(
+ $"Position {signal.Identifier} don't need to be update. Position still opened");
}
}
}
@@ -353,16 +392,17 @@ public class TradingBot : Bot, ITradingBot
}
-
private async Task OpenPosition(Signal signal)
{
// Check if a position is already open
Logger.LogInformation($"Opening position for {signal.Identifier}");
var openedPosition = Positions.FirstOrDefault(p => p.Status == PositionStatus.Filled
- && p.SignalIdentifier != signal.Identifier);
+ && p.SignalIdentifier != signal.Identifier);
- var lastPrice = IsForBacktest ? Candles.Last().Close : ExchangeService.GetPrice(Account, Ticker, DateTime.UtcNow);
+ var lastPrice = IsForBacktest
+ ? Candles.Last().Close
+ : ExchangeService.GetPrice(Account, Ticker, DateTime.UtcNow);
// If position open
if (openedPosition != null)
@@ -373,7 +413,8 @@ public class TradingBot : Bot, ITradingBot
if (openedPosition.OriginDirection == signal.Direction)
{
// An operation is already open for the same direction
- await LogInformation($"Signal {signal.Identifier} try to open a position but {previousSignal.Identifier} is already open for the same direction");
+ await LogInformation(
+ $"Signal {signal.Identifier} try to open a position but {previousSignal.Identifier} is already open for the same direction");
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
}
else
@@ -386,11 +427,13 @@ public class TradingBot : Bot, ITradingBot
await CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true);
await SetPositionStatus(previousSignal.Identifier, PositionStatus.Flipped);
await OpenPosition(signal);
- await LogInformation($"Position {previousSignal.Identifier} flipped by {signal.Identifier} at {lastPrice}$");
+ await LogInformation(
+ $"Position {previousSignal.Identifier} flipped by {signal.Identifier} at {lastPrice}$");
}
else
{
- await LogWarning($"A position is already open for signal {previousSignal.Identifier}. Position flipping is currently not enable, the position will not be flipped.");
+ await LogWarning(
+ $"A position is already open for signal {previousSignal.Identifier}. Position flipping is currently not enable, the position will not be flipped.");
SetSignalStatus(signal.Identifier, SignalStatus.Expired);
}
}
@@ -399,12 +442,14 @@ public class TradingBot : Bot, ITradingBot
{
if (!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");
+ 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;
}
- await LogInformation($"Open position - Date: {signal.Date:T} - SignalIdentifier : {signal.Identifier} - Strategie : {signal.StrategyType}");
+ await LogInformation(
+ $"Open position - Date: {signal.Date:T} - SignalIdentifier : {signal.Identifier} - Strategie : {signal.StrategyType}");
try
{
@@ -459,9 +504,9 @@ public class TradingBot : Bot, ITradingBot
return true;
var lastPosition = Positions.LastOrDefault(p => p.IsFinished()
- && p.SignalIdentifier != signal.Identifier
- && p.ProfitAndLoss.Realized < 0
- && p.OriginDirection == signal.Direction);
+ && p.SignalIdentifier != signal.Identifier
+ && p.ProfitAndLoss.Realized < 0
+ && p.OriginDirection == signal.Direction);
if (lastPosition == null)
return true;
@@ -472,15 +517,18 @@ public class TradingBot : Bot, ITradingBot
return positionSignal.Date < tenCandleAgo.Date;
}
- private async Task CloseTrade(Signal signal, Position position, Trade tradeToClose, decimal lastPrice, bool tradeClosingPosition = false)
+ private async Task CloseTrade(Signal signal, Position position, Trade tradeToClose, decimal lastPrice,
+ bool tradeClosingPosition = false)
{
- if (position.TakeProfit2 != null && position.TakeProfit1.Status == TradeStatus.Filled && tradeToClose.TradeType == TradeType.StopMarket)
+ if (position.TakeProfit2 != null && position.TakeProfit1.Status == TradeStatus.Filled &&
+ tradeToClose.TradeType == TradeType.StopMarket)
{
// If trade is the 2nd Take profit
tradeToClose.Quantity = position.TakeProfit2.Quantity;
}
- await LogInformation($"Trying to close trade {Ticker} at {lastPrice} - Type : {tradeToClose.TradeType} - Quantity : {tradeToClose.Quantity} " +
+ await LogInformation(
+ $"Trying to close trade {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
@@ -494,8 +542,9 @@ public class TradingBot : Bot, ITradingBot
var command = new ClosePositionCommand(position, lastPrice);
try
{
- var closedPosition = await (new ClosePositionCommandHandler(ExchangeService, AccountService, TradingService))
- .Handle(command);
+ var closedPosition =
+ await (new ClosePositionCommandHandler(ExchangeService, AccountService, TradingService))
+ .Handle(command);
if (closedPosition.Status == (PositionStatus.Finished | PositionStatus.Flipped))
{
@@ -521,7 +570,6 @@ public class TradingBot : Bot, ITradingBot
await SetPositionStatus(signal.Identifier, PositionStatus.Finished);
}
}
-
}
}
@@ -534,7 +582,8 @@ public class TradingBot : Bot, ITradingBot
position.SignalIdentifier = previousPosition.SignalIdentifier;
Positions[positionIndex] = position;
SetSignalStatus(position.SignalIdentifier, SignalStatus.Expired);
- Logger.LogInformation($"Position {position.SignalIdentifier} type correctly close. Pnl on position : {position.ProfitAndLoss.Realized}");
+ Logger.LogInformation(
+ $"Position {position.SignalIdentifier} type correctly close. Pnl on position : {position.ProfitAndLoss.Realized}");
}
else
{
@@ -551,7 +600,7 @@ public class TradingBot : Bot, ITradingBot
private async Task CancelAllOrders()
{
- if (!IsForBacktest)
+ if (!IsForBacktest && !IsForWatchingOnly)
{
try
{
@@ -648,4 +697,52 @@ public class TradingBot : Bot, ITradingBot
await MessengerService.SendTradeMessage(message, isBadBehavior);
}
}
+
+ public override void SaveBackup()
+ {
+ var data = new TradingBotBackup
+ {
+ Name = Name,
+ BotType = BotType,
+ Signals = Signals,
+ Positions = Positions,
+ Timeframe = Timeframe,
+ Ticker = Ticker,
+ Scenario = Scenario,
+ AccountName = AccountName,
+ IsForWatchingOnly = IsForWatchingOnly,
+ WalletBalances = WalletBalances,
+ MoneyManagement = MoneyManagement
+ };
+ BotService.SaveBotBackup(Name, BotType, JsonConvert.SerializeObject(data));
+ }
+
+ public override void LoadBackup(BotBackup backup)
+ {
+ var data = JsonConvert.DeserializeObject(backup.Data);
+ Signals = data.Signals;
+ Positions = data.Positions;
+ WalletBalances = data.WalletBalances;
+ MoneyManagement = data.MoneyManagement;
+ Timeframe = data.Timeframe;
+ Ticker = data.Ticker;
+ Scenario = data.Scenario;
+ AccountName = data.AccountName;
+ IsForWatchingOnly = data.IsForWatchingOnly;
+ }
}
+
+public class TradingBotBackup
+{
+ public string Name { get; set; }
+ public BotType BotType { get; set; }
+ public HashSet Signals { get; set; }
+ public List Positions { get; set; }
+ public Timeframe Timeframe { get; set; }
+ public Ticker Ticker { get; set; }
+ public string Scenario { get; set; }
+ public string AccountName { get; set; }
+ public bool IsForWatchingOnly { get; set; }
+ public Dictionary WalletBalances { get; set; }
+ public MoneyManagement MoneyManagement { get; set; }
+}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/BotService.cs b/src/Managing.Application/ManageBot/BotService.cs
new file mode 100644
index 0000000..5291d60
--- /dev/null
+++ b/src/Managing.Application/ManageBot/BotService.cs
@@ -0,0 +1,278 @@
+using System.Collections.Concurrent;
+using Managing.Application.Abstractions;
+using Managing.Application.Abstractions.Services;
+using Managing.Application.Bots;
+using Managing.Common;
+using Managing.Domain.Bots;
+using Managing.Domain.MoneyManagements;
+using Managing.Domain.Workflows;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+
+namespace Managing.Application.ManageBot
+{
+ public class BotService : IBotService
+ {
+ private readonly IBotRepository _botRepository;
+ private readonly IExchangeService _exchangeService;
+ private readonly IMessengerService _messengerService;
+ private readonly IAccountService _accountService;
+ private readonly ILogger _tradingBotLogger;
+ private readonly ITradingService _tradingService;
+
+ private ConcurrentDictionary _botTasks =
+ new ConcurrentDictionary();
+
+ public BotService(IBotRepository botRepository, IExchangeService exchangeService,
+ IMessengerService messengerService, IAccountService accountService, ILogger tradingBotLogger,
+ ITradingService tradingService)
+ {
+ _botRepository = botRepository;
+ _exchangeService = exchangeService;
+ _messengerService = messengerService;
+ _accountService = accountService;
+ _tradingBotLogger = tradingBotLogger;
+ _tradingService = tradingService;
+ }
+
+ public async void SaveBotBackup(BotBackup botBackup)
+ {
+ await _botRepository.InsertBotAsync(botBackup);
+ }
+
+ public BotBackup GetBotBackup(string name)
+ {
+ return _botRepository.GetBots().FirstOrDefault(b => b.Name == name);
+ }
+
+ public void SaveBotBackup(string name, Enums.BotType botType, string data)
+ {
+ var backup = GetBotBackup(name);
+
+ if (backup != null)
+ {
+ backup.Data = data;
+ _botRepository.UpdateBackupBot(backup);
+ return;
+ }
+
+ var botBackup = new BotBackup
+ {
+ Name = name,
+ BotType = botType,
+ Data = data
+ };
+
+ _botRepository.InsertBotAsync(botBackup);
+ }
+
+ public class BotTaskWrapper
+ {
+ public Task Task { get; private set; }
+ public Type BotType { get; private set; }
+ public object BotInstance { get; private set; } // Add this line
+
+ public BotTaskWrapper(Task task, Type botType, object botInstance) // Update constructor
+ {
+ Task = task;
+ BotType = botType;
+ BotInstance = botInstance; // Set the bot instance
+ }
+ }
+
+ public void AddSimpleBotToCache(IBot bot)
+ {
+ var botTask =
+ new BotTaskWrapper(Task.Run(() => bot.Start()), bot.GetType(), bot); // Pass bot as the instance
+ _botTasks.AddOrUpdate(bot.GetName(), botTask, (key, existingVal) => botTask);
+ }
+
+ public void AddTradingBotToCache(ITradingBot bot)
+ {
+ var botTask = new BotTaskWrapper(Task.Run(() => bot.Start()), bot.GetType(), bot);
+ _botTasks.AddOrUpdate(bot.GetName(), botTask, (key, existingVal) => botTask);
+ }
+
+ public List GetActiveBots()
+ {
+ return _botTasks.Values
+ .Where(wrapper => typeof(ITradingBot).IsAssignableFrom(wrapper.BotType))
+ .Select(wrapper => wrapper.BotInstance as ITradingBot)
+ .Where(bot => bot != null)
+ .ToList();
+ }
+
+ public IEnumerable GetSavedBots()
+ {
+ return _botRepository.GetBots();
+ }
+
+ public void StartBotFromBackup(BotBackup backupBot)
+ {
+ object bot = null;
+ Task botTask = null;
+
+ switch (backupBot.BotType)
+ {
+ case Enums.BotType.SimpleBot:
+ bot = CreateSimpleBot(backupBot.Name,
+ null); // Assuming null is an acceptable parameter for workflow
+ botTask = Task.Run(() => ((IBot)bot).Start());
+ break;
+ case Enums.BotType.ScalpingBot:
+ var scalpingBotData = JsonConvert.DeserializeObject(backupBot.Data);
+ bot = CreateScalpingBot(
+ scalpingBotData.AccountName,
+ scalpingBotData.MoneyManagement,
+ backupBot.Name,
+ scalpingBotData.Ticker,
+ scalpingBotData.Scenario,
+ scalpingBotData.Timeframe,
+ scalpingBotData.IsForWatchingOnly);
+ botTask = Task.Run(() => ((ITradingBot)bot).Start());
+ break;
+ case Enums.BotType.FlippingBot:
+ var flippingBotData = JsonConvert.DeserializeObject(backupBot.Data);
+ bot = CreateFlippingBot(
+ flippingBotData.AccountName,
+ flippingBotData.MoneyManagement,
+ backupBot.Name,
+ flippingBotData.Ticker,
+ flippingBotData.Scenario,
+ flippingBotData.Timeframe,
+ flippingBotData.IsForWatchingOnly);
+ botTask = Task.Run(() => ((ITradingBot)bot).Start());
+ break;
+ }
+
+ if (bot != null && botTask != null)
+ {
+ var botWrapper = new BotTaskWrapper(botTask, bot.GetType(), bot);
+ _botTasks.AddOrUpdate(backupBot.Name, botWrapper, (key, existingVal) => botWrapper);
+ }
+ }
+
+ public IBot CreateSimpleBot(string botName, Workflow workflow)
+ {
+ return new SimpleBot(botName, _tradingBotLogger, workflow, this);
+ }
+
+ public async Task StopBot(string botName)
+ {
+ if (_botTasks.TryGetValue(botName, out var botWrapper))
+ {
+ if (botWrapper.BotInstance is IBot bot)
+ {
+ await Task.Run(() =>
+ bot.Stop()); // Assuming Stop is an asynchronous process wrapped in Task.Run for synchronous methods
+ return bot.GetStatus();
+ }
+ }
+
+ return Enums.BotStatus.Down.ToString();
+ }
+
+ public Task DeleteBot(string botName)
+ {
+ if (_botTasks.TryRemove(botName, out _))
+ {
+ _botRepository.DeleteBotBackup(botName);
+ return Task.FromResult(true);
+ }
+
+ return Task.FromResult(false);
+ }
+
+ public Task RestartBot(string botName)
+ {
+ if (_botTasks.TryGetValue(botName, out var botWrapper))
+ {
+ if (botWrapper.BotInstance is IBot bot)
+ {
+ bot.Restart();
+ return Task.FromResult(bot.GetStatus());
+ }
+ }
+
+ return Task.FromResult(Enums.BotStatus.Down.ToString());
+ }
+
+ public ITradingBot CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name,
+ Enums.Ticker ticker, string scenario, Enums.Timeframe interval, bool isForWatchingOnly)
+ {
+ return new ScalpingBot(
+ accountName,
+ moneyManagement,
+ name,
+ scenario,
+ _exchangeService,
+ ticker,
+ _tradingService,
+ _tradingBotLogger,
+ interval,
+ _accountService,
+ _messengerService,
+ this,
+ isForWatchingOnly: isForWatchingOnly);
+ }
+
+ public ITradingBot CreateBacktestScalpingBot(string accountName, MoneyManagement moneyManagement,
+ Enums.Ticker ticker, string scenario, Enums.Timeframe interval, bool isForWatchingOnly)
+ {
+ return new ScalpingBot(
+ accountName,
+ moneyManagement,
+ "BacktestBot",
+ scenario,
+ _exchangeService,
+ ticker,
+ _tradingService,
+ _tradingBotLogger,
+ interval,
+ _accountService,
+ _messengerService,
+ this,
+ true,
+ isForWatchingOnly);
+ }
+
+ public ITradingBot CreateFlippingBot(string accountName, MoneyManagement moneyManagement, string name,
+ Enums.Ticker ticker, string scenario, Enums.Timeframe interval, bool isForWatchingOnly)
+ {
+ return new FlippingBot(
+ accountName,
+ moneyManagement,
+ name,
+ scenario,
+ _exchangeService,
+ ticker,
+ _tradingService,
+ _tradingBotLogger,
+ interval,
+ _accountService,
+ _messengerService,
+ this,
+ isForWatchingOnly: isForWatchingOnly);
+ }
+
+ public ITradingBot CreateBacktestFlippingBot(string accountName, MoneyManagement moneyManagement,
+ Enums.Ticker ticker, string scenario, Enums.Timeframe interval, bool isForWatchingOnly)
+ {
+ return new FlippingBot(
+ accountName,
+ moneyManagement,
+ "BacktestBot",
+ scenario,
+ _exchangeService,
+ ticker,
+ _tradingService,
+ _tradingBotLogger,
+ interval,
+ _accountService,
+ _messengerService,
+ this,
+ true,
+ isForWatchingOnly);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/DeleteBotCommandHandler.cs b/src/Managing.Application/ManageBot/DeleteBotCommandHandler.cs
index 36c4757..7ae2734 100644
--- a/src/Managing.Application/ManageBot/DeleteBotCommandHandler.cs
+++ b/src/Managing.Application/ManageBot/DeleteBotCommandHandler.cs
@@ -8,25 +8,16 @@ namespace Managing.Application.ManageBot;
public class DeleteBotCommandHandler : IRequestHandler
{
private readonly ILogger _log;
- private readonly ITaskCache _taskCache;
+ private readonly IBotService _botService;
- public DeleteBotCommandHandler(ITaskCache taskCache, ILogger log)
+ public DeleteBotCommandHandler(ILogger log, IBotService botService)
{
- _taskCache = taskCache;
_log = log;
+ _botService = botService;
}
public Task Handle(DeleteBotCommand request, CancellationToken cancellationToken)
{
- try
- {
- _taskCache.Invalidate(request.Name);
- return Task.FromResult(true);
- }
- catch (Exception e)
- {
- _log.LogError(e.Message);
- return Task.FromResult(false);
- }
+ return _botService.DeleteBot(request.Name);
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/GetActiveBotsCommandHandler.cs b/src/Managing.Application/ManageBot/GetActiveBotsCommandHandler.cs
index 4ee8e46..930049e 100644
--- a/src/Managing.Application/ManageBot/GetActiveBotsCommandHandler.cs
+++ b/src/Managing.Application/ManageBot/GetActiveBotsCommandHandler.cs
@@ -1,30 +1,15 @@
using Managing.Application.Abstractions;
using Managing.Application.ManageBot.Commands;
-using Managing.Core;
using MediatR;
namespace Managing.Application.ManageBot
{
- public class GetActiveBotsCommandHandler : IRequestHandler>
+ public class GetActiveBotsCommandHandler(IBotService botService)
+ : IRequestHandler>
{
- private readonly ITaskCache taskCache;
-
- public GetActiveBotsCommandHandler(ITaskCache taskCache)
- {
- this.taskCache = taskCache;
- }
-
public Task> Handle(GetActiveBotsCommand request, CancellationToken cancellationToken)
{
- var cachedTask = taskCache.GetCache>();
- var result = new List();
-
- foreach (var item in cachedTask)
- {
- result.Add(item.Value.Result);
- }
-
- return Task.FromResult(result);
+ return Task.FromResult(botService.GetActiveBots());
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/LoadBackupBotCommandHandler.cs b/src/Managing.Application/ManageBot/LoadBackupBotCommandHandler.cs
new file mode 100644
index 0000000..9b0e37c
--- /dev/null
+++ b/src/Managing.Application/ManageBot/LoadBackupBotCommandHandler.cs
@@ -0,0 +1,87 @@
+using MediatR;
+using static Managing.Common.Enums;
+using Managing.Application.Abstractions;
+using Managing.Core;
+using Microsoft.Extensions.Logging;
+
+namespace Managing.Application.ManageBot;
+
+public class LoadBackupBotCommandHandler : IRequestHandler
+{
+ private readonly IBotService _botService;
+ private readonly ILogger _logger;
+
+ public LoadBackupBotCommandHandler(
+ ILogger logger, IBotService botService)
+ {
+ _logger = logger;
+ _botService = botService;
+ }
+
+ public Task Handle(LoadBackupBotCommand request, CancellationToken cancellationToken)
+ {
+ BotStatus botStatus = BotStatus.Down;
+ var backupBots = _botService.GetSavedBots();
+ var activeBots = _botService.GetActiveBots();
+ var result = new Dictionary();
+
+ _logger.LogInformation($"Loading {backupBots.Count()} backup bots");
+
+ foreach (var backupBot in backupBots)
+ {
+ // Check if bot is existing in cache
+ try
+ {
+ switch (backupBot.BotType)
+ {
+ case BotType.SimpleBot:
+ var simpleBot = activeBots.FirstOrDefault(b => b.GetName() == backupBot.Name);
+ if (simpleBot == null)
+ {
+ _logger.LogInformation($"Starting backup bot {backupBot.Name}");
+ _botService.StartBotFromBackup(backupBot);
+ result.Add(simpleBot.GetName(), BotStatus.Backup);
+ }
+ else
+ {
+ result.Add(simpleBot.GetName(), MiscExtensions.ParseEnum(simpleBot.GetStatus()));
+ }
+
+ break;
+ case BotType.ScalpingBot:
+ case BotType.FlippingBot:
+ var scalpingBot = activeBots.FirstOrDefault(b => b.GetName() == backupBot.Name);
+ if (scalpingBot == null)
+ {
+ _logger.LogInformation($"Starting backup bot {backupBot.Name}");
+ _botService.StartBotFromBackup(backupBot);
+ var bots = _botService.GetActiveBots();
+ scalpingBot = bots.FirstOrDefault(b => b.GetName() == backupBot.Name);
+ result.Add(scalpingBot.GetName(), BotStatus.Backup);
+ }
+ else
+ {
+ result.Add(scalpingBot.GetName(),
+ MiscExtensions.ParseEnum(scalpingBot.GetStatus()));
+ }
+
+ break;
+ default:
+ result.Add(backupBot.Name, BotStatus.Down);
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"Error loading bot {backupBot.Name}", ex.Message);
+ result.Add(backupBot.Name, BotStatus.Down);
+ }
+ }
+
+ return Task.FromResult(botStatus.ToString());
+ }
+}
+
+public class LoadBackupBotCommand : IRequest
+{
+}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs b/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs
index b8a1079..dd9a4a2 100644
--- a/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs
+++ b/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs
@@ -1,39 +1,21 @@
using Managing.Application.Abstractions;
using Managing.Application.ManageBot.Commands;
-using Managing.Domain.Bots;
using MediatR;
-using static Managing.Common.Enums;
namespace Managing.Application.ManageBot
{
public class RestartBotCommandHandler : IRequestHandler
{
- private readonly ITaskCache _taskCache;
+ private readonly IBotService _botService;
- public RestartBotCommandHandler(ITaskCache taskCache)
+ public RestartBotCommandHandler(IBotService botService)
{
- _taskCache = taskCache;
+ _botService = botService;
}
public Task Handle(RestartBotCommand request, CancellationToken cancellationToken)
{
- switch (request.BotType)
- {
- case BotType.SimpleBot:
- var simpleBot = _taskCache.Get(request.Name);
- simpleBot.Restart();
- return Task.FromResult(simpleBot.GetStatus());
- case BotType.ScalpingBot:
- var scalpingBot = _taskCache.Get(request.Name);
- scalpingBot.Restart();
- return Task.FromResult(scalpingBot.GetStatus());
- case BotType.FlippingBot:
- var flippingBot = _taskCache.Get(request.Name);
- flippingBot.Restart();
- return Task.FromResult(flippingBot.GetStatus());
- default:
- return Task.FromResult(BotStatus.Down.ToString());
- }
+ return _botService.RestartBot(request.Name);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/StartBotCommandHandler.cs b/src/Managing.Application/ManageBot/StartBotCommandHandler.cs
index 7828678..b98b7c7 100644
--- a/src/Managing.Application/ManageBot/StartBotCommandHandler.cs
+++ b/src/Managing.Application/ManageBot/StartBotCommandHandler.cs
@@ -1,5 +1,4 @@
-using Managing.Domain.Bots;
-using MediatR;
+using MediatR;
using static Managing.Common.Enums;
using Managing.Application.Abstractions;
using Managing.Application.ManageBot.Commands;
@@ -9,13 +8,14 @@ namespace Managing.Application.ManageBot
public class StartBotCommandHandler : IRequestHandler
{
private readonly IBotFactory _botFactory;
- private readonly ITaskCache _taskCache;
+ private readonly IBotService _botService;
private readonly IMoneyManagementService _moneyManagementService;
- public StartBotCommandHandler(IBotFactory botFactory, ITaskCache taskCache, IMoneyManagementService moneyManagementService)
+ public StartBotCommandHandler(IBotFactory botFactory, IBotService botService,
+ IMoneyManagementService moneyManagementService)
{
_botFactory = botFactory;
- _taskCache = taskCache;
+ _botService = botService;
_moneyManagementService = moneyManagementService;
}
@@ -26,17 +26,27 @@ namespace Managing.Application.ManageBot
switch (request.BotType)
{
case BotType.SimpleBot:
- Func> simpleBot = () => Task.FromResult(_botFactory.CreateSimpleBot(request.Name, null));
- return Task.FromResult(_taskCache.AddOrGetExisting(request.Name, simpleBot).Result.GetStatus());
+ var bot = _botFactory.CreateSimpleBot(request.Name, null);
+ bot.Start();
+ _botService.AddSimpleBotToCache(bot);
+ return Task.FromResult(bot.GetStatus());
case BotType.ScalpingBot:
- Func> scalpingBot = () => Task.FromResult(_botFactory.CreateScalpingBot(request.AccountName, moneyManagement, request.Name, request.Ticker, request.Scenario, request.Timeframe, request.IsForWatchingOnly));
- return Task.FromResult(_taskCache.AddOrGetExisting(request.Name, scalpingBot).Result.GetStatus());
+ var sBot = _botFactory.CreateScalpingBot(request.AccountName, moneyManagement, request.Name,
+ request.Ticker, request.Scenario, request.Timeframe, request.IsForWatchingOnly);
+ sBot.Start();
+ _botService.AddTradingBotToCache(sBot);
+ return Task.FromResult(sBot.GetStatus());
case BotType.FlippingBot:
- Func> flippingBot = () => Task.FromResult(_botFactory.CreateFlippingBot(request.AccountName, moneyManagement, request.Name, request.Ticker, request.Scenario, request.Timeframe, request.IsForWatchingOnly));
- return Task.FromResult(_taskCache.AddOrGetExisting(request.Name, flippingBot).Result.GetStatus());
- };
+ var fBot = _botFactory.CreateFlippingBot(request.AccountName, moneyManagement, request.Name,
+ request.Ticker, request.Scenario, request.Timeframe, request.IsForWatchingOnly);
+ fBot.Start();
+ _botService.AddTradingBotToCache(fBot);
+ return Task.FromResult(fBot.GetStatus());
+ }
+
+ ;
return Task.FromResult(botStatus.ToString());
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Application/ManageBot/StopBotCommandHandler.cs b/src/Managing.Application/ManageBot/StopBotCommandHandler.cs
index 5a0a701..e92bbb4 100644
--- a/src/Managing.Application/ManageBot/StopBotCommandHandler.cs
+++ b/src/Managing.Application/ManageBot/StopBotCommandHandler.cs
@@ -1,39 +1,21 @@
using Managing.Application.Abstractions;
using Managing.Application.ManageBot.Commands;
-using Managing.Domain.Bots;
using MediatR;
-using static Managing.Common.Enums;
namespace Managing.Application.ManageBot
{
public class StopBotCommandHandler : IRequestHandler
{
- private readonly ITaskCache _taskCache;
+ private readonly IBotService _botService;
- public StopBotCommandHandler(ITaskCache taskCache)
+ public StopBotCommandHandler(IBotService botService)
{
- _taskCache = taskCache;
+ _botService = botService;
}
public Task Handle(StopBotCommand request, CancellationToken cancellationToken)
{
- switch (request.BotType)
- {
- case BotType.SimpleBot:
- var simpleBot = _taskCache.Get(request.Name);
- simpleBot.Stop();
- return Task.FromResult(simpleBot.GetStatus());
- case BotType.ScalpingBot:
- var scalpingBot = _taskCache.Get(request.Name);
- scalpingBot.Stop();
- return Task.FromResult(scalpingBot.GetStatus());
- case BotType.FlippingBot:
- var flippingBot = _taskCache.Get(request.Name);
- flippingBot.Stop();
- return Task.FromResult(flippingBot.GetStatus());
- default:
- return Task.FromResult(BotStatus.Down.ToString());
- }
+ return _botService.StopBot(request.Name);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Application/Trading/TradingService.cs b/src/Managing.Application/Trading/TradingService.cs
index 47871b5..12f7263 100644
--- a/src/Managing.Application/Trading/TradingService.cs
+++ b/src/Managing.Application/Trading/TradingService.cs
@@ -1,5 +1,4 @@
-using DnsClient.Internal;
-using Managing.Application.Abstractions;
+using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Domain.Accounts;
@@ -9,7 +8,6 @@ using Managing.Domain.Strategies;
using Managing.Domain.Trades;
using Managing.Domain.Shared.Helpers;
using Microsoft.Extensions.Logging;
-using MongoDB.Driver;
using static Managing.Common.Enums;
namespace Managing.Application.Trading;
@@ -206,7 +204,7 @@ public class TradingService : ITradingService
return _cacheService.GetOrSave($"Fee-{account.Exchange}", () =>
{
- return _tradingRepository.GetFee(TradingExchanges.Evm).Cost;
+ return _tradingRepository.GetFee(TradingExchanges.Evm)?.Cost ?? 0m;
}, TimeSpan.FromHours(2));
}
diff --git a/src/Managing.Application/Users/UserService.cs b/src/Managing.Application/Users/UserService.cs
index 84f2e4d..0b37d1a 100644
--- a/src/Managing.Application/Users/UserService.cs
+++ b/src/Managing.Application/Users/UserService.cs
@@ -11,7 +11,7 @@ public class UserService : IUserService
private readonly IUserRepository _userRepository;
private readonly IAccountService _accountService;
- private string[] authorizedAddresses = new string[] { "0x6781920674dA695aa5120d95D80c4B1788046806" };
+ private string[] authorizedAddresses = ["0x6781920674dA695aa5120d95D80c4B1788046806", "0xA2B43AFF0992a47838DF2e6099A8439981f0B717"];
public UserService(
IEvmManager evmManager,
@@ -29,7 +29,7 @@ public class UserService : IUserService
if (!authorizedAddresses.Contains(recoveredAddress))
throw new Exception("Address not authorized");
-
+
if (recoveredAddress == null || !recoveredAddress.Equals(address))
throw new Exception("Address not corresponding");
diff --git a/src/Managing.Application/Workflows/Flows/Trading/OpenPosition.cs b/src/Managing.Application/Workflows/Flows/Trading/OpenPosition.cs
index 67eda2b..c309fd1 100644
--- a/src/Managing.Application/Workflows/Flows/Trading/OpenPosition.cs
+++ b/src/Managing.Application/Workflows/Flows/Trading/OpenPosition.cs
@@ -1,11 +1,8 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
-using Managing.Application.Accounts;
-using Managing.Application.Shared;
using Managing.Application.Trading.Commands;
using Managing.Application.Trading;
using Managing.Domain.Accounts;
-using Managing.Domain.MoneyManagements;
using Managing.Domain.Strategies;
using Managing.Domain.Trades;
using Managing.Domain.Workflows;
diff --git a/src/Managing.Bootstrap/ApiBootstrap.cs b/src/Managing.Bootstrap/ApiBootstrap.cs
index f90c98a..509d180 100644
--- a/src/Managing.Bootstrap/ApiBootstrap.cs
+++ b/src/Managing.Bootstrap/ApiBootstrap.cs
@@ -41,6 +41,7 @@ using Binance.Net.Interfaces.Clients;
using Managing.Infrastructure.Evm.Services;
using Managing.Application.Workflows;
using Managing.Application.Bots.Base;
+using Managing.Application.ManageBot;
namespace Managing.Bootstrap;
@@ -105,11 +106,13 @@ public static class ApiBootstrap
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
// Cache
services.AddDistributedMemoryCache();
services.AddTransient();
- services.AddTransient();
+ services.AddSingleton();
// Processors
services.AddTransient();
@@ -123,6 +126,8 @@ public static class ApiBootstrap
services.AddTransient();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
// Stream
services.AddSingleton();
diff --git a/src/Managing.Bootstrap/WorkersBootstrap.cs b/src/Managing.Bootstrap/WorkersBootstrap.cs
index b6676e1..ba0df40 100644
--- a/src/Managing.Bootstrap/WorkersBootstrap.cs
+++ b/src/Managing.Bootstrap/WorkersBootstrap.cs
@@ -33,6 +33,7 @@ using Managing.Application.Trading.Commands;
using Managing.Domain.Trades;
using Managing.Infrastructure.Evm.Services;
using Managing.Application.Bots.Base;
+using Managing.Application.ManageBot;
namespace Managing.Bootstrap;
@@ -58,6 +59,7 @@ public static class WorkersBootstrap
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
services.AddTransient, OpenPositionCommandHandler>();
services.AddTransient, ClosePositionCommandHandler>();
@@ -93,6 +95,7 @@ public static class WorkersBootstrap
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
// Cache
services.AddDistributedMemoryCache();
diff --git a/src/Managing.Common/Enums.cs b/src/Managing.Common/Enums.cs
index 6bf4d83..b342b8a 100644
--- a/src/Managing.Common/Enums.cs
+++ b/src/Managing.Common/Enums.cs
@@ -73,7 +73,8 @@ public static class Enums
{
Down,
Starting,
- Up
+ Up,
+ Backup
}
public enum SignalStatus
@@ -316,7 +317,8 @@ public static class Enums
PositionFetcher,
TraderWatcher,
LeaderboardWorker,
- Noobiesboard
+ Noobiesboard,
+ BotManager
}
public enum WorkflowUsage
diff --git a/src/Managing.Docker/docker-compose.sandbox.yml b/src/Managing.Docker/docker-compose.local.yml
similarity index 52%
rename from src/Managing.Docker/docker-compose.sandbox.yml
rename to src/Managing.Docker/docker-compose.local.yml
index d4c7d4e..5b4f8b7 100644
--- a/src/Managing.Docker/docker-compose.sandbox.yml
+++ b/src/Managing.Docker/docker-compose.local.yml
@@ -4,9 +4,9 @@ version: '3.4'
services:
managing.api:
environment:
- - ASPNETCORE_ENVIRONMENT=oda-docker
+ - ASPNETCORE_ENVIRONMENT=Oda-docker
- ASPNETCORE_URLS=https://+:443;http://+:80
- - ASPNETCORE_Kestrel__Certificates__Default__Password=!MotdepasseFort11
+ - ASPNETCORE_Kestrel__Certificates__Default__Password=!Managing94
- ASPNETCORE_Kestrel__Certificates__Default__Path=/app/managing_cert.pfx
ports:
- "80:80"
@@ -14,20 +14,23 @@ services:
volumes:
- ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
- ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
+ - /Users/oda/ASP.NET/Https:/root/.aspnet/https:ro
+ - /Users/oda/Microsoft/UserSecrets:/root/.microsoft/usersecrets/$USER_SECRETS_ID
depends_on:
- managingdb
managing.api.workers:
environment:
- - ASPNETCORE_ENVIRONMENT=oda-docker
+ - ASPNETCORE_ENVIRONMENT=Oda-docker
- ASPNETCORE_URLS=https://+:443;http://+:80
- - ASPNETCORE_Kestrel__Certificates__Default__Password=!MotdepasseFort11
+ - ASPNETCORE_Kestrel__Certificates__Default__Password=!Managing94
- ASPNETCORE_Kestrel__Certificates__Default__Path=/app/managing_cert.pfx
ports:
- "81:80"
- "444:443"
volumes:
- ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
+ - /Users/oda/ASP.NET/Https:/root/.aspnet/https:ro
depends_on:
- managingdb
@@ -38,24 +41,24 @@ services:
ports:
- "27017:27017"
- elasticsearch:
- ports:
- - 9200:9200
- volumes:
- - elasticsearch-data:/usr/share/elasticsearch/data
- environment:
- - discovery.type=single-node
- - xpack.monitoring.templates.enabled=true
- - ES_JAVA_OPTS=-Xms1g -Xmx1g
- - xpack.security.enabled=false
+ # elasticsearch:
+ # ports:
+ # - 9200:9200
+ # volumes:
+ # - elasticsearch-data:/usr/share/elasticsearch/data
+ # environment:
+ # - discovery.type=single-node
+ # - xpack.monitoring.templates.enabled=true
+ # - ES_JAVA_OPTS=-Xms1g -Xmx1g
+ # - xpack.security.enabled=false
- kibana:
- ports:
- - 5601:5601
- depends_on:
- - elasticsearch
- environment:
- - ELASTICSEARCH_URL=http://elasticsearch:9200
+ # kibana:
+ # ports:
+ # - 5601:5601
+ # depends_on:
+ # - elasticsearch
+ # environment:
+ # - ELASTICSEARCH_URL=http://elasticsearch:9200
influxdb:
image: influxdb:latest
diff --git a/src/Managing.Docker/docker-compose.yml b/src/Managing.Docker/docker-compose.yml
index f86658c..e8e3f83 100644
--- a/src/Managing.Docker/docker-compose.yml
+++ b/src/Managing.Docker/docker-compose.yml
@@ -22,13 +22,13 @@ services:
networks:
- managing-network
- elasticsearch:
- image: elasticsearch:8.4.1
- networks:
- - managing-network
+ # elasticsearch:
+ # image: elasticsearch:8.4.1
+ # networks:
+ # - managing-network
- kibana:
- image: kibana:8.4.1
+ # kibana:
+ # image: kibana:8.4.1
influxdb:
image: influxdb:latest
diff --git a/src/Managing.Domain/Bots/Bot.cs b/src/Managing.Domain/Bots/Bot.cs
index ca4af2a..9911d32 100644
--- a/src/Managing.Domain/Bots/Bot.cs
+++ b/src/Managing.Domain/Bots/Bot.cs
@@ -71,5 +71,8 @@ namespace Managing.Domain.Bots
{
return Name;
}
+
+ public abstract void SaveBackup();
+ public abstract void LoadBackup(BotBackup backup);
}
}
\ No newline at end of file
diff --git a/src/Managing.Domain/Bots/BotBackup.cs b/src/Managing.Domain/Bots/BotBackup.cs
new file mode 100644
index 0000000..4028572
--- /dev/null
+++ b/src/Managing.Domain/Bots/BotBackup.cs
@@ -0,0 +1,8 @@
+using static Managing.Common.Enums;
+
+public class BotBackup
+{
+ public string Name { get; set; }
+ public BotType BotType { get; set; }
+ public string Data { 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 7572a3d..95a240e 100644
--- a/src/Managing.Domain/Bots/IBot.cs
+++ b/src/Managing.Domain/Bots/IBot.cs
@@ -8,5 +8,7 @@
void Restart();
string GetStatus();
string GetName();
+ void SaveBackup();
+ void LoadBackup(BotBackup backup);
}
}
diff --git a/src/Managing.Infrastructure.Database/BotRepository.cs b/src/Managing.Infrastructure.Database/BotRepository.cs
new file mode 100644
index 0000000..573e351
--- /dev/null
+++ b/src/Managing.Infrastructure.Database/BotRepository.cs
@@ -0,0 +1,39 @@
+using Managing.Infrastructure.Databases.MongoDb;
+using Managing.Infrastructure.Databases.MongoDb.Abstractions;
+using Managing.Infrastructure.Databases.MongoDb.Collections;
+
+namespace Managing.Infrastructure.Databases;
+
+public class BotRepository : IBotRepository
+{
+ private readonly IMongoRepository _botRepository;
+
+ public BotRepository(IMongoRepository botRepository)
+ {
+ _botRepository = botRepository;
+ }
+
+ public async Task InsertBotAsync(BotBackup bot)
+ {
+ await _botRepository.InsertOneAsync(MongoMappers.Map(bot));
+ }
+
+ public IEnumerable GetBots()
+ {
+ var bots = _botRepository.FindAll();
+ return bots.Select(b => MongoMappers.Map(b));
+ }
+
+ public async Task UpdateBackupBot(BotBackup bot)
+ {
+ var b = await _botRepository.FindOneAsync(b => b.Name == bot.Name);
+ var dto = MongoMappers.Map(bot);
+ dto.Id = b.Id;
+ _botRepository.Update(dto);
+ }
+
+ public void DeleteBotBackup(string botName)
+ {
+ _botRepository.DeleteOne(b => b.Name == botName);
+ }
+}
\ No newline at end of file
diff --git a/src/Managing.Infrastructure.Database/MongoDb/Collections/BotDto.cs b/src/Managing.Infrastructure.Database/MongoDb/Collections/BotDto.cs
new file mode 100644
index 0000000..ab7303a
--- /dev/null
+++ b/src/Managing.Infrastructure.Database/MongoDb/Collections/BotDto.cs
@@ -0,0 +1,13 @@
+using Managing.Infrastructure.Databases.MongoDb.Attributes;
+using Managing.Infrastructure.Databases.MongoDb.Configurations;
+using static Managing.Common.Enums;
+
+namespace Managing.Infrastructure.Databases.MongoDb.Collections;
+
+[BsonCollection("Bots")]
+public class BotDto : Document
+{
+ public string Name { get; set; }
+ public string Data { get; set; }
+ public BotType BotType { 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 8412872..e1c0592 100644
--- a/src/Managing.Infrastructure.Database/MongoDb/MongoMappers.cs
+++ b/src/Managing.Infrastructure.Database/MongoDb/MongoMappers.cs
@@ -650,5 +650,29 @@ public static class MongoMappers
};
}
+ internal static BotDto Map(BotBackup bot)
+ {
+ if (bot == null) return null;
+
+ return new BotDto
+ {
+ Name = bot.Name,
+ BotType = bot.BotType,
+ Data = bot.Data,
+ };
+ }
+
+ internal static BotBackup Map(BotDto b)
+ {
+ if (b == null) return null;
+
+ return new BotBackup
+ {
+ Name = b.Name,
+ BotType = b.BotType,
+ Data = b.Data
+ };
+ }
+
#endregion
}
diff --git a/src/Managing.Infrastructure.Exchanges/Exchanges/BinanceProcessor.cs b/src/Managing.Infrastructure.Exchanges/Exchanges/BinanceProcessor.cs
index 71885ad..1516b8e 100644
--- a/src/Managing.Infrastructure.Exchanges/Exchanges/BinanceProcessor.cs
+++ b/src/Managing.Infrastructure.Exchanges/Exchanges/BinanceProcessor.cs
@@ -1,7 +1,6 @@
using Binance.Net.Clients;
using Binance.Net.Enums;
using Binance.Net.Interfaces.Clients;
-using CryptoExchange.Net;
using CryptoExchange.Net.Authentication;
using Managing.Common;
using Managing.Core;
diff --git a/src/Managing.Infrastructure.Exchanges/Exchanges/FtxProcessor.cs b/src/Managing.Infrastructure.Exchanges/Exchanges/FtxProcessor.cs
index de1b687..01fcd5c 100644
--- a/src/Managing.Infrastructure.Exchanges/Exchanges/FtxProcessor.cs
+++ b/src/Managing.Infrastructure.Exchanges/Exchanges/FtxProcessor.cs
@@ -1,5 +1,4 @@
using CryptoExchange.Net.Authentication;
-using FTX.Net.Clients;
using FTX.Net.Interfaces.Clients;
using FTX.Net.Objects;
using Managing.Common;
diff --git a/src/Managing.Infrastructure.Storage/TaskCache.cs b/src/Managing.Infrastructure.Storage/TaskCache.cs
index b89e52e..0461580 100644
--- a/src/Managing.Infrastructure.Storage/TaskCache.cs
+++ b/src/Managing.Infrastructure.Storage/TaskCache.cs
@@ -1,6 +1,5 @@
using Managing.Application.Abstractions;
using Managing.Core;
-//using Microsoft.Extensions.Caching.Memory;
using System.Runtime.Caching;
namespace Managing.Infrastructure.Storage
@@ -8,11 +7,11 @@ namespace Managing.Infrastructure.Storage
public class TaskCache : ITaskCache
{
private MemoryCache _cache { get; } = MemoryCache.Default;
+
private CacheItemPolicy _defaultPolicy { get; } = new CacheItemPolicy();
public async Task AddOrGetExisting(string key, Func> valueFactory)
{
-
var asyncLazyValue = new AsyncLazy(valueFactory);
var existingValue = (AsyncLazy)_cache.AddOrGetExisting(key, asyncLazyValue, _defaultPolicy);
@@ -33,6 +32,7 @@ namespace Managing.Infrastructure.Storage
// Get the most recent value with a recursive call.
return await AddOrGetExisting(key, valueFactory);
}
+
return result;
}
catch (Exception)
@@ -64,13 +64,14 @@ namespace Managing.Infrastructure.Storage
public T Get(string key)
{
var existingValue = (AsyncLazy)_cache.Get(key);
- return existingValue.Value.Result;
+ if (existingValue != null) return existingValue.Value.Result;
+ return default(T);
}
public virtual List GetCache()
{
List list = new List();
-
+
foreach (var item in _cache)
{
list.Add((T)item.Value);
@@ -79,4 +80,4 @@ namespace Managing.Infrastructure.Storage
return list;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Infrastructure.Tests/SubgraphTests.cs b/src/Managing.Infrastructure.Tests/SubgraphTests.cs
index ff2040d..f2af853 100644
--- a/src/Managing.Infrastructure.Tests/SubgraphTests.cs
+++ b/src/Managing.Infrastructure.Tests/SubgraphTests.cs
@@ -1,7 +1,4 @@
-using Xunit;
-using static Managing.Common.Enums;
-
-namespace Managing.Infrastructure.Tests;
+namespace Managing.Infrastructure.Tests;
public class SubgraphTests
{
diff --git a/src/Managing.WebApp/.env b/src/Managing.WebApp/.env
index 0b33460..f8d9d0e 100644
--- a/src/Managing.WebApp/.env
+++ b/src/Managing.WebApp/.env
@@ -1,4 +1,4 @@
-VITE_API_URL_LOCAL=https://localhost:5001
+VITE_API_URL_LOCAL=http://localhost:5000
VITE_API_URL_SERVER=https://dev-managing-api.apps.managing.live
VITE_WORKER_URL_LOCAL=https://localhost:5002
VITE_WORKER_URL_SERVER=https://dev-managing-worker.apps.managing.live
diff --git a/src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx b/src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx
index b80ff7a..92eca05 100644
--- a/src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx
+++ b/src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx
@@ -8,21 +8,24 @@ import { UserClient } from '../../../generated/ManagingApi'
import type { ILoginFormInput } from '../../../global/type'
import useCookie from '../../../hooks/useCookie'
import { SecondaryNavbar } from '../NavBar/NavBar'
+import Toast from '../Toast/Toast'
const LogIn = () => {
const { apiUrl } = useApiUrlStore()
const { register, handleSubmit } = useForm()
const { disconnect } = useDisconnect()
const { address } = useAccount()
- const { isLoading, signMessageAsync } = useSignMessage({})
+ const { signMessageAsync } = useSignMessage({})
const { setCookie } = useCookie()
const onSubmit: SubmitHandler = async (form) => {
const message = 'wagmi'
const signature = await signMessageAsync({ message })
+ const t = new Toast('Creating token')
if (signature && address) {
const userClient = new UserClient({}, apiUrl)
+
await userClient
.user_CreateToken({
address: address.toString(),
@@ -34,11 +37,11 @@ const LogIn = () => {
setCookie('token', data, 1)
location.reload()
})
- .catch((err) => {
- // eslint-disable-next-line no-console
- console.error(err)
+ .catch((err: any) => {
+ t.update('error', 'Error :' + err.message)
})
- } else {
+ }else{
+ t.update('error', 'Error : No signature')
}
}
@@ -77,7 +80,6 @@ const LogIn = () => {
-
+