Filter everything with users (#16)
* Filter everything with users * Fix backtests and user management * Add cursor rules * Fix backtest and bots * Update configs names * Sign until unauth * Setup delegate * Setup delegate and sign * refact * Enhance Privy signature generation with improved cryptographic methods * Add Fastify backend * Add Fastify backend routes for privy * fix privy signing * fix privy client * Fix tests * add gmx core * fix merging sdk * Fix tests * add gmx core * add gmx core * add privy to boilerplate * clean * fix * add fastify * Remove Managing.Fastify submodule * Add Managing.Fastify as regular directory instead of submodule * Update .gitignore to exclude Managing.Fastify dist and node_modules directories * Add token approval functionality to Privy plugin - Introduced a new endpoint `/approve-token` for approving ERC20 tokens. - Added `approveToken` method to the Privy plugin for handling token approvals. - Updated `signPrivyMessage` to differentiate between message signing and token approval requests. - Enhanced the plugin with additional schemas for input validation. - Included new utility functions for token data retrieval and message construction. - Updated tests to verify the new functionality and ensure proper request decoration. * Add PrivyApproveTokenResponse model for token approval response - Created a new class `PrivyApproveTokenResponse` to encapsulate the response structure for token approval requests. - The class includes properties for `Success` status and a transaction `Hash`. * Refactor trading commands and enhance API routes - Updated `OpenPositionCommandHandler` to use asynchronous methods for opening trades and canceling orders. - Introduced new Fastify routes for opening positions and canceling orders with appropriate request validation. - Modified `EvmManager` to handle both Privy and non-Privy wallet operations, utilizing the Fastify API for Privy wallets. - Adjusted test configurations to reflect changes in account types and added helper methods for testing Web3 proxy services. * Enhance GMX trading functionality and update dependencies - Updated `dev:start` script in `package.json` to include the `-d` flag for Fastify. - Upgraded `fastify-cli` dependency to version 7.3.0. - Added `sourceMap` option to `tsconfig.json`. - Refactored GMX plugin to improve position opening logic, including enhanced error handling and validation. - Introduced a new method `getMarketInfoFromTicker` for better market data retrieval. - Updated account type in `PrivateKeys.cs` to use `Privy`. - Adjusted `EvmManager` to utilize the `direction` enum directly for trade direction handling. * Refactor GMX plugin for improved trading logic and market data retrieval - Enhanced the `openGmxPositionImpl` function to utilize the `TradeDirection` enum for trade direction handling. - Introduced `getTokenDataFromTicker` and `getMarketByIndexToken` functions for better market and token data retrieval. - Updated collateral calculation and logging for clarity. - Adjusted `EvmManager` to ensure proper handling of price values in trade requests. * Refactor GMX plugin and enhance testing for position opening - Updated `test:single` script in `package.json` to include TypeScript compilation before running tests. - Removed `this` context from `getClientForAddress` function and replaced logging with `console.error`. - Improved collateral calculation in `openGmxPositionImpl` for better precision. - Adjusted type casting for `direction` in the API route to utilize `TradeDirection` enum. - Added a new test for opening a long position in GMX, ensuring functionality and correctness. * Update sdk * Update * update fastify * Refactor start script in package.json to simplify command execution - Removed the build step from the start script, allowing for a more direct launch of the Fastify server. * Update package.json for Web3Proxy - Changed the name from "Web3Proxy" to "web3-proxy". - Updated version from "0.0.0" to "1.0.0". - Modified the description to "The official Managing Web3 Proxy". * Update Dockerfile for Web3Proxy - Upgraded Node.js base image from 18-alpine to 22.14.0-alpine. - Added NODE_ENV environment variable set to production. * Refactor Dockerfile and package.json for Web3Proxy - Removed the build step from the Dockerfile to streamline the image creation process. - Updated the start script in package.json to include the build step, ensuring the application is built before starting the server. * Add fastify-tsconfig as a development dependency in Dockerfile-web3proxy * Remove fastify-tsconfig extension from tsconfig.json for Web3Proxy * Add PrivyInitAddressResponse model for handling initialization responses - Introduced a new class `PrivyInitAddressResponse` to encapsulate the response structure for Privy initialization, including properties for success status, USDC hash, order vault hash, and error message. * Update * Update * Remove fastify-tsconfig installation from Dockerfile-web3proxy * Add build step to Dockerfile-web3proxy - Included `npm run build` in the Dockerfile to ensure the application is built during the image creation process. * Update * approvals * Open position from front embedded wallet * Open position from front embedded wallet * Open position from front embedded wallet * Fix call contracts * Fix limit price * Close position * Fix close position * Fix close position * add pinky * Refactor position handling logic * Update Dockerfile-pinky to copy package.json and source code from the correct directory * Implement password protection modal and enhance UI with new styles; remove unused audio elements and update package dependencies. * add cancel orders * Update callContract function to explicitly cast account address as Address type * Update callContract function to cast transaction parameters as any type for compatibility * Cast transaction parameters as any type in approveTokenImpl for compatibility * Cast wallet address and transaction parameters as Address type in approveTokenImpl for type safety * Add .env configuration file for production setup including database and server settings * Refactor home route to update welcome message and remove unused SDK configuration code * add referral code * fix referral * Add sltp * Fix typo * Fix typo * setup sltp on backtend * get orders * get positions with slp * fixes * fixes close position * fixes * Remove MongoDB project references from Dockerfiles for managing and worker APIs * Comment out BotManagerWorker service registration and remove MongoDB project reference from Dockerfile * fixes
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Managing.Common;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Workflows;
|
||||
@@ -14,6 +15,7 @@ public interface IBotService
|
||||
List<ITradingBot> GetActiveBots();
|
||||
IEnumerable<BotBackup> GetSavedBots();
|
||||
void StartBotFromBackup(BotBackup backupBot);
|
||||
BotBackup GetBotBackup(string name);
|
||||
|
||||
ITradingBot CreateScalpingBot(string accountName, MoneyManagement moneyManagement, string name, Enums.Ticker ticker,
|
||||
string scenario, Enums.Timeframe interval, bool isForWatchingOnly);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Users;
|
||||
|
||||
namespace Managing.Application.Abstractions
|
||||
{
|
||||
public interface IMoneyManagementService
|
||||
{
|
||||
Task<MoneyManagement> CreateOrUpdateMoneyManagement(MoneyManagement request);
|
||||
Task<MoneyManagement> GetMoneyMangement(string name);
|
||||
IEnumerable<MoneyManagement> GetMoneyMangements();
|
||||
bool DeleteMoneyManagement(string name);
|
||||
bool DeleteMoneyManagements();
|
||||
Task<MoneyManagement> CreateOrUpdateMoneyManagement(User user, MoneyManagement request);
|
||||
Task<MoneyManagement> GetMoneyMangement(User user, string name);
|
||||
IEnumerable<MoneyManagement> GetMoneyMangements(User user);
|
||||
bool DeleteMoneyManagement(User user, string name);
|
||||
bool DeleteMoneyManagements(User user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Abstractions
|
||||
@@ -30,5 +31,31 @@ namespace Managing.Application.Abstractions
|
||||
|
||||
bool UpdateStrategy(StrategyType strategyType, string name, int? period, int? fastPeriods, int? slowPeriods,
|
||||
int? signalPeriods, double? multiplier, int? stochPeriods, int? smoothPeriods, int? cyclePeriods);
|
||||
|
||||
IEnumerable<Scenario> GetScenariosByUser(User user);
|
||||
Scenario CreateScenarioForUser(User user, string name, List<string> strategies, int? loopbackPeriod = 1);
|
||||
IEnumerable<Strategy> GetStrategiesByUser(User user);
|
||||
bool DeleteStrategyByUser(User user, string name);
|
||||
bool DeleteScenarioByUser(User user, string name);
|
||||
Scenario GetScenarioByUser(User user, string name);
|
||||
|
||||
Strategy CreateStrategyForUser(User user,
|
||||
StrategyType type,
|
||||
string name,
|
||||
int? period = null,
|
||||
int? fastPeriods = null,
|
||||
int? slowPeriods = null,
|
||||
int? signalPeriods = null,
|
||||
double? multiplier = null,
|
||||
int? stochPeriods = null,
|
||||
int? smoothPeriods = null,
|
||||
int? cyclePeriods = null);
|
||||
|
||||
bool DeleteStrategiesByUser(User user);
|
||||
bool DeleteScenariosByUser(User user);
|
||||
bool UpdateScenarioByUser(User user, string name, List<string> strategies, int? loopbackPeriod);
|
||||
|
||||
bool UpdateStrategyByUser(User user, StrategyType strategyType, string name, int? period, int? fastPeriods,
|
||||
int? slowPeriods, int? signalPeriods, double? multiplier, int? stochPeriods, int? smoothPeriods, int? cyclePeriods);
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,5 @@ public interface ISettingsService
|
||||
{
|
||||
bool SetupSettings();
|
||||
Task<bool> ResetSettings();
|
||||
Task<bool> CreateDefaultConfiguration(Domain.Users.User user);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Core.FixedSizedQueue;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
@@ -34,5 +35,6 @@ namespace Managing.Application.Abstractions
|
||||
void LoadStrategies(IEnumerable<IStrategy> strategies);
|
||||
void LoadScenario(string scenarioName);
|
||||
void UpdateStrategiesValues();
|
||||
Task LoadAccount();
|
||||
}
|
||||
}
|
||||
@@ -52,8 +52,10 @@ public class AccountService : IAccountService
|
||||
else if (request.Exchange == Enums.TradingExchanges.Evm
|
||||
&& request.Type == Enums.AccountType.Privy)
|
||||
{
|
||||
if (string.IsNullOrEmpty(request.Key) || string.IsNullOrEmpty(request.Secret))
|
||||
if (string.IsNullOrEmpty(request.Key))
|
||||
{
|
||||
// No key provided, create new privy embedded wallet.
|
||||
// TODO : Fix it to create privy wallet
|
||||
var privyClient = await _evmManager.CreatePrivyWallet();
|
||||
request.Key = privyClient.Address;
|
||||
request.Secret = privyClient.Id;
|
||||
|
||||
@@ -14,6 +14,7 @@ using Managing.Domain.Strategies.Base;
|
||||
using Managing.Domain.Workflows;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static Managing.Common.Enums;
|
||||
using Managing.Domain.Users;
|
||||
|
||||
namespace Managing.Application.Backtesting
|
||||
{
|
||||
@@ -42,21 +43,25 @@ namespace Managing.Application.Backtesting
|
||||
{
|
||||
var simplebot = _botFactory.CreateSimpleBot("scenario", workflow);
|
||||
Backtest result = null;
|
||||
if (save)
|
||||
if (save && result != null)
|
||||
{
|
||||
_backtestRepository.InsertBacktest(result);
|
||||
// Simple bot backtest not implemented yet, would need user
|
||||
// _backtestRepository.InsertBacktestForUser(null, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Backtest RunScalpingBotBacktest(Account account,
|
||||
public async Task<Backtest> RunScalpingBotBacktest(
|
||||
Account account,
|
||||
MoneyManagement moneyManagement,
|
||||
Ticker ticker,
|
||||
Scenario scenario,
|
||||
Timeframe timeframe,
|
||||
double days,
|
||||
decimal balance,
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
User user = null,
|
||||
bool isForWatchingOnly = false,
|
||||
bool save = false,
|
||||
List<Candle> initialCandles = null)
|
||||
@@ -64,21 +69,36 @@ namespace Managing.Application.Backtesting
|
||||
var scalpingBot = _botFactory.CreateBacktestScalpingBot(account.Name, moneyManagement, ticker, "scenario",
|
||||
timeframe, isForWatchingOnly);
|
||||
scalpingBot.LoadScenario(scenario.Name);
|
||||
var candles = initialCandles ?? GetCandles(account, ticker, timeframe, days);
|
||||
await scalpingBot.LoadAccount();
|
||||
var candles = initialCandles ?? GetCandles(account, ticker, timeframe, startDate, endDate);
|
||||
var result = GetBacktestingResult(ticker, scenario, timeframe, scalpingBot, candles, balance, account,
|
||||
moneyManagement);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
result.User = user;
|
||||
}
|
||||
|
||||
// Set start and end dates
|
||||
result.StartDate = startDate;
|
||||
result.EndDate = endDate;
|
||||
|
||||
if (save)
|
||||
{
|
||||
_backtestRepository.InsertBacktest(result);
|
||||
_backtestRepository.InsertBacktestForUser(user, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Candle> GetCandles(Account account, Ticker ticker, Timeframe timeframe, double days)
|
||||
private List<Candle> GetCandles(Account account, Ticker ticker, Timeframe timeframe,
|
||||
DateTime startDate, DateTime endDate)
|
||||
{
|
||||
var candles = _exchangeService.GetCandlesInflux(account.Exchange, ticker,
|
||||
DateTime.Now.AddDays(Convert.ToDouble(days)), timeframe).Result;
|
||||
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}");
|
||||
@@ -86,46 +106,85 @@ namespace Managing.Application.Backtesting
|
||||
return candles;
|
||||
}
|
||||
|
||||
public Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Ticker ticker,
|
||||
Scenario scenario, Timeframe timeframe,
|
||||
double days, decimal balance, bool isForWatchingOnly = false, bool save = false,
|
||||
public async Task<Backtest> RunFlippingBotBacktest(
|
||||
Account account,
|
||||
MoneyManagement moneyManagement,
|
||||
Ticker ticker,
|
||||
Scenario scenario,
|
||||
Timeframe timeframe,
|
||||
decimal balance,
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
User user = null,
|
||||
bool isForWatchingOnly = false,
|
||||
bool save = false,
|
||||
List<Candle> initialCandles = null)
|
||||
{
|
||||
var flippingBot = _botFactory.CreateBacktestFlippingBot(account.Name, moneyManagement, ticker, "scenario",
|
||||
timeframe, false);
|
||||
flippingBot.LoadScenario(scenario.Name);
|
||||
var candles = initialCandles ?? GetCandles(account, ticker, timeframe, days);
|
||||
await flippingBot.LoadAccount();
|
||||
|
||||
var candles = initialCandles ?? GetCandles(account, ticker, timeframe, startDate, endDate);
|
||||
var result = GetBacktestingResult(ticker, scenario, timeframe, flippingBot, candles, balance, account,
|
||||
moneyManagement);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
result.User = user;
|
||||
}
|
||||
|
||||
// Set start and end dates
|
||||
result.StartDate = startDate;
|
||||
result.EndDate = endDate;
|
||||
|
||||
if (save)
|
||||
{
|
||||
_backtestRepository.InsertBacktest(result);
|
||||
_backtestRepository.InsertBacktestForUser(user, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Backtest RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario,
|
||||
Timeframe timeframe, List<Candle> candles, decimal balance)
|
||||
public async Task<Backtest> RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement,
|
||||
Scenario scenario,
|
||||
Timeframe timeframe, List<Candle> candles, decimal balance, User user = null)
|
||||
{
|
||||
var ticker = MiscExtensions.ParseEnum<Ticker>(candles.FirstOrDefault().Ticker);
|
||||
var bot = _botFactory.CreateBacktestScalpingBot(account.Name, moneyManagement, ticker, "scenario",
|
||||
timeframe, false);
|
||||
bot.LoadScenario(scenario.Name);
|
||||
await bot.LoadAccount();
|
||||
|
||||
var result = GetBacktestingResult(ticker, scenario, timeframe, bot, candles, balance, account,
|
||||
moneyManagement);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
result.User = user;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario,
|
||||
Timeframe timeframe, List<Candle> candles, decimal balance)
|
||||
public async Task<Backtest> RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement,
|
||||
Scenario scenario,
|
||||
Timeframe timeframe, List<Candle> candles, decimal balance, User user = null)
|
||||
{
|
||||
var ticker = MiscExtensions.ParseEnum<Ticker>(candles.FirstOrDefault().Ticker);
|
||||
var bot = _botFactory.CreateBacktestFlippingBot(account.Name, moneyManagement, ticker, "scenario",
|
||||
timeframe, false);
|
||||
bot.LoadScenario(scenario.Name);
|
||||
await bot.LoadAccount();
|
||||
|
||||
var result = GetBacktestingResult(ticker, scenario, timeframe, bot, candles, balance, account,
|
||||
moneyManagement);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
result.User = user;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -177,10 +236,8 @@ namespace Managing.Application.Backtesting
|
||||
maxDrawdownRecoveryTime: stats.MaxDrawdownRecoveryTime
|
||||
);
|
||||
|
||||
// Then calculate the score
|
||||
var score = BacktestScorer.CalculateTotalScore(scoringParams);
|
||||
|
||||
|
||||
var result = new Backtest(ticker, scenario.Name, bot.Positions, bot.Signals.ToList(), timeframe, candles,
|
||||
bot.BotType, account.Name)
|
||||
{
|
||||
@@ -197,7 +254,6 @@ namespace Managing.Application.Backtesting
|
||||
Score = score
|
||||
};
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -252,16 +308,14 @@ namespace Managing.Application.Backtesting
|
||||
return strategiesValues;
|
||||
}
|
||||
|
||||
public IEnumerable<Backtest> GetBacktests()
|
||||
{
|
||||
return _backtestRepository.GetBacktests();
|
||||
}
|
||||
|
||||
public bool DeleteBacktest(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
_backtestRepository.DeleteBacktestById(id);
|
||||
// Since we no longer have a general DeleteBacktestById method in the repository,
|
||||
// this should be implemented using DeleteBacktestByIdForUser with null
|
||||
_backtestRepository.DeleteBacktestByIdForUser(null, id);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -275,8 +329,111 @@ namespace Managing.Application.Backtesting
|
||||
{
|
||||
try
|
||||
{
|
||||
_backtestRepository.DeleteAllBacktests();
|
||||
//_backtestRepository.DropCollection();
|
||||
// Since we no longer have a general DeleteAllBacktests method in the repository,
|
||||
// this should be implemented using DeleteAllBacktestsForUser with null
|
||||
_backtestRepository.DeleteAllBacktestsForUser(null);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Backtest>> GetBacktestsByUser(User user)
|
||||
{
|
||||
var backtests = _backtestRepository.GetBacktestsByUser(user).ToList();
|
||||
|
||||
// For each backtest, ensure candles are loaded
|
||||
foreach (var backtest in backtests)
|
||||
{
|
||||
// If the backtest has no candles or only a few sample candles, retrieve them
|
||||
if (backtest.Candles == null || backtest.Candles.Count == 0 || backtest.Candles.Count < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
var candles = await _exchangeService.GetCandlesInflux(
|
||||
user.Accounts.First().Exchange,
|
||||
backtest.Ticker,
|
||||
backtest.StartDate,
|
||||
backtest.Timeframe,
|
||||
backtest.EndDate);
|
||||
|
||||
if (candles != null && candles.Count > 0)
|
||||
{
|
||||
backtest.Candles = candles;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to retrieve candles for backtest {Id}", backtest.Id);
|
||||
// Continue with the next backtest if there's an error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return backtests;
|
||||
}
|
||||
|
||||
public Backtest GetBacktestByIdForUser(User user, string id)
|
||||
{
|
||||
// Get the backtest from the repository
|
||||
var backtest = _backtestRepository.GetBacktestByIdForUser(user, id);
|
||||
|
||||
if (backtest == null)
|
||||
return null;
|
||||
|
||||
// If the backtest has no candles or only a few sample candles, retrieve them
|
||||
if (backtest.Candles == null || backtest.Candles.Count == 0 || backtest.Candles.Count < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the account
|
||||
var account = new Account { Name = backtest.AccountName, Exchange = TradingExchanges.Binance };
|
||||
|
||||
// Use the stored start and end dates to retrieve candles
|
||||
var candles = _exchangeService.GetCandlesInflux(
|
||||
account.Exchange,
|
||||
backtest.Ticker,
|
||||
backtest.StartDate,
|
||||
backtest.Timeframe,
|
||||
backtest.EndDate).Result;
|
||||
|
||||
if (candles != null && candles.Count > 0)
|
||||
{
|
||||
backtest.Candles = candles;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to retrieve candles for backtest {Id}", id);
|
||||
// Return the backtest without candles if there's an error
|
||||
}
|
||||
}
|
||||
|
||||
return backtest;
|
||||
}
|
||||
|
||||
public bool DeleteBacktestByUser(User user, string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
_backtestRepository.DeleteBacktestByIdForUser(user, id);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DeleteBacktestsByUser(User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
_backtestRepository.DeleteAllBacktestsForUser(user);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -101,6 +101,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
public override async void Start()
|
||||
{
|
||||
base.Start();
|
||||
// Load account synchronously
|
||||
await LoadAccount();
|
||||
|
||||
if (!IsForBacktest)
|
||||
@@ -127,7 +128,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
Fee = TradingService.GetFee(Account, IsForBacktest);
|
||||
}
|
||||
|
||||
private async Task LoadAccount()
|
||||
public async Task LoadAccount()
|
||||
{
|
||||
var account = await AccountService.GetAccount(AccountName, false, true);
|
||||
if (account == null)
|
||||
@@ -187,7 +188,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
|
||||
if (!IsForWatchingOnly)
|
||||
await ManagePositions();
|
||||
|
||||
|
||||
if (!IsForBacktest)
|
||||
{
|
||||
SaveBackup();
|
||||
@@ -225,6 +226,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
if (!OptimizedCandles.Any(c => c.Date == candle.Date))
|
||||
{
|
||||
OptimizedCandles.Enqueue(candle);
|
||||
Candles.Add(candle);
|
||||
await UpdateSignals(OptimizedCandles);
|
||||
}
|
||||
}
|
||||
@@ -235,9 +237,9 @@ public class TradingBot : Bot, ITradingBot
|
||||
private async Task UpdateSignals(FixedSizeQueue<Candle> candles)
|
||||
{
|
||||
var signal = TradingBox.GetSignal(candles.ToHashSet(), Strategies, Signals, Scenario.LoopbackPeriod);
|
||||
|
||||
if (signal == null) return;
|
||||
|
||||
signal.User = Account.User;
|
||||
await AddSignal(signal);
|
||||
}
|
||||
|
||||
@@ -246,8 +248,8 @@ public class TradingBot : Bot, ITradingBot
|
||||
{
|
||||
Signals.Add(signal);
|
||||
|
||||
if (!IsForBacktest)
|
||||
TradingService.InsertSignal(signal);
|
||||
// if (!IsForBacktest)
|
||||
// TradingService.InsertSignal(signal);
|
||||
|
||||
if (IsForWatchingOnly || (ExecutionCount < 1 && !IsForBacktest))
|
||||
signal.Status = SignalStatus.Expired;
|
||||
@@ -274,6 +276,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
foreach (var candle in newCandle.Where(c => c.Date < DateTime.Now.ToUniversalTime()))
|
||||
{
|
||||
OptimizedCandles.Enqueue(candle);
|
||||
Candles.Add(candle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,7 +500,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
MoneyManagement,
|
||||
signal.Direction,
|
||||
Ticker,
|
||||
IsForBacktest ? PositionInitiator.PaperTrading : PositionInitiator.Bot ,
|
||||
IsForBacktest ? PositionInitiator.PaperTrading : PositionInitiator.Bot,
|
||||
signal.Date,
|
||||
IsForBacktest,
|
||||
lastPrice,
|
||||
@@ -643,12 +646,23 @@ public class TradingBot : Bot, ITradingBot
|
||||
{
|
||||
try
|
||||
{
|
||||
var closePendingOrderStatus = await ExchangeService.CancelOrder(Account, Ticker);
|
||||
Logger.LogInformation($"Closing all {Ticker} orders status : {closePendingOrderStatus}");
|
||||
var test = await ExchangeService.CancelOrder(Account, Ticker);
|
||||
|
||||
var openOrders = await ExchangeService.GetOpenOrders(Account, Ticker);
|
||||
if (openOrders.Any())
|
||||
{
|
||||
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($"No need to cancel orders for {Ticker}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Todo handle exception from evm
|
||||
Logger.LogError(ex, "Error during cancelOrders");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,11 +97,13 @@ namespace Managing.Application.ManageBot
|
||||
|
||||
public List<ITradingBot> GetActiveBots()
|
||||
{
|
||||
return _botTasks.Values
|
||||
var bots = _botTasks.Values
|
||||
.Where(wrapper => typeof(ITradingBot).IsAssignableFrom(wrapper.BotType))
|
||||
.Select(wrapper => wrapper.BotInstance as ITradingBot)
|
||||
.Where(bot => bot != null)
|
||||
.ToList();
|
||||
|
||||
return bots;
|
||||
}
|
||||
|
||||
public IEnumerable<BotBackup> GetSavedBots()
|
||||
@@ -158,7 +160,7 @@ namespace Managing.Application.ManageBot
|
||||
{
|
||||
bot.Start();
|
||||
bot.LoadBackup(backupBot);
|
||||
return () => { };
|
||||
return () => { };
|
||||
}
|
||||
|
||||
public IBot CreateSimpleBot(string botName, Workflow workflow)
|
||||
@@ -227,12 +229,10 @@ namespace Managing.Application.ManageBot
|
||||
|
||||
public void ToggleIsForWatchingOnly(string botName)
|
||||
{
|
||||
if (_botTasks.TryGetValue(botName, out var botWrapper))
|
||||
if (_botTasks.TryGetValue(botName, out var botTaskWrapper) &&
|
||||
botTaskWrapper.BotInstance is ITradingBot tradingBot)
|
||||
{
|
||||
if (botWrapper.BotInstance is ITradingBot bot)
|
||||
{
|
||||
bot.IsForWatchingOnly = !bot.IsForWatchingOnly;
|
||||
}
|
||||
tradingBot.ToggleIsForWatchOnly().Wait();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using MediatR;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.ManageBot.Commands
|
||||
@@ -13,6 +14,7 @@ namespace Managing.Application.ManageBot.Commands
|
||||
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 StartBotCommand(BotType botType,
|
||||
string name,
|
||||
@@ -21,6 +23,7 @@ namespace Managing.Application.ManageBot.Commands
|
||||
Timeframe timeframe,
|
||||
string accountName,
|
||||
string moneyManagementName,
|
||||
User user,
|
||||
bool isForWatchingOnly = false)
|
||||
{
|
||||
BotType = botType;
|
||||
@@ -31,6 +34,7 @@ namespace Managing.Application.ManageBot.Commands
|
||||
IsForWatchingOnly = isForWatchingOnly;
|
||||
AccountName = accountName;
|
||||
MoneyManagementName = moneyManagementName;
|
||||
User = user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Managing.Application.ManageBot
|
||||
public Task<string> Handle(StartBotCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
BotStatus botStatus = BotStatus.Down;
|
||||
var moneyManagement = _moneyManagementService.GetMoneyMangement(request.MoneyManagementName).Result;
|
||||
var moneyManagement = _moneyManagementService.GetMoneyMangement(request.User, request.MoneyManagementName).Result;
|
||||
switch (request.BotType)
|
||||
{
|
||||
case BotType.SimpleBot:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Managing.Application.Abstractions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.Users;
|
||||
|
||||
namespace Managing.Application.MoneyManagements;
|
||||
|
||||
@@ -18,42 +19,110 @@ public class MoneyManagementService : IMoneyManagementService
|
||||
_settingsRepository = settingsRepository;
|
||||
}
|
||||
|
||||
public async Task<MoneyManagement> CreateOrUpdateMoneyManagement(MoneyManagement request)
|
||||
public async Task<MoneyManagement> CreateOrUpdateMoneyManagement(User user, MoneyManagement request)
|
||||
{
|
||||
var moneyManagement = await _settingsRepository.GetMoneyManagement(request.Name);
|
||||
// Try to get user-specific strategy first
|
||||
var moneyManagement = await _settingsRepository.GetMoneyManagementByUser(user, request.Name);
|
||||
|
||||
// Fall back to regular lookup if user-specific endpoint is not implemented
|
||||
if (moneyManagement == null)
|
||||
{
|
||||
moneyManagement = await _settingsRepository.GetMoneyManagement(request.Name);
|
||||
|
||||
// If found by name but doesn't belong to this user, treat as new
|
||||
if (moneyManagement != null && moneyManagement.User?.Name != user.Name)
|
||||
{
|
||||
moneyManagement = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (moneyManagement == null)
|
||||
{
|
||||
request.User = user;
|
||||
await _settingsRepository.InsertMoneyManagement(request);
|
||||
return request;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Additional check to ensure user's ownership
|
||||
if (moneyManagement.User?.Name != user.Name)
|
||||
{
|
||||
throw new UnauthorizedAccessException("You do not have permission to update this money management strategy.");
|
||||
}
|
||||
|
||||
moneyManagement.StopLoss = request.StopLoss;
|
||||
moneyManagement.TakeProfit = request.TakeProfit;
|
||||
moneyManagement.BalanceAtRisk = request.BalanceAtRisk;
|
||||
moneyManagement.Leverage = request.Leverage;
|
||||
moneyManagement.Timeframe = request.Timeframe;
|
||||
moneyManagement.User = user;
|
||||
|
||||
_settingsRepository.UpdateMoneyManagement(moneyManagement);
|
||||
return moneyManagement;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<MoneyManagement> GetMoneyMangements(User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to use user-specific repository method first
|
||||
return _settingsRepository.GetMoneyManagementsByUser(user);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fall back to filtering if user-specific endpoint is not implemented
|
||||
return _settingsRepository.GetMoneyManagements()
|
||||
.Where(mm => mm.User?.Name == user.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MoneyManagement> GetMoneyMangement(User user, string name)
|
||||
{
|
||||
MoneyManagement moneyManagement;
|
||||
|
||||
try
|
||||
{
|
||||
// Try to use user-specific repository method first
|
||||
moneyManagement = await _settingsRepository.GetMoneyManagementByUser(user, name);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fall back to regular lookup if user-specific endpoint is not implemented
|
||||
moneyManagement = await _settingsRepository.GetMoneyManagement(name);
|
||||
|
||||
// Filter by user
|
||||
if (moneyManagement != null && moneyManagement.User?.Name != user.Name)
|
||||
{
|
||||
moneyManagement = null;
|
||||
}
|
||||
}
|
||||
|
||||
return moneyManagement;
|
||||
}
|
||||
|
||||
public IEnumerable<MoneyManagement> GetMoneyMangements()
|
||||
{
|
||||
return _settingsRepository.GetMoneyManagements();
|
||||
}
|
||||
|
||||
public async Task<MoneyManagement> GetMoneyMangement(string name)
|
||||
{
|
||||
return await _settingsRepository.GetMoneyManagement(name);
|
||||
}
|
||||
|
||||
public bool DeleteMoneyManagement(string name)
|
||||
public bool DeleteMoneyManagement(User user, string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
_settingsRepository.DeleteMoneyManagement(name);
|
||||
try
|
||||
{
|
||||
// Try to use user-specific repository method first
|
||||
_settingsRepository.DeleteMoneyManagementByUser(user, name);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fall back to verifying user ownership before deletion
|
||||
var moneyManagement = _settingsRepository.GetMoneyManagement(name).Result;
|
||||
|
||||
if (moneyManagement != null && moneyManagement.User?.Name != user.Name)
|
||||
{
|
||||
throw new UnauthorizedAccessException("You do not have permission to delete this money management strategy.");
|
||||
}
|
||||
|
||||
_settingsRepository.DeleteMoneyManagement(name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -63,11 +132,22 @@ public class MoneyManagementService : IMoneyManagementService
|
||||
}
|
||||
}
|
||||
|
||||
public bool DeleteMoneyManagements()
|
||||
public bool DeleteMoneyManagements(User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
_settingsRepository.DeleteMoneyManagements();
|
||||
try
|
||||
{
|
||||
// Try to use user-specific repository method first
|
||||
_settingsRepository.DeleteMoneyManagementsByUser(user);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// This fallback is not ideal as it would delete all money managements regardless of user
|
||||
// In a real implementation, we would need a filtered repository method
|
||||
_logger.LogWarning("DeleteMoneyManagementsByUser not implemented, cannot delete user-specific money managements");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -5,6 +5,8 @@ using Managing.Domain.Strategies;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Driver;
|
||||
using static Managing.Common.Enums;
|
||||
using System.Collections.Generic;
|
||||
using Managing.Domain.Users;
|
||||
|
||||
namespace Managing.Application.Scenarios
|
||||
{
|
||||
@@ -186,5 +188,166 @@ namespace Managing.Application.Scenarios
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// User-specific methods implementation
|
||||
|
||||
public IEnumerable<Scenario> GetScenariosByUser(User user)
|
||||
{
|
||||
var scenarios = _tradingService.GetScenarios();
|
||||
return scenarios.Where(s => s.User?.Name == user.Name);
|
||||
}
|
||||
|
||||
public Scenario CreateScenarioForUser(User user, string name, List<string> strategies, int? loopbackPeriod = 1)
|
||||
{
|
||||
var scenario = new Scenario(name, loopbackPeriod ?? 1)
|
||||
{
|
||||
User = user
|
||||
};
|
||||
|
||||
foreach (var strategyName in strategies)
|
||||
{
|
||||
var strategy = _tradingService.GetStrategyByName(strategyName);
|
||||
if (strategy != null && strategy.User?.Name == user.Name)
|
||||
{
|
||||
scenario.AddStrategy(strategy);
|
||||
}
|
||||
}
|
||||
|
||||
_tradingService.InsertScenario(scenario);
|
||||
return scenario;
|
||||
}
|
||||
|
||||
public IEnumerable<Strategy> GetStrategiesByUser(User user)
|
||||
{
|
||||
var strategies = _tradingService.GetStrategies();
|
||||
return strategies.Where(s => s.User?.Name == user.Name);
|
||||
}
|
||||
|
||||
public bool DeleteStrategyByUser(User user, string name)
|
||||
{
|
||||
var strategy = _tradingService.GetStrategyByName(name);
|
||||
if (strategy != null && strategy.User?.Name == user.Name)
|
||||
{
|
||||
_tradingService.DeleteStrategy(strategy.Name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DeleteScenarioByUser(User user, string name)
|
||||
{
|
||||
var scenario = _tradingService.GetScenarioByName(name);
|
||||
if (scenario != null && scenario.User?.Name == user.Name)
|
||||
{
|
||||
_tradingService.DeleteScenario(scenario.Name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Scenario GetScenarioByUser(User user, string name)
|
||||
{
|
||||
var scenario = _tradingService.GetScenarioByName(name);
|
||||
return scenario != null && scenario.User?.Name == user.Name ? scenario : null;
|
||||
}
|
||||
|
||||
public Strategy CreateStrategyForUser(User user, StrategyType type, string name, int? period = null,
|
||||
int? fastPeriods = null, int? slowPeriods = null, int? signalPeriods = null,
|
||||
double? multiplier = null, int? stochPeriods = null, int? smoothPeriods = null,
|
||||
int? cyclePeriods = null)
|
||||
{
|
||||
// Create a new strategy using the existing implementation
|
||||
var strategy = CreateStrategy(type, name, period, fastPeriods, slowPeriods, signalPeriods,
|
||||
multiplier, stochPeriods, smoothPeriods, cyclePeriods);
|
||||
|
||||
// Set the user
|
||||
strategy.User = user;
|
||||
|
||||
// Update the strategy to save the user property
|
||||
_tradingService.UpdateStrategy(strategy);
|
||||
|
||||
return strategy;
|
||||
}
|
||||
|
||||
public bool DeleteStrategiesByUser(User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var strategies = GetStrategiesByUser(user);
|
||||
|
||||
foreach (var strategy in strategies)
|
||||
{
|
||||
_tradingService.DeleteStrategy(strategy.Name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DeleteScenariosByUser(User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var scenarios = GetScenariosByUser(user);
|
||||
|
||||
foreach (var scenario in scenarios)
|
||||
{
|
||||
_tradingService.DeleteScenario(scenario.Name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UpdateScenarioByUser(User user, string name, List<string> strategies, int? loopbackPeriod)
|
||||
{
|
||||
var scenario = _tradingService.GetScenarioByName(name);
|
||||
if (scenario == null || scenario.User?.Name != user.Name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
scenario.Strategies.Clear();
|
||||
scenario.LoopbackPeriod = loopbackPeriod ?? 1;
|
||||
|
||||
foreach (var strategyName in strategies)
|
||||
{
|
||||
var strategy = _tradingService.GetStrategyByName(strategyName);
|
||||
if (strategy != null && strategy.User?.Name == user.Name)
|
||||
{
|
||||
scenario.AddStrategy(strategy);
|
||||
}
|
||||
}
|
||||
|
||||
_tradingService.UpdateScenario(scenario);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool UpdateStrategyByUser(User user, StrategyType strategyType, string name, int? period,
|
||||
int? fastPeriods, int? slowPeriods, int? signalPeriods, double? multiplier,
|
||||
int? stochPeriods, int? smoothPeriods, int? cyclePeriods)
|
||||
{
|
||||
var strategy = _tradingService.GetStrategyByName(name);
|
||||
if (strategy == null || strategy.User?.Name != user.Name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use the existing update strategy logic
|
||||
var result = UpdateStrategy(strategyType, name, period, fastPeriods, slowPeriods,
|
||||
signalPeriods, multiplier, stochPeriods, smoothPeriods, cyclePeriods);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Users;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
@@ -41,10 +42,6 @@ public class SettingsService : ISettingsService
|
||||
throw new Exception("Cannot delete all strategies");
|
||||
}
|
||||
|
||||
if (!_moneyManagementService.DeleteMoneyManagements())
|
||||
{
|
||||
throw new Exception("Cannot delete all money management settings");
|
||||
}
|
||||
|
||||
if (!SetupSettings())
|
||||
{
|
||||
@@ -58,10 +55,10 @@ public class SettingsService : ISettingsService
|
||||
{
|
||||
try
|
||||
{
|
||||
SetupMoneyManagementsSeed(Timeframe.FiveMinutes);
|
||||
SetupMoneyManagementsSeed(Timeframe.FifteenMinutes);
|
||||
SetupMoneyManagementsSeed(Timeframe.OneHour);
|
||||
SetupMoneyManagementsSeed(Timeframe.OneDay);
|
||||
// SetupMoneyManagementsSeed(Timeframe.FiveMinutes);
|
||||
// SetupMoneyManagementsSeed(Timeframe.FifteenMinutes);
|
||||
// SetupMoneyManagementsSeed(Timeframe.OneHour);
|
||||
// SetupMoneyManagementsSeed(Timeframe.OneDay);
|
||||
SetupScenariosSeed();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -73,20 +70,20 @@ public class SettingsService : ISettingsService
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void SetupMoneyManagementsSeed(Timeframe timeframe)
|
||||
{
|
||||
var moneyManagement = new MoneyManagement()
|
||||
{
|
||||
Timeframe = timeframe,
|
||||
BalanceAtRisk = 0.05m,
|
||||
Leverage = 1,
|
||||
StopLoss = 0.021m,
|
||||
TakeProfit = 0.042m,
|
||||
Name = $"{timeframe}-MediumRisk",
|
||||
};
|
||||
|
||||
await _moneyManagementService.CreateOrUpdateMoneyManagement(moneyManagement);
|
||||
}
|
||||
// private async void SetupMoneyManagementsSeed(Timeframe timeframe)
|
||||
// {
|
||||
// var moneyManagement = new MoneyManagement()
|
||||
// {
|
||||
// Timeframe = timeframe,
|
||||
// BalanceAtRisk = 0.05m,
|
||||
// Leverage = 1,
|
||||
// StopLoss = 0.021m,
|
||||
// TakeProfit = 0.042m,
|
||||
// Name = $"{timeframe}-MediumRisk",
|
||||
// };
|
||||
//
|
||||
// await _moneyManagementService.CreateOrUpdateMoneyManagement(moneyManagement);
|
||||
// }
|
||||
|
||||
private void SetupScenariosSeed()
|
||||
{
|
||||
@@ -190,4 +187,58 @@ public class SettingsService : ISettingsService
|
||||
period: 200);
|
||||
_scenarioService.CreateScenario(name, new List<string> { strategy.Name });
|
||||
}
|
||||
|
||||
public async Task<bool> CreateDefaultConfiguration(User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (user == null)
|
||||
throw new ArgumentNullException(nameof(user), "User cannot be null");
|
||||
|
||||
// Create default Money Management
|
||||
var defaultMoneyManagement = new MoneyManagement
|
||||
{
|
||||
Name = "Personal-Hourly",
|
||||
Timeframe = Timeframe.OneHour,
|
||||
BalanceAtRisk = 25, // 25%
|
||||
StopLoss = 2, // 2%
|
||||
TakeProfit = 4, // 4%
|
||||
Leverage = 1,
|
||||
User = user
|
||||
};
|
||||
|
||||
// Format the percentage values correctly
|
||||
defaultMoneyManagement.FormatPercentage();
|
||||
|
||||
await _moneyManagementService.CreateOrUpdateMoneyManagement(user, defaultMoneyManagement);
|
||||
|
||||
// Create default Strategy (StcTrend)
|
||||
var defaultStrategy = _scenarioService.CreateStrategyForUser(
|
||||
user,
|
||||
StrategyType.Stc,
|
||||
"Stc",
|
||||
period: null,
|
||||
fastPeriods: 23,
|
||||
slowPeriods: 50,
|
||||
null,
|
||||
null,
|
||||
cyclePeriods: 10
|
||||
);
|
||||
|
||||
// Create default Scenario containing the strategy
|
||||
var strategyNames = new List<string> { defaultStrategy.Name };
|
||||
var defaultScenario = _scenarioService.CreateScenarioForUser(
|
||||
user,
|
||||
"STC Scenario",
|
||||
strategyNames
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error creating default configuration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Trading.Commands;
|
||||
using Managing.Domain.Shared.Helpers;
|
||||
using Managing.Domain.Trades;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Trading;
|
||||
@@ -10,40 +13,51 @@ namespace Managing.Application.Trading;
|
||||
public class ClosePositionCommandHandler(
|
||||
IExchangeService exchangeService,
|
||||
IAccountService accountService,
|
||||
ITradingService tradingService)
|
||||
ITradingService tradingService,
|
||||
ILogger<ClosePositionCommandHandler> logger = null)
|
||||
: ICommandHandler<ClosePositionCommand, Position>
|
||||
{
|
||||
public async Task<Position> Handle(ClosePositionCommand request)
|
||||
{
|
||||
// Get Trade
|
||||
var account = await accountService.GetAccount(request.Position.AccountName, false, false);
|
||||
if (request.Position == null)
|
||||
try
|
||||
{
|
||||
_ = exchangeService.CancelOrder(account, request.Position.Ticker).Result;
|
||||
// Get Trade
|
||||
var account = await accountService.GetAccount(request.Position.AccountName, false, false);
|
||||
if (request.Position == null)
|
||||
{
|
||||
_ = exchangeService.CancelOrder(account, request.Position.Ticker).Result;
|
||||
return request.Position;
|
||||
}
|
||||
|
||||
var isForPaperTrading = request.Position.Initiator == PositionInitiator.PaperTrading;
|
||||
|
||||
var lastPrice = request.Position.Initiator == PositionInitiator.PaperTrading
|
||||
? request.ExecutionPrice.GetValueOrDefault()
|
||||
: exchangeService.GetPrice(account, request.Position.Ticker, DateTime.UtcNow);
|
||||
|
||||
// Close market
|
||||
var closedPosition =
|
||||
await exchangeService.ClosePosition(account, request.Position, lastPrice, isForPaperTrading);
|
||||
var closeRequestedOrders =
|
||||
isForPaperTrading || (await exchangeService.CancelOrder(account, request.Position.Ticker));
|
||||
|
||||
if (closeRequestedOrders || closedPosition.Status == (TradeStatus.PendingOpen | TradeStatus.Filled))
|
||||
{
|
||||
request.Position.Status = PositionStatus.Finished;
|
||||
request.Position.ProfitAndLoss =
|
||||
TradingBox.GetProfitAndLoss(request.Position, closedPosition.Quantity, lastPrice,
|
||||
request.Position.Open.Leverage);
|
||||
tradingService.UpdatePosition(request.Position);
|
||||
}
|
||||
|
||||
return request.Position;
|
||||
}
|
||||
|
||||
var isForPaperTrading = request.Position.Initiator == PositionInitiator.PaperTrading;
|
||||
|
||||
var lastPrice = request.Position.Initiator == PositionInitiator.PaperTrading
|
||||
? request.ExecutionPrice.GetValueOrDefault()
|
||||
: exchangeService.GetPrice(account, request.Position.Ticker, DateTime.UtcNow);
|
||||
|
||||
// Close market
|
||||
var closedPosition =
|
||||
await exchangeService.ClosePosition(account, request.Position, lastPrice, isForPaperTrading);
|
||||
var closeRequestedOrders =
|
||||
isForPaperTrading || (await exchangeService.CancelOrder(account, request.Position.Ticker));
|
||||
|
||||
if (closeRequestedOrders || closedPosition.Status == (TradeStatus.PendingOpen | TradeStatus.Filled))
|
||||
catch (Exception ex)
|
||||
{
|
||||
request.Position.Status = PositionStatus.Finished;
|
||||
request.Position.ProfitAndLoss =
|
||||
TradingBox.GetProfitAndLoss(request.Position, closedPosition.Quantity, lastPrice,
|
||||
request.Position.Open.Leverage);
|
||||
tradingService.UpdatePosition(request.Position);
|
||||
}
|
||||
// Log the error - regardless of the error type
|
||||
logger?.LogError(ex, "Error closing position: {Message}", ex.Message);
|
||||
|
||||
return request.Position;
|
||||
throw new Exception(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,11 @@ namespace Managing.Application.Trading
|
||||
var account = await accountService.GetAccount(request.AccountName, hideSecrets: false, getBalance: false);
|
||||
if (!request.IsForPaperTrading)
|
||||
{
|
||||
var cancelOrderResult = await exchangeService.CancelOrder(account, request.Ticker);
|
||||
if (!cancelOrderResult)
|
||||
{
|
||||
throw new Exception($"Not able to close all orders for {request.Ticker}");
|
||||
}
|
||||
// var cancelOrderResult = await exchangeService.CancelOrder(account, request.Ticker);
|
||||
// if (!cancelOrderResult)
|
||||
// {
|
||||
// throw new Exception($"Not able to close all orders for {request.Ticker}");
|
||||
// }
|
||||
}
|
||||
|
||||
var initiator = request.IsForPaperTrading ? PositionInitiator.PaperTrading : request.Initiator;
|
||||
@@ -47,63 +47,60 @@ namespace Managing.Application.Trading
|
||||
: tradingService.GetFee(account, request.IsForPaperTrading);
|
||||
|
||||
var expectedStatus = GetExpectedStatus(request);
|
||||
position.Open = TradingPolicies.OpenPosition(expectedStatus).Execute(
|
||||
() =>
|
||||
{
|
||||
var openPrice = request.IsForPaperTrading || request.Price.HasValue
|
||||
? request.Price.Value
|
||||
: exchangeService.GetBestPrice(account, request.Ticker, price, quantity, request.Direction);
|
||||
// position.Open = TradingPolicies.OpenPosition(expectedStatus).Execute(async () => { });
|
||||
|
||||
var trade = exchangeService.OpenTrade(
|
||||
account,
|
||||
request.Ticker,
|
||||
request.Direction,
|
||||
openPrice,
|
||||
quantity,
|
||||
request.MoneyManagement.Leverage,
|
||||
TradeType.Limit,
|
||||
isForPaperTrading: request.IsForPaperTrading,
|
||||
currentDate: request.Date).Result;
|
||||
var openPrice = request.IsForPaperTrading || request.Price.HasValue
|
||||
? request.Price.Value
|
||||
: exchangeService.GetBestPrice(account, request.Ticker, price, quantity, request.Direction);
|
||||
|
||||
trade.Fee = TradingHelpers.GetFeeAmount(fee, openPrice * quantity, account.Exchange);
|
||||
return trade;
|
||||
});
|
||||
// Determine SL/TP Prices
|
||||
var stopLossPrice = RiskHelpers.GetStopLossPrice(request.Direction, openPrice, request.MoneyManagement);
|
||||
var takeProfitPrice = RiskHelpers.GetTakeProfitPrice(request.Direction, openPrice, request.MoneyManagement);
|
||||
|
||||
var trade = await exchangeService.OpenTrade(
|
||||
account,
|
||||
request.Ticker,
|
||||
request.Direction,
|
||||
openPrice,
|
||||
quantity,
|
||||
request.MoneyManagement.Leverage,
|
||||
TradeType.Limit,
|
||||
isForPaperTrading: request.IsForPaperTrading,
|
||||
currentDate: request.Date,
|
||||
stopLossPrice: stopLossPrice, // Pass determined SL price
|
||||
takeProfitPrice: takeProfitPrice); // Pass determined TP price
|
||||
|
||||
if (IsOpenTradeHandled(position.Open.Status, account.Exchange) && !request.IgnoreSLTP.GetValueOrDefault())
|
||||
{
|
||||
var closeDirection = request.Direction == TradeDirection.Long
|
||||
? TradeDirection.Short
|
||||
: TradeDirection.Long;
|
||||
trade.Fee = TradingHelpers.GetFeeAmount(fee, openPrice * quantity, account.Exchange);
|
||||
position.Open = trade;
|
||||
|
||||
var closeDirection = request.Direction == TradeDirection.Long
|
||||
? TradeDirection.Short
|
||||
: TradeDirection.Long;
|
||||
|
||||
// Stop loss
|
||||
position.StopLoss = exchangeService.BuildEmptyTrade(
|
||||
request.Ticker,
|
||||
RiskHelpers.GetStopLossPrice(request.Direction, position.Open.Price, request.MoneyManagement),
|
||||
position.Open.Quantity,
|
||||
closeDirection,
|
||||
request.MoneyManagement.Leverage,
|
||||
TradeType.StopLoss,
|
||||
request.Date,
|
||||
TradeStatus.PendingOpen);
|
||||
// Stop loss - Use the determined price
|
||||
position.StopLoss = exchangeService.BuildEmptyTrade(
|
||||
request.Ticker,
|
||||
stopLossPrice, // Use determined SL price
|
||||
position.Open.Quantity,
|
||||
closeDirection,
|
||||
request.MoneyManagement.Leverage,
|
||||
TradeType.StopLoss,
|
||||
request.Date,
|
||||
TradeStatus.PendingOpen);
|
||||
|
||||
position.StopLoss.Fee = TradingHelpers.GetFeeAmount(fee,
|
||||
position.StopLoss.Price * position.StopLoss.Quantity, account.Exchange);
|
||||
position.StopLoss.Fee = TradingHelpers.GetFeeAmount(fee,
|
||||
position.StopLoss.Price * position.StopLoss.Quantity, account.Exchange);
|
||||
|
||||
// Take profit
|
||||
position.TakeProfit1 = exchangeService.BuildEmptyTrade(
|
||||
request.Ticker,
|
||||
RiskHelpers.GetTakeProfitPrice(request.Direction, position.Open.Price, request.MoneyManagement),
|
||||
quantity,
|
||||
closeDirection,
|
||||
request.MoneyManagement.Leverage,
|
||||
TradeType.TakeProfit,
|
||||
request.Date,
|
||||
TradeStatus.PendingOpen);
|
||||
|
||||
position.TakeProfit1.Fee = TradingHelpers.GetFeeAmount(fee,
|
||||
position.TakeProfit1.Price * position.TakeProfit1.Quantity, account.Exchange);
|
||||
}
|
||||
// Take profit - Use the determined price
|
||||
position.TakeProfit1 = exchangeService.BuildEmptyTrade(
|
||||
request.Ticker,
|
||||
takeProfitPrice, // Use determined TP price
|
||||
quantity,
|
||||
closeDirection,
|
||||
request.MoneyManagement.Leverage,
|
||||
TradeType.TakeProfit,
|
||||
request.Date,
|
||||
TradeStatus.PendingOpen);
|
||||
|
||||
position.Status = IsOpenTradeHandled(position.Open.Status, account.Exchange)
|
||||
? position.Status
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Common;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Domain.Users;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -42,12 +43,19 @@ public class UserService : IUserService
|
||||
{
|
||||
var recoveredAddress = _evmManager.VerifySignature(signature, message);
|
||||
|
||||
if (!authorizedAddresses.Contains(recoveredAddress))
|
||||
// Verify message
|
||||
if (!message.Equals("KaigenTeamXCowchain"))
|
||||
{
|
||||
_logger.LogWarning($"Address {recoveredAddress} not authorized");
|
||||
throw new Exception("Address not authorized");
|
||||
_logger.LogWarning($"Message {message} not starting with KaigenTeamXCowchain");
|
||||
throw new Exception("Message not good");
|
||||
}
|
||||
|
||||
// if (!authorizedAddresses.Contains(recoveredAddress))
|
||||
// {
|
||||
// _logger.LogWarning($"Address {recoveredAddress} not authorized");
|
||||
// throw new Exception("Address not authorized");
|
||||
// }
|
||||
|
||||
if (recoveredAddress == null || !recoveredAddress.Equals(address))
|
||||
{
|
||||
_logger.LogWarning($"Address {recoveredAddress} not corresponding");
|
||||
@@ -55,50 +63,49 @@ public class UserService : IUserService
|
||||
}
|
||||
|
||||
// Check if account exist
|
||||
var account = await _accountService.GetAccountByKey(recoveredAddress, true, false);
|
||||
var user = await _userRepository.GetUserByNameAsync(name);
|
||||
|
||||
if (account != null && user != null)
|
||||
if (user != null)
|
||||
{
|
||||
if (!user.Name.Equals(name))
|
||||
throw new Exception("Name not corresponding");
|
||||
|
||||
// User and account found
|
||||
user.Accounts = _accountService.GetAccountsByUser(user).ToList();
|
||||
|
||||
if (!user.Name.Equals(name))
|
||||
throw new Exception("Name not corresponding");
|
||||
// Check if recoverred address owns the account
|
||||
var account = user.Accounts.FirstOrDefault(a => a.Key.Equals(recoveredAddress));
|
||||
|
||||
if (account == null)
|
||||
{
|
||||
throw new Exception("Account not found");
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No account and no
|
||||
account = new Account
|
||||
// First login
|
||||
user = new User
|
||||
{
|
||||
Name = $"Auth-{new Random().Next(1, 99)}",
|
||||
Key = recoveredAddress,
|
||||
Secret = "",
|
||||
Exchange = Common.Enums.TradingExchanges.Evm,
|
||||
Type = Common.Enums.AccountType.Auth,
|
||||
Name = name
|
||||
};
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
_ = await _accountService.CreateAccount(user, account);
|
||||
user.Accounts = _accountService.GetAccountsByUser(user).ToList();
|
||||
return user;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No user found, we create one
|
||||
// Create user if not existing
|
||||
user = new User()
|
||||
{
|
||||
Name = name,
|
||||
Accounts = new List<Account> { account },
|
||||
};
|
||||
await _userRepository.InsertUserAsync(user);
|
||||
|
||||
_ = await _accountService.CreateAccount(user, account);
|
||||
await _userRepository.InsertUserAsync(user);
|
||||
}
|
||||
// Create embedded account to authenticate user
|
||||
var account = await _accountService.CreateAccount(user, new Account
|
||||
{
|
||||
Name = $"{name}-embedded",
|
||||
Key = recoveredAddress,
|
||||
Exchange = Enums.TradingExchanges.Evm,
|
||||
Type = Enums.AccountType.Privy
|
||||
});
|
||||
|
||||
user.Accounts = new List<Account>()
|
||||
{
|
||||
account
|
||||
};
|
||||
}
|
||||
|
||||
return user;
|
||||
|
||||
Reference in New Issue
Block a user