Fixes for bots running (#22)
* Fixes for bots running * Up botmanager * Add cooldown * Refact can open position * Add cooldown Period and MaxLossStreak * Add agentName * Add env variable for botManager * Always enable Botmanager * Fix bot handle * Fix get positions * Add Ticker url * Dont start stopped bot * fix
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ITradingBot> GetActiveBots();
|
||||
IEnumerable<BotBackup> 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<string> StopBot(string requestName);
|
||||
Task<bool> DeleteBot(string requestName);
|
||||
Task<string> RestartBot(string requestName);
|
||||
Task<string> StopBot(string botName);
|
||||
Task<bool> DeleteBot(string botName);
|
||||
Task<string> RestartBot(string botName);
|
||||
void DeleteBotBackup(string backupBotName);
|
||||
void ToggleIsForWatchingOnly(string botName);
|
||||
}
|
||||
@@ -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<Signal> Signals { get; set; }
|
||||
List<Position> Positions { get; set; }
|
||||
TradingBotConfig Config { get; set; }
|
||||
Account Account { get; set; }
|
||||
HashSet<IStrategy> Strategies { get; set; }
|
||||
FixedSizeQueue<Candle> OptimizedCandles { get; set; }
|
||||
HashSet<Candle> Candles { get; set; }
|
||||
Timeframe Timeframe { get; set; }
|
||||
HashSet<IStrategy> 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<Signal> Signals { get; set; }
|
||||
List<Position> Positions { get; set; }
|
||||
Dictionary<DateTime, decimal> WalletBalances { get; set; }
|
||||
Dictionary<StrategyType, StrategiesResultBase> 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<Position> OpenPositionManually(TradeDirection direction);
|
||||
}
|
||||
}
|
||||
@@ -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<Candle> initialCandles = null)
|
||||
List<Candle>? 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<Candle> GetCandles(Account account, Ticker ticker, Timeframe timeframe,
|
||||
DateTime startDate, DateTime endDate)
|
||||
{
|
||||
List<Candle> 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<Backtest> RunFlippingBotBacktest(
|
||||
Account account,
|
||||
MoneyManagement moneyManagement,
|
||||
@@ -118,10 +120,26 @@ namespace Managing.Application.Backtesting
|
||||
User user = null,
|
||||
bool isForWatchingOnly = false,
|
||||
bool save = false,
|
||||
List<Candle> initialCandles = null)
|
||||
List<Candle>? 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<Backtest> RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement,
|
||||
public async Task<Backtest> RunScalpingBotBacktest(
|
||||
Account account,
|
||||
MoneyManagement moneyManagement,
|
||||
Scenario scenario,
|
||||
Timeframe timeframe, List<Candle> candles, decimal balance, User user = null)
|
||||
Timeframe timeframe,
|
||||
List<Candle> candles,
|
||||
decimal balance,
|
||||
User user = null,
|
||||
decimal cooldownPeriod = 1,
|
||||
int maxLossStreak = 0)
|
||||
{
|
||||
var ticker = MiscExtensions.ParseEnum<Ticker>(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<Backtest> RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement,
|
||||
public async Task<Backtest> RunFlippingBotBacktest(
|
||||
Account account,
|
||||
MoneyManagement moneyManagement,
|
||||
Scenario scenario,
|
||||
Timeframe timeframe, List<Candle> candles, decimal balance, User user = null)
|
||||
Timeframe timeframe,
|
||||
List<Candle> candles,
|
||||
decimal balance,
|
||||
User user = null,
|
||||
decimal cooldownPeriod = 1,
|
||||
int maxLossStreak = 0)
|
||||
{
|
||||
var ticker = MiscExtensions.ParseEnum<Ticker>(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<Candle> GetCandles(Account account, Ticker ticker, Timeframe timeframe,
|
||||
DateTime startDate, DateTime endDate)
|
||||
{
|
||||
List<Candle> 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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<TradingBot> 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()
|
||||
|
||||
@@ -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<TradingBot> 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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<IStrategy> Strategies { get; set; }
|
||||
public FixedSizeQueue<Candle> OptimizedCandles { get; set; }
|
||||
public HashSet<Candle> Candles { get; set; }
|
||||
public HashSet<Signal> Signals { get; set; }
|
||||
public List<Position> 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<DateTime, decimal> WalletBalances { get; set; }
|
||||
public Dictionary<StrategyType, StrategiesResultBase> StrategiesValues { get; set; }
|
||||
public DateTime StartupTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The dedicated trading balance for this bot in USD
|
||||
/// </summary>
|
||||
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<TradingBot> 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<IStrategy>();
|
||||
Signals = new HashSet<Signal>();
|
||||
@@ -107,10 +78,10 @@ public class TradingBot : Bot, ITradingBot
|
||||
WalletBalances = new Dictionary<DateTime, decimal>();
|
||||
StrategiesValues = new Dictionary<StrategyType, StrategiesResultBase>();
|
||||
|
||||
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> { 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<bool> 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<bool> 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<bool> 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<bool> 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<TradingBotBackup>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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; }
|
||||
}
|
||||
20
src/Managing.Application/Bots/TradingBotConfig.cs
Normal file
20
src/Managing.Application/Bots/TradingBotConfig.cs
Normal file
@@ -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
|
||||
}
|
||||
@@ -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<TradingBot> _tradingBotLogger;
|
||||
private readonly ITradingService _tradingService;
|
||||
private readonly IMoneyManagementService _moneyManagementService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
private ConcurrentDictionary<string, BotTaskWrapper> _botTasks =
|
||||
new ConcurrentDictionary<string, BotTaskWrapper>();
|
||||
|
||||
public BotService(IBotRepository botRepository, IExchangeService exchangeService,
|
||||
IMessengerService messengerService, IAccountService accountService, ILogger<TradingBot> 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<TradingBotBackup>(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<TradingBotBackup>(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<string> StopBot(string botName)
|
||||
public async Task<string> 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<bool> DeleteBot(string botName)
|
||||
public async Task<bool> 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<string> RestartBot(string botName)
|
||||
public Task<string> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<string>
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -19,15 +19,14 @@ namespace Managing.Application.ManageBot
|
||||
public Task<ITradingBot> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,13 @@ public class LoadBackupBotCommandHandler : IRequestHandler<LoadBackupBotCommand,
|
||||
{
|
||||
try
|
||||
{
|
||||
if (backupBot.LastStatus == BotStatus.Down)
|
||||
{
|
||||
_logger.LogInformation("Skipping backup bot {Identifier} as it is marked as Down.",
|
||||
backupBot.Identifier);
|
||||
continue;
|
||||
}
|
||||
|
||||
var activeBot = _botService.GetActiveBots().FirstOrDefault(b => b.Identifier == backupBot.Identifier);
|
||||
|
||||
if (activeBot == null)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<User> 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<User> 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user