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:
BIN
src/.DS_Store
vendored
BIN
src/.DS_Store
vendored
Binary file not shown.
@@ -11,7 +11,6 @@ COPY ["/src/Managing.Api/Managing.Api.csproj", "Managing.Api/"]
|
||||
COPY ["/src/Managing.Bootstrap/Managing.Bootstrap.csproj", "Managing.Bootstrap/"]
|
||||
COPY ["/src/Managing.Infrastructure.Storage/Managing.Infrastructure.Storage.csproj", "Managing.Infrastructure.Storage/"]
|
||||
COPY ["/src/Managing.Application/Managing.Application.csproj", "Managing.Application/"]
|
||||
COPY ["/src/Managing.Infrastructure.MongoDb/Managing.Infrastructure.MongoDb.csproj", "Managing.Infrastructure.MongoDb/"]
|
||||
COPY ["/src/Managing.Common/Managing.Common.csproj", "Managing.Common/"]
|
||||
COPY ["/src/Managing.Core/Managing.Core.csproj", "Managing.Core/"]
|
||||
COPY ["/src/Managing.Application.Abstractions/Managing.Application.Abstractions.csproj", "Managing.Application.Abstractions/"]
|
||||
|
||||
@@ -11,7 +11,6 @@ COPY ["/src/Managing.Api.Workers/Managing.Api.Workers.csproj", "Managing.Api.Wor
|
||||
COPY ["/src/Managing.Bootstrap/Managing.Bootstrap.csproj", "Managing.Bootstrap/"]
|
||||
COPY ["/src/Managing.Infrastructure.Storage/Managing.Infrastructure.Storage.csproj", "Managing.Infrastructure.Storage/"]
|
||||
COPY ["/src/Managing.Application/Managing.Application.csproj", "Managing.Application/"]
|
||||
COPY ["/src/Managing.Infrastructure.MongoDb/Managing.Infrastructure.MongoDb.csproj", "Managing.Infrastructure.MongoDb/"]
|
||||
COPY ["/src/Managing.Common/Managing.Common.csproj", "Managing.Common/"]
|
||||
COPY ["/src/Managing.Core/Managing.Core.csproj", "Managing.Core/"]
|
||||
COPY ["/src/Managing.Application.Abstractions/Managing.Application.Abstractions.csproj", "Managing.Application.Abstractions/"]
|
||||
|
||||
@@ -11,7 +11,6 @@ COPY ["Managing.Api.Workers/Managing.Api.Workers.csproj", "Managing.Api.Workers/
|
||||
COPY ["Managing.Bootstrap/Managing.Bootstrap.csproj", "Managing.Bootstrap/"]
|
||||
COPY ["Managing.Infrastructure.Storage/Managing.Infrastructure.Storage.csproj", "Managing.Infrastructure.Storage/"]
|
||||
COPY ["Managing.Application/Managing.Application.csproj", "Managing.Application/"]
|
||||
COPY ["Managing.Infrastructure.MongoDb/Managing.Infrastructure.MongoDb.csproj", "Managing.Infrastructure.MongoDb/"]
|
||||
COPY ["Managing.Common/Managing.Common.csproj", "Managing.Common/"]
|
||||
COPY ["Managing.Core/Managing.Core.csproj", "Managing.Core/"]
|
||||
COPY ["Managing.Application.Abstractions/Managing.Application.Abstractions.csproj", "Managing.Application.Abstractions/"]
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Managing.Api.Controllers;
|
||||
[Authorize]
|
||||
[Route("[controller]")]
|
||||
[Produces("application/json")]
|
||||
public class BacktestController : ControllerBase
|
||||
public class BacktestController : BaseController
|
||||
{
|
||||
private readonly IHubContext<BotHub> _hubContext;
|
||||
private readonly IBacktester _backtester;
|
||||
@@ -40,7 +40,8 @@ public class BacktestController : ControllerBase
|
||||
IBacktester backtester,
|
||||
IScenarioService scenarioService,
|
||||
IAccountService accountService,
|
||||
IMoneyManagementService moneyManagementService)
|
||||
IMoneyManagementService moneyManagementService,
|
||||
IUserService userService) : base(userService)
|
||||
{
|
||||
_hubContext = hubContext;
|
||||
_backtester = backtester;
|
||||
@@ -50,24 +51,46 @@ public class BacktestController : ControllerBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all backtests.
|
||||
/// Retrieves all backtests for the authenticated user.
|
||||
/// </summary>
|
||||
/// <returns>A list of backtests.</returns>
|
||||
[HttpGet]
|
||||
public ActionResult<IEnumerable<Backtest>> Backtests()
|
||||
public async Task<ActionResult<IEnumerable<Backtest>>> Backtests()
|
||||
{
|
||||
return Ok(_backtester.GetBacktests());
|
||||
var user = await GetUser();
|
||||
return Ok(await _backtester.GetBacktestsByUser(user));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a specific backtest by ID.
|
||||
/// Retrieves a specific backtest by ID for the authenticated user.
|
||||
/// This endpoint will also populate the candles for visualization.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the backtest to retrieve.</param>
|
||||
/// <returns>The requested backtest with populated candle data.</returns>
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<Backtest>> Backtest(string id)
|
||||
{
|
||||
var user = await GetUser();
|
||||
var backtest = _backtester.GetBacktestByIdForUser(user, id);
|
||||
|
||||
if (backtest == null)
|
||||
{
|
||||
return NotFound($"Backtest with ID {id} not found or doesn't belong to the current user.");
|
||||
}
|
||||
|
||||
return Ok(backtest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a specific backtest by ID for the authenticated user.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the backtest to delete.</param>
|
||||
/// <returns>An ActionResult indicating the outcome of the operation.</returns>
|
||||
[HttpDelete]
|
||||
public ActionResult DeleteBacktest(string id)
|
||||
public async Task<ActionResult> DeleteBacktest(string id)
|
||||
{
|
||||
return Ok(_backtester.DeleteBacktest(id));
|
||||
var user = await GetUser();
|
||||
return Ok(_backtester.DeleteBacktestByUser(user, id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -90,7 +113,8 @@ public class BacktestController : ControllerBase
|
||||
/// <param name="scenarioName">The name of the scenario to use for the backtest.</param>
|
||||
/// <param name="timeframe">The timeframe for the backtest.</param>
|
||||
/// <param name="watchOnly">Whether to only watch the backtest without executing trades.</param>
|
||||
/// <param name="days">The number of days to backtest.</param>
|
||||
/// <param name="startDate">The start date for the backtest.</param>
|
||||
/// <param name="endDate">The end date for the backtest.</param>
|
||||
/// <param name="balance">The starting balance for the backtest.</param>
|
||||
/// <param name="moneyManagementName">The name of the money management strategy to use.</param>
|
||||
/// <param name="moneyManagement">The money management strategy details, if not using a named strategy.</param>
|
||||
@@ -104,9 +128,10 @@ public class BacktestController : ControllerBase
|
||||
string scenarioName,
|
||||
Timeframe timeframe,
|
||||
bool watchOnly,
|
||||
int days,
|
||||
decimal balance,
|
||||
string moneyManagementName,
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
MoneyManagement? moneyManagement = null,
|
||||
bool save = false)
|
||||
{
|
||||
@@ -127,18 +152,14 @@ public class BacktestController : ControllerBase
|
||||
nameof(moneyManagementName));
|
||||
}
|
||||
|
||||
if (days > 0)
|
||||
{
|
||||
days = days * -1;
|
||||
}
|
||||
|
||||
Backtest backtestResult = null;
|
||||
var scenario = _scenarioService.GetScenario(scenarioName);
|
||||
var account = await _accountService.GetAccount(accountName, true, false);
|
||||
var user = await GetUser();
|
||||
|
||||
if (!string.IsNullOrEmpty(moneyManagementName) && moneyManagement is null)
|
||||
{
|
||||
moneyManagement = await _moneyManagementService.GetMoneyMangement(moneyManagementName);
|
||||
moneyManagement = await _moneyManagementService.GetMoneyMangement(user, moneyManagementName);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -148,21 +169,37 @@ public class BacktestController : ControllerBase
|
||||
if (scenario == null)
|
||||
return BadRequest("No scenario found");
|
||||
|
||||
// var localCandles = FileHelpers
|
||||
// .ReadJson<List<Candle>>($"{ticker.ToString()}-{timeframe.ToString()}-candles.json")
|
||||
// .TakeLast(500).ToList();
|
||||
|
||||
switch (botType)
|
||||
{
|
||||
case BotType.SimpleBot:
|
||||
break;
|
||||
case BotType.ScalpingBot:
|
||||
backtestResult = _backtester.RunScalpingBotBacktest(account, moneyManagement, ticker, scenario,
|
||||
timeframe, Convert.ToDouble(days), balance, watchOnly, save);
|
||||
backtestResult = await _backtester.RunScalpingBotBacktest(
|
||||
account,
|
||||
moneyManagement,
|
||||
ticker,
|
||||
scenario,
|
||||
timeframe,
|
||||
balance,
|
||||
startDate,
|
||||
endDate,
|
||||
user,
|
||||
watchOnly,
|
||||
save);
|
||||
break;
|
||||
case BotType.FlippingBot:
|
||||
backtestResult = _backtester.RunFlippingBotBacktest(account, moneyManagement, ticker, scenario,
|
||||
timeframe, Convert.ToDouble(days), balance, watchOnly, save);
|
||||
backtestResult = await _backtester.RunFlippingBotBacktest(
|
||||
account,
|
||||
moneyManagement,
|
||||
ticker,
|
||||
scenario,
|
||||
timeframe,
|
||||
balance,
|
||||
startDate,
|
||||
endDate,
|
||||
user,
|
||||
watchOnly,
|
||||
save);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,22 @@ using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Hubs;
|
||||
using Managing.Application.ManageBot.Commands;
|
||||
using Managing.Common;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Users;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using static Managing.Common.Enums;
|
||||
using ApplicationTradingBot = Managing.Application.Bots.TradingBot;
|
||||
using ApiTradingBot = Managing.Api.Models.Responses.TradingBot;
|
||||
|
||||
namespace Managing.Api.Controllers;
|
||||
|
||||
@@ -20,13 +31,15 @@ namespace Managing.Api.Controllers;
|
||||
[Authorize]
|
||||
[Route("[controller]")]
|
||||
[Produces("application/json")]
|
||||
public class BotController : ControllerBase
|
||||
public class BotController : BaseController
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
private readonly ILogger<BotController> _logger;
|
||||
private readonly IHubContext<BotHub> _hubContext;
|
||||
private readonly IBacktester _backtester;
|
||||
private readonly IBotService _botService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IMoneyManagementService _moneyManagementService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BotController"/> class.
|
||||
@@ -36,13 +49,57 @@ public class BotController : ControllerBase
|
||||
/// <param name="hubContext">SignalR hub context for real-time communication.</param>
|
||||
/// <param name="backtester">Backtester for running backtests on bots.</param>
|
||||
public BotController(ILogger<BotController> logger, IMediator mediator, IHubContext<BotHub> hubContext,
|
||||
IBacktester backtester, IBotService botService)
|
||||
IBacktester backtester, IBotService botService, IUserService userService,
|
||||
IAccountService accountService, IMoneyManagementService moneyManagementService) : base(userService)
|
||||
{
|
||||
_logger = logger;
|
||||
_mediator = mediator;
|
||||
_hubContext = hubContext;
|
||||
_backtester = backtester;
|
||||
_botService = botService;
|
||||
_accountService = accountService;
|
||||
_moneyManagementService = moneyManagementService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the current authenticated user owns the account associated with the specified bot or account name
|
||||
/// </summary>
|
||||
/// <param name="botName">The name of the bot to check</param>
|
||||
/// <param name="accountName">Optional account name to check when creating a new bot</param>
|
||||
/// <returns>True if the user owns the account, False otherwise</returns>
|
||||
private async Task<bool> UserOwnsBotAccount(string botName, string accountName = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await GetUser();
|
||||
if (user == null)
|
||||
return false;
|
||||
|
||||
// For new bot creation, check if the user owns the account provided in the request
|
||||
if (!string.IsNullOrEmpty(accountName))
|
||||
{
|
||||
var accountService = HttpContext.RequestServices.GetRequiredService<IAccountService>();
|
||||
var account = await accountService.GetAccount(accountName, true, false);
|
||||
// Compare the user names
|
||||
return account != null && account.User != null && account.User.Name == user.Name;
|
||||
}
|
||||
|
||||
// For existing bots, check if the user owns the bot's account
|
||||
var activeBots = _botService.GetActiveBots();
|
||||
var bot = activeBots.FirstOrDefault(b => b.Name == botName);
|
||||
if (bot == null)
|
||||
return true; // Bot doesn't exist yet, so no ownership conflict
|
||||
|
||||
var botAccountService = HttpContext.RequestServices.GetRequiredService<IAccountService>();
|
||||
var botAccount = await botAccountService.GetAccount(bot.AccountName, true, false);
|
||||
// Compare the user names
|
||||
return botAccount != null && botAccount.User != null && botAccount.User.Name == user.Name;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error checking if user owns bot account");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,12 +111,39 @@ public class BotController : ControllerBase
|
||||
[Route("Start")]
|
||||
public async Task<ActionResult<string>> Start(StartBotRequest request)
|
||||
{
|
||||
var result = await _mediator.Send(new StartBotCommand(request.BotType, request.BotName, request.Ticker,
|
||||
request.Scenario, request.Timeframe, request.AccountName, request.MoneyManagementName,
|
||||
request.IsForWatchOnly));
|
||||
try
|
||||
{
|
||||
// Check if user owns the account
|
||||
if (!await UserOwnsBotAccount(request.BotName, request.AccountName))
|
||||
{
|
||||
return Forbid("You don't have permission to start this bot");
|
||||
}
|
||||
|
||||
await NotifyBotSubscriberAsync();
|
||||
return Ok(result);
|
||||
// Trigger error if money management is not provided
|
||||
if (string.IsNullOrEmpty(request.MoneyManagementName))
|
||||
{
|
||||
return BadRequest("Money management name is required");
|
||||
}
|
||||
|
||||
var user = await GetUser();
|
||||
var moneyManagement = await _moneyManagementService.GetMoneyMangement(user, request.MoneyManagementName);
|
||||
if (moneyManagement == null)
|
||||
{
|
||||
return BadRequest("Money management not found");
|
||||
}
|
||||
|
||||
var result = await _mediator.Send(new StartBotCommand(request.BotType, request.BotName, request.Ticker,
|
||||
request.Scenario, request.Timeframe, request.AccountName, request.MoneyManagementName, user,
|
||||
request.IsForWatchOnly));
|
||||
|
||||
await NotifyBotSubscriberAsync();
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error starting bot");
|
||||
return StatusCode(500, $"Error starting bot: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -72,12 +156,26 @@ public class BotController : ControllerBase
|
||||
[Route("Stop")]
|
||||
public async Task<ActionResult<string>> Stop(BotType botType, string botName)
|
||||
{
|
||||
var result = await _mediator.Send(new StopBotCommand(botType, botName));
|
||||
_logger.LogInformation($"{botType} type called {botName} is now {result}");
|
||||
try
|
||||
{
|
||||
// Check if user owns the account
|
||||
if (!await UserOwnsBotAccount(botName))
|
||||
{
|
||||
return Forbid("You don't have permission to stop this bot");
|
||||
}
|
||||
|
||||
await NotifyBotSubscriberAsync();
|
||||
var result = await _mediator.Send(new StopBotCommand(botType, botName));
|
||||
_logger.LogInformation($"{botType} type called {botName} is now {result}");
|
||||
|
||||
return Ok(result);
|
||||
await NotifyBotSubscriberAsync();
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error stopping bot");
|
||||
return StatusCode(500, $"Error stopping bot: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -89,31 +187,68 @@ public class BotController : ControllerBase
|
||||
[Route("Delete")]
|
||||
public async Task<ActionResult<bool>> Delete(string botName)
|
||||
{
|
||||
var result = await _botService.DeleteBot(botName);
|
||||
await NotifyBotSubscriberAsync();
|
||||
return Ok(result);
|
||||
try
|
||||
{
|
||||
// Check if user owns the account
|
||||
if (!await UserOwnsBotAccount(botName))
|
||||
{
|
||||
return Forbid("You don't have permission to delete this bot");
|
||||
}
|
||||
|
||||
var result = await _botService.DeleteBot(botName);
|
||||
await NotifyBotSubscriberAsync();
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error deleting bot");
|
||||
return StatusCode(500, $"Error deleting bot: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops all active bots.
|
||||
/// </summary>
|
||||
/// <returns>A string summarizing the results of the stop operations for all bots.</returns>
|
||||
[HttpGet]
|
||||
[Route("StopAll")]
|
||||
[HttpPost("stop-all")]
|
||||
public async Task<string> StopAll()
|
||||
{
|
||||
var bots = await GetBotList();
|
||||
var result = "";
|
||||
foreach (var bot in bots)
|
||||
// This method should be restricted to only stop bots owned by the current user
|
||||
var user = await GetUser();
|
||||
if (user == null)
|
||||
return "No authenticated user found";
|
||||
|
||||
try
|
||||
{
|
||||
result += $"{bot.Name} : ";
|
||||
result += await _mediator.Send(new StopBotCommand(bot.BotType, bot.Name));
|
||||
result += $" |";
|
||||
var bots = await GetBotList();
|
||||
// Filter to only include bots owned by the current user
|
||||
var userBots = new List<ApiTradingBot>();
|
||||
|
||||
foreach (var bot in bots)
|
||||
{
|
||||
var account = await _accountService.GetAccount(bot.AccountName, true, false);
|
||||
// Compare the user names
|
||||
if (account != null && account.User != null && account.User.Name == user.Name)
|
||||
{
|
||||
userBots.Add(bot);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var bot in userBots)
|
||||
{
|
||||
await _mediator.Send(new StopBotCommand(bot.BotType, bot.Name));
|
||||
await _hubContext.Clients.All.SendAsync("SendNotification",
|
||||
$"Bot {bot.Name} paused by {user.Name}.", "Info");
|
||||
}
|
||||
|
||||
await NotifyBotSubscriberAsync();
|
||||
return "All your bots have been stopped successfully!";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error stopping all bots");
|
||||
return $"Error stopping bots: {ex.Message}";
|
||||
}
|
||||
|
||||
await NotifyBotSubscriberAsync();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -126,34 +261,80 @@ public class BotController : ControllerBase
|
||||
[Route("Restart")]
|
||||
public async Task<ActionResult<string>> Restart(BotType botType, string botName)
|
||||
{
|
||||
var result = await _mediator.Send(new RestartBotCommand(botType, botName));
|
||||
_logger.LogInformation($"{botType} type called {botName} is now {result}");
|
||||
try
|
||||
{
|
||||
// Check if user owns the account
|
||||
if (!await UserOwnsBotAccount(botName))
|
||||
{
|
||||
return Forbid("You don't have permission to restart this bot");
|
||||
}
|
||||
|
||||
await NotifyBotSubscriberAsync();
|
||||
var result = await _mediator.Send(new RestartBotCommand(botType, botName));
|
||||
_logger.LogInformation($"{botType} type called {botName} is now {result}");
|
||||
|
||||
return Ok(result);
|
||||
await NotifyBotSubscriberAsync();
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error restarting bot");
|
||||
return StatusCode(500, $"Error restarting bot: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restarts all active bots.
|
||||
/// </summary>
|
||||
/// <returns>A string summarizing the results of the restart operations for all bots.</returns>
|
||||
[HttpGet]
|
||||
[Route("RestartAll")]
|
||||
[HttpPost("restart-all")]
|
||||
public async Task<string> RestartAll()
|
||||
{
|
||||
var bots = await GetBotList();
|
||||
var result = "";
|
||||
foreach (var bot in bots)
|
||||
var user = await GetUser();
|
||||
if (user == null)
|
||||
return "No authenticated user found";
|
||||
|
||||
try
|
||||
{
|
||||
result += $"{bot.Name} : ";
|
||||
result += await _mediator.Send(new RestartBotCommand(bot.BotType, bot.Name));
|
||||
result += $" |";
|
||||
var bots = await GetBotList();
|
||||
// Filter to only include bots owned by the current user
|
||||
var userBots = new List<ApiTradingBot>();
|
||||
var accountService = HttpContext.RequestServices.GetRequiredService<IAccountService>();
|
||||
|
||||
foreach (var bot in bots)
|
||||
{
|
||||
var account = await accountService.GetAccount(bot.AccountName, true, false);
|
||||
// Compare the user names
|
||||
if (account != null && account.User != null && account.User.Name == user.Name)
|
||||
{
|
||||
userBots.Add(bot);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var bot in userBots)
|
||||
{
|
||||
// We can't directly restart a bot with just BotType and Name
|
||||
// Instead, stop the bot and then retrieve the backup to start it again
|
||||
await _mediator.Send(new StopBotCommand(bot.BotType, bot.Name));
|
||||
|
||||
// Get the saved bot backup
|
||||
var backup = _botService.GetBotBackup(bot.Name);
|
||||
if (backup != null)
|
||||
{
|
||||
_botService.StartBotFromBackup(backup);
|
||||
await _hubContext.Clients.All.SendAsync("SendNotification",
|
||||
$"Bot {bot.Name} restarted by {user.Name}.", "Info");
|
||||
}
|
||||
}
|
||||
|
||||
await NotifyBotSubscriberAsync();
|
||||
return "All your bots have been restarted successfully!";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to restart all bots");
|
||||
return $"Error restarting bots: {e.Message}";
|
||||
}
|
||||
|
||||
await NotifyBotSubscriberAsync();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -165,12 +346,26 @@ public class BotController : ControllerBase
|
||||
[Route("ToggleIsForWatching")]
|
||||
public async Task<ActionResult<string>> ToggleIsForWatching(string botName)
|
||||
{
|
||||
var result = await _mediator.Send(new ToggleIsForWatchingCommand(botName));
|
||||
_logger.LogInformation($"{botName} bot is now {result}");
|
||||
try
|
||||
{
|
||||
// Check if user owns the account
|
||||
if (!await UserOwnsBotAccount(botName))
|
||||
{
|
||||
return Forbid("You don't have permission to modify this bot");
|
||||
}
|
||||
|
||||
await NotifyBotSubscriberAsync();
|
||||
var result = await _mediator.Send(new ToggleIsForWatchingCommand(botName));
|
||||
_logger.LogInformation($"{botName} bot is now {result}");
|
||||
|
||||
return Ok(result);
|
||||
await NotifyBotSubscriberAsync();
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error toggling bot watching status");
|
||||
return StatusCode(500, $"Error toggling bot watching status: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -178,7 +373,7 @@ public class BotController : ControllerBase
|
||||
/// </summary>
|
||||
/// <returns>A list of active trading bots.</returns>
|
||||
[HttpGet]
|
||||
public async Task<List<TradingBot>> GetActiveBots()
|
||||
public async Task<List<ApiTradingBot>> GetActiveBots()
|
||||
{
|
||||
return await GetBotList();
|
||||
}
|
||||
@@ -187,28 +382,28 @@ public class BotController : ControllerBase
|
||||
/// Retrieves a list of active bots by sending a command to the mediator.
|
||||
/// </summary>
|
||||
/// <returns>A list of trading bots.</returns>
|
||||
private async Task<List<TradingBot>> GetBotList()
|
||||
private async Task<List<ApiTradingBot>> GetBotList()
|
||||
{
|
||||
var result = await _mediator.Send(new GetActiveBotsCommand());
|
||||
var list = new List<TradingBot>();
|
||||
var list = new List<ApiTradingBot>();
|
||||
|
||||
foreach (var item in result)
|
||||
{
|
||||
list.Add(new TradingBot
|
||||
list.Add(new ApiTradingBot
|
||||
{
|
||||
Status = item.GetStatus(),
|
||||
Name = item.GetName(),
|
||||
Candles = item.OptimizedCandles.ToList(),
|
||||
Positions = item.Positions,
|
||||
Name = item.Name,
|
||||
Signals = item.Signals.ToList(),
|
||||
Positions = item.Positions,
|
||||
Candles = item.Candles.ToList(),
|
||||
WinRate = item.GetWinRate(),
|
||||
ProfitAndLoss = item.GetProfitAndLoss(),
|
||||
Timeframe = item.Timeframe,
|
||||
Ticker = item.Ticker,
|
||||
AccountName = item.AccountName,
|
||||
Scenario = item.ScenarioName,
|
||||
IsForWatchingOnly = item.IsForWatchingOnly,
|
||||
BotType = item.BotType,
|
||||
AccountName = item.AccountName,
|
||||
MoneyManagement = item.MoneyManagement
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class DataController : ControllerBase
|
||||
var cacheKey = string.Concat(timeframe.ToString());
|
||||
var tickers = _cacheService.GetValue<List<Ticker>>(cacheKey);
|
||||
|
||||
if (tickers == null)
|
||||
if (tickers == null || tickers.Count == 0)
|
||||
{
|
||||
tickers = await _exchangeService.GetTickers(timeframe);
|
||||
_cacheService.SaveValue(cacheKey, tickers, TimeSpan.FromHours(2));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -14,7 +15,7 @@ namespace Managing.Api.Controllers;
|
||||
[Authorize]
|
||||
[Route("[controller]")]
|
||||
[Produces("application/json")]
|
||||
public class MoneyManagementController : ControllerBase
|
||||
public class MoneyManagementController : BaseController
|
||||
{
|
||||
private readonly IMoneyManagementService _moneyManagementService;
|
||||
|
||||
@@ -22,52 +23,60 @@ public class MoneyManagementController : ControllerBase
|
||||
/// Initializes a new instance of the <see cref="MoneyManagementController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="moneyManagementService">The service for managing money management strategies.</param>
|
||||
public MoneyManagementController(IMoneyManagementService moneyManagementService)
|
||||
/// <param name="userService">The service for user-related operations.</param>
|
||||
public MoneyManagementController(
|
||||
IMoneyManagementService moneyManagementService,
|
||||
IUserService userService)
|
||||
: base(userService)
|
||||
{
|
||||
_moneyManagementService = moneyManagementService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new money management strategy or updates an existing one.
|
||||
/// Creates a new money management strategy or updates an existing one for the authenticated user.
|
||||
/// </summary>
|
||||
/// <param name="moneyManagement">The money management strategy to create or update.</param>
|
||||
/// <returns>The created or updated money management strategy.</returns>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<MoneyManagement>> PostMoneyManagement(MoneyManagement moneyManagement)
|
||||
{
|
||||
return Ok(await _moneyManagementService.CreateOrUpdateMoneyManagement(moneyManagement));
|
||||
var user = await GetUser();
|
||||
return Ok(await _moneyManagementService.CreateOrUpdateMoneyManagement(user, moneyManagement));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all money management strategies.
|
||||
/// Retrieves all money management strategies for the authenticated user.
|
||||
/// </summary>
|
||||
/// <returns>A list of money management strategies.</returns>
|
||||
[HttpGet]
|
||||
[Route("moneymanagements")]
|
||||
public ActionResult<IEnumerable<MoneyManagement>> GetMoneyManagements()
|
||||
public async Task<ActionResult<IEnumerable<MoneyManagement>>> GetMoneyManagements()
|
||||
{
|
||||
return Ok(_moneyManagementService.GetMoneyMangements());
|
||||
var user = await GetUser();
|
||||
return Ok(_moneyManagementService.GetMoneyMangements(user));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a specific money management strategy by name.
|
||||
/// Retrieves a specific money management strategy by name for the authenticated user.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the money management strategy to retrieve.</param>
|
||||
/// <returns>The requested money management strategy if found.</returns>
|
||||
[HttpGet]
|
||||
public ActionResult<MoneyManagement> GetMoneyManagement(string name)
|
||||
public async Task<ActionResult<MoneyManagement>> GetMoneyManagement(string name)
|
||||
{
|
||||
return Ok(_moneyManagementService.GetMoneyMangement(name));
|
||||
var user = await GetUser();
|
||||
return Ok(await _moneyManagementService.GetMoneyMangement(user, name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a specific money management strategy by name.
|
||||
/// Deletes a specific money management strategy by name for the authenticated user.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the money management strategy to delete.</param>
|
||||
/// <returns>An ActionResult indicating the outcome of the operation.</returns>
|
||||
[HttpDelete]
|
||||
public ActionResult DeleteMoneyManagement(string name)
|
||||
public async Task<ActionResult> DeleteMoneyManagement(string name)
|
||||
{
|
||||
return Ok(_moneyManagementService.DeleteMoneyManagement(name));
|
||||
var user = await GetUser();
|
||||
return Ok(_moneyManagementService.DeleteMoneyManagement(user, name));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Strategies;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@@ -16,7 +17,7 @@ namespace Managing.Api.Controllers;
|
||||
[Authorize]
|
||||
[Route("[controller]")]
|
||||
[Produces("application/json")]
|
||||
public class ScenarioController : ControllerBase
|
||||
public class ScenarioController : BaseController
|
||||
{
|
||||
private readonly IScenarioService _scenarioService;
|
||||
|
||||
@@ -24,67 +25,75 @@ public class ScenarioController : ControllerBase
|
||||
/// Initializes a new instance of the <see cref="ScenarioController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="scenarioService">The service for managing scenarios.</param>
|
||||
public ScenarioController(IScenarioService scenarioService)
|
||||
/// <param name="userService">The service for user-related operations.</param>
|
||||
public ScenarioController(
|
||||
IScenarioService scenarioService,
|
||||
IUserService userService)
|
||||
: base(userService)
|
||||
{
|
||||
_scenarioService = scenarioService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all scenarios.
|
||||
/// Retrieves all scenarios for the authenticated user.
|
||||
/// </summary>
|
||||
/// <returns>A list of scenarios.</returns>
|
||||
[HttpGet]
|
||||
public ActionResult<IEnumerable<Scenario>> GetScenarios()
|
||||
public async Task<ActionResult<IEnumerable<Scenario>>> GetScenarios()
|
||||
{
|
||||
return Ok(_scenarioService.GetScenarios());
|
||||
var user = await GetUser();
|
||||
return Ok(_scenarioService.GetScenariosByUser(user));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new scenario with the specified name and strategies.
|
||||
/// Creates a new scenario with the specified name and strategies for the authenticated user.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the scenario.</param>
|
||||
/// <param name="strategies">A list of strategy names to include in the scenario.</param>
|
||||
/// <returns>The created scenario.</returns>
|
||||
[HttpPost]
|
||||
public ActionResult<Scenario> CreateScenario(string name, List<string> strategies, int? loopbackPeriod = null)
|
||||
public async Task<ActionResult<Scenario>> CreateScenario(string name, List<string> strategies, int? loopbackPeriod = null)
|
||||
{
|
||||
return Ok(_scenarioService.CreateScenario(name, strategies, loopbackPeriod));
|
||||
var user = await GetUser();
|
||||
return Ok(_scenarioService.CreateScenarioForUser(user, name, strategies, loopbackPeriod));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a scenario by name.
|
||||
/// Deletes a scenario by name for the authenticated user.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the scenario to delete.</param>
|
||||
/// <returns>An ActionResult indicating the outcome of the operation.</returns>
|
||||
[HttpDelete]
|
||||
public ActionResult DeleteScenario(string name)
|
||||
public async Task<ActionResult> DeleteScenario(string name)
|
||||
{
|
||||
return Ok(_scenarioService.DeleteScenario(name));
|
||||
var user = await GetUser();
|
||||
return Ok(_scenarioService.DeleteScenarioByUser(user, name));
|
||||
}
|
||||
|
||||
// Update scenario
|
||||
[HttpPut]
|
||||
public ActionResult UpdateScenario(string name, List<string> strategies, int? loopbackPeriod = null)
|
||||
public async Task<ActionResult> UpdateScenario(string name, List<string> strategies, int? loopbackPeriod = null)
|
||||
{
|
||||
return Ok(_scenarioService.UpdateScenario(name, strategies, loopbackPeriod));
|
||||
var user = await GetUser();
|
||||
return Ok(_scenarioService.UpdateScenarioByUser(user, name, strategies, loopbackPeriod));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all strategies.
|
||||
/// Retrieves all strategies for the authenticated user.
|
||||
/// </summary>
|
||||
/// <returns>A list of strategies.</returns>
|
||||
[HttpGet]
|
||||
[Route("strategy")]
|
||||
public ActionResult<IEnumerable<Strategy>> GetStrategies()
|
||||
public async Task<ActionResult<IEnumerable<Strategy>>> GetStrategies()
|
||||
{
|
||||
return Ok(_scenarioService.GetStrategies());
|
||||
var user = await GetUser();
|
||||
return Ok(_scenarioService.GetStrategiesByUser(user));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new strategy with specified parameters.
|
||||
/// Creates a new strategy with specified parameters for the authenticated user.
|
||||
/// </summary>
|
||||
/// <param name="strategyType">The type of the strategy.</param>
|
||||
/// <param name="timeframe">The timeframe for the strategy.</param>
|
||||
/// <param name="name">The name of the strategy.</param>
|
||||
/// <param name="period">The period for the strategy (optional).</param>
|
||||
/// <param name="fastPeriods">The fast periods for the strategy (optional).</param>
|
||||
@@ -97,7 +106,7 @@ public class ScenarioController : ControllerBase
|
||||
/// <returns>The created strategy.</returns>
|
||||
[HttpPost]
|
||||
[Route("strategy")]
|
||||
public ActionResult<Strategy> CreateStrategy(
|
||||
public async Task<ActionResult<Strategy>> CreateStrategy(
|
||||
StrategyType strategyType,
|
||||
string name,
|
||||
int? period = null,
|
||||
@@ -109,7 +118,9 @@ public class ScenarioController : ControllerBase
|
||||
int? smoothPeriods = null,
|
||||
int? cyclePeriods = null)
|
||||
{
|
||||
return Ok(_scenarioService.CreateStrategy(
|
||||
var user = await GetUser();
|
||||
return Ok(_scenarioService.CreateStrategyForUser(
|
||||
user,
|
||||
strategyType,
|
||||
name,
|
||||
period,
|
||||
@@ -123,21 +134,22 @@ public class ScenarioController : ControllerBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a strategy by name.
|
||||
/// Deletes a strategy by name for the authenticated user.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the strategy to delete.</param>
|
||||
/// <returns>An ActionResult indicating the outcome of the operation.</returns>
|
||||
[HttpDelete]
|
||||
[Route("strategy")]
|
||||
public ActionResult DeleteStrategy(string name)
|
||||
public async Task<ActionResult> DeleteStrategy(string name)
|
||||
{
|
||||
return Ok(_scenarioService.DeleteStrategy(name));
|
||||
var user = await GetUser();
|
||||
return Ok(_scenarioService.DeleteStrategyByUser(user, name));
|
||||
}
|
||||
|
||||
// Update strategy
|
||||
[HttpPut]
|
||||
[Route("strategy")]
|
||||
public ActionResult UpdateStrategy(
|
||||
public async Task<ActionResult> UpdateStrategy(
|
||||
StrategyType strategyType,
|
||||
string name,
|
||||
int? period = null,
|
||||
@@ -149,7 +161,9 @@ public class ScenarioController : ControllerBase
|
||||
int? smoothPeriods = null,
|
||||
int? cyclePeriods = null)
|
||||
{
|
||||
return Ok(_scenarioService.UpdateStrategy(
|
||||
var user = await GetUser();
|
||||
return Ok(_scenarioService.UpdateStrategyByUser(
|
||||
user,
|
||||
strategyType,
|
||||
name,
|
||||
period,
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
using Managing.Application.Abstractions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Api.Controllers;
|
||||
|
||||
@@ -8,10 +18,12 @@ namespace Managing.Api.Controllers;
|
||||
[Authorize]
|
||||
[Route("[controller]")]
|
||||
[Produces("application/json")]
|
||||
public class SettingsController : ControllerBase
|
||||
public class SettingsController : BaseController
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
public SettingsController(ISettingsService settingsService)
|
||||
|
||||
public SettingsController(ISettingsService settingsService, IUserService userService)
|
||||
: base(userService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
}
|
||||
@@ -27,4 +39,26 @@ public class SettingsController : ControllerBase
|
||||
{
|
||||
return Ok(await _settingsService.ResetSettings());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates default configuration for backtesting including Money Management, Strategy, and Scenario
|
||||
/// </summary>
|
||||
/// <returns>A result indicating if the default configuration was created successfully</returns>
|
||||
[HttpPost]
|
||||
[Route("create-default-config")]
|
||||
public async Task<ActionResult<bool>> CreateDefaultConfiguration()
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await GetUser();
|
||||
if (user == null)
|
||||
return Unauthorized("User not found");
|
||||
|
||||
return Ok(await _settingsService.CreateDefaultConfiguration(user));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"Error creating default configuration: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Managing.Api.Controllers;
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("[controller]")]
|
||||
public class TradingController : ControllerBase
|
||||
public class TradingController : BaseController
|
||||
{
|
||||
private readonly ICommandHandler<OpenPositionRequest, Position> _openTradeCommandHandler;
|
||||
private readonly ICommandHandler<ClosePositionCommand, Position> _closeTradeCommandHandler;
|
||||
@@ -39,7 +39,8 @@ public class TradingController : ControllerBase
|
||||
ICommandHandler<OpenPositionRequest, Position> openTradeCommandHandler,
|
||||
ICommandHandler<ClosePositionCommand, Position> closeTradeCommandHandler,
|
||||
ITradingService tradingService,
|
||||
IMediator mediator, IMoneyManagementService moneyManagementService)
|
||||
IMediator mediator, IMoneyManagementService moneyManagementService,
|
||||
IUserService userService) : base(userService)
|
||||
{
|
||||
_logger = logger;
|
||||
_openTradeCommandHandler = openTradeCommandHandler;
|
||||
@@ -137,7 +138,8 @@ public class TradingController : ControllerBase
|
||||
|
||||
if (moneyManagement != null)
|
||||
{
|
||||
moneyManagement = await _moneyManagementService.GetMoneyMangement(moneyManagementName);
|
||||
var user = await GetUser();
|
||||
moneyManagement = await _moneyManagementService.GetMoneyMangement(user, moneyManagementName);
|
||||
}
|
||||
|
||||
var command = new OpenPositionRequest(
|
||||
|
||||
@@ -11,7 +11,6 @@ COPY ["Managing.Api/Managing.Api.csproj", "Managing.Api/"]
|
||||
COPY ["Managing.Bootstrap/Managing.Bootstrap.csproj", "Managing.Bootstrap/"]
|
||||
COPY ["Managing.Infrastructure.Storage/Managing.Infrastructure.Storage.csproj", "Managing.Infrastructure.Storage/"]
|
||||
COPY ["Managing.Application/Managing.Application.csproj", "Managing.Application/"]
|
||||
COPY ["Managing.Infrastructure.MongoDb/Managing.Infrastructure.MongoDb.csproj", "Managing.Infrastructure.MongoDb/"]
|
||||
COPY ["Managing.Common/Managing.Common.csproj", "Managing.Common/"]
|
||||
COPY ["Managing.Core/Managing.Core.csproj", "Managing.Core/"]
|
||||
COPY ["Managing.Application.Abstractions/Managing.Application.Abstractions.csproj", "Managing.Application.Abstractions/"]
|
||||
|
||||
@@ -9,37 +9,19 @@ namespace Managing.Api.Models.Responses
|
||||
{
|
||||
public class TradingBot
|
||||
{
|
||||
[Required]
|
||||
public string Name { get; internal set; }
|
||||
[Required]
|
||||
public string Status { get; internal set; }
|
||||
[Required]
|
||||
public List<Signal> Signals { get; internal set; }
|
||||
[Required]
|
||||
public List<Position> Positions { get; internal set; }
|
||||
[Required]
|
||||
public List<Candle> Candles { get; internal set; }
|
||||
[Required]
|
||||
public RiskLevel RiskLevel { get; internal set; }
|
||||
[Required]
|
||||
public int WinRate { get; internal set; }
|
||||
[Required]
|
||||
public decimal ProfitAndLoss { get; internal set; }
|
||||
[Required]
|
||||
public Timeframe Timeframe { get; internal set; }
|
||||
[Required]
|
||||
public Ticker Ticker { get; internal set; }
|
||||
[Required]
|
||||
public string Scenario { get; internal set; }
|
||||
[Required]
|
||||
public TradingExchanges Exchange { get; internal set; }
|
||||
[Required]
|
||||
public bool IsForWatchingOnly { get; internal set; }
|
||||
[Required]
|
||||
public BotType BotType { get; internal set; }
|
||||
[Required]
|
||||
public string AccountName { get; internal set; }
|
||||
[Required]
|
||||
public MoneyManagement MoneyManagement { get; internal set; }
|
||||
[Required] public string Name { get; internal set; }
|
||||
[Required] public string Status { get; internal set; }
|
||||
[Required] public List<Signal> Signals { get; internal set; }
|
||||
[Required] public List<Position> Positions { get; internal set; }
|
||||
[Required] public List<Candle> Candles { get; internal set; }
|
||||
[Required] public int WinRate { get; internal set; }
|
||||
[Required] public decimal ProfitAndLoss { get; internal set; }
|
||||
[Required] public Timeframe Timeframe { get; internal set; }
|
||||
[Required] public Ticker Ticker { get; internal set; }
|
||||
[Required] public string Scenario { get; internal set; }
|
||||
[Required] public bool IsForWatchingOnly { get; internal set; }
|
||||
[Required] public BotType BotType { get; internal set; }
|
||||
[Required] public string AccountName { get; internal set; }
|
||||
[Required] public MoneyManagement MoneyManagement { get; internal set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,9 @@
|
||||
"AppId": "cm6f47n1l003jx7mjwaembhup",
|
||||
"AppSecret": "63Chz2z5M8TgR5qc8dznSLRAGTHTyPU4cjdQobrBF1Cx5tszZpTuFgyrRd7hZ2k6HpwDz3GEwQZzsCqHb8Z311bF"
|
||||
},
|
||||
"Web3Proxy": {
|
||||
"BaseUrl": "http://srv-captain--web3-proxy:4111"
|
||||
},
|
||||
"Serilog": {
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
"Organization": "",
|
||||
"Token": ""
|
||||
},
|
||||
"Web3Proxy": {
|
||||
"BaseUrl": "http://localhost:3000"
|
||||
},
|
||||
"Serilog": {
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Users;
|
||||
|
||||
namespace Managing.Application.Abstractions.Repositories;
|
||||
|
||||
public interface IBacktestRepository
|
||||
{
|
||||
void InsertBacktest(Backtest result);
|
||||
IEnumerable<Backtest> GetBacktests();
|
||||
void DeleteBacktestById(string id);
|
||||
void DeleteAllBacktests();
|
||||
void InsertBacktestForUser(User user, Backtest result);
|
||||
IEnumerable<Backtest> GetBacktestsByUser(User user);
|
||||
Backtest GetBacktestByIdForUser(User user, string id);
|
||||
void DeleteBacktestByIdForUser(User user, string id);
|
||||
void DeleteAllBacktestsForUser(User user);
|
||||
}
|
||||
@@ -10,6 +10,14 @@ public interface ICandleRepository
|
||||
Enums.Ticker ticker,
|
||||
Enums.Timeframe timeframe,
|
||||
DateTime start);
|
||||
|
||||
Task<IList<Candle>> GetCandles(
|
||||
Enums.TradingExchanges exchange,
|
||||
Enums.Ticker ticker,
|
||||
Enums.Timeframe timeframe,
|
||||
DateTime start,
|
||||
DateTime end);
|
||||
|
||||
Task<IList<Enums.Ticker>> GetTickersAsync(
|
||||
Enums.TradingExchanges exchange,
|
||||
Enums.Timeframe timeframe,
|
||||
|
||||
@@ -25,7 +25,7 @@ public interface IEvmManager
|
||||
decimal GetVolume(SubgraphProvider subgraphProvider, Ticker ticker);
|
||||
Task<List<Ticker>> GetAvailableTicker();
|
||||
Task<Candle> GetCandle(SubgraphProvider subgraphProvider, Ticker ticker);
|
||||
Task<bool> InitAddress(string chainName, string publicAddress, string privateKey);
|
||||
Task<bool> InitAddress(string publicAddress);
|
||||
|
||||
Task<bool> Send(Chain chain, Ticker ticker, decimal amount, string publicAddress, string privateKey,
|
||||
string receiverAddress);
|
||||
@@ -34,7 +34,9 @@ public interface IEvmManager
|
||||
Task<bool> CancelOrders(Account account, Ticker ticker);
|
||||
|
||||
Task<Trade> IncreasePosition(Account account, Ticker ticker, TradeDirection direction, decimal price,
|
||||
decimal quantity, decimal? leverage = 1);
|
||||
decimal quantity, decimal? leverage = 1,
|
||||
decimal? stopLossPrice = null,
|
||||
decimal? takeProfitPrice = null);
|
||||
|
||||
Task<Trade> GetTrade(Account account, string chainName, Ticker ticker);
|
||||
|
||||
@@ -44,11 +46,22 @@ public interface IEvmManager
|
||||
Task<decimal> QuantityInPosition(string chainName, string publicAddress, Ticker ticker);
|
||||
|
||||
Task<Trade> DecreaseOrder(Account account, TradeType tradeType, Ticker ticker, TradeDirection direction,
|
||||
decimal price, decimal quantity, decimal? leverage);
|
||||
decimal price, decimal quantity, decimal? leverage,
|
||||
decimal? stopLossPrice = null,
|
||||
decimal? takeProfitPrice = null);
|
||||
|
||||
Task<decimal> GetFee(string chainName);
|
||||
Task<List<Trade>> GetOrders(Account account, Ticker ticker);
|
||||
Task<Trade> GetTrade(string reference, string arbitrum, Ticker ticker);
|
||||
Task<List<FundingRate>> GetFundingRates();
|
||||
Task<(string Id, string Address)> CreatePrivyWallet();
|
||||
|
||||
/// <summary>
|
||||
/// Signs a message using the embedded wallet
|
||||
/// </summary>
|
||||
/// <param name="embeddedWalletId">The wallet id of the embedded wallet</param>
|
||||
/// <param name="address">The address of the embedded wallet</param>
|
||||
/// <param name="message">The message to sign</param>
|
||||
/// <returns>The signature response</returns>
|
||||
Task<string> SignMessageAsync(string embeddedWalletId, string address, string message);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Users;
|
||||
|
||||
namespace Managing.Application.Abstractions.Repositories;
|
||||
|
||||
@@ -10,4 +11,10 @@ public interface ISettingsRepository
|
||||
IEnumerable<MoneyManagement> GetMoneyManagements();
|
||||
void DeleteMoneyManagement(string name);
|
||||
void DeleteMoneyManagements();
|
||||
|
||||
// User-specific operations (these should eventually replace the above methods)
|
||||
Task<MoneyManagement> GetMoneyManagementByUser(User user, string name);
|
||||
IEnumerable<MoneyManagement> GetMoneyManagementsByUser(User user);
|
||||
void DeleteMoneyManagementByUser(User user, string name);
|
||||
void DeleteMoneyManagementsByUser(User user);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Abstractions.Repositories;
|
||||
@@ -9,6 +10,8 @@ public interface ITradingRepository
|
||||
{
|
||||
Scenario GetScenarioByName(string scenario);
|
||||
void InsertSignal(Signal signal);
|
||||
IEnumerable<Signal> GetSignalsByUser(User user);
|
||||
Signal GetSignalByIdentifier(string identifier, User user = null);
|
||||
void InsertPosition(Position position);
|
||||
void UpdatePosition(Position position);
|
||||
Strategy GetStrategyByName(string strategy);
|
||||
|
||||
@@ -6,4 +6,5 @@ public interface IUserRepository
|
||||
{
|
||||
Task<User> GetUserByNameAsync(string name);
|
||||
Task InsertUserAsync(User user);
|
||||
}
|
||||
Task UpdateUser(User user);
|
||||
}
|
||||
@@ -3,28 +3,54 @@ using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Abstractions.Services
|
||||
{
|
||||
public interface IBacktester
|
||||
{
|
||||
Backtest RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Ticker ticker,
|
||||
Scenario scenario, Timeframe timeframe, double days, decimal balance, bool isForWatchingOnly = false,
|
||||
bool save = false, List<Candle>? initialCandles = null);
|
||||
Task<Backtest> RunScalpingBotBacktest(
|
||||
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);
|
||||
|
||||
Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Ticker ticker,
|
||||
Scenario scenario, Timeframe timeframe, double days, decimal balance, bool isForWatchingOnly = false,
|
||||
bool save = false, List<Candle>? initialCandles = null);
|
||||
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);
|
||||
|
||||
IEnumerable<Backtest> GetBacktests();
|
||||
bool DeleteBacktest(string id);
|
||||
bool DeleteBacktests();
|
||||
|
||||
Backtest RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario,
|
||||
Timeframe timeframe, List<Candle> candles, decimal balance);
|
||||
Task<Backtest> RunScalpingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario,
|
||||
Timeframe timeframe, List<Candle> candles, decimal balance, User user = null);
|
||||
|
||||
Backtest RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario,
|
||||
Timeframe timeframe, List<Candle> candles, decimal balance);
|
||||
Task<Backtest> RunFlippingBotBacktest(Account account, MoneyManagement moneyManagement, Scenario scenario,
|
||||
Timeframe timeframe, List<Candle> candles, decimal balance, User user = null);
|
||||
|
||||
// User-specific operations
|
||||
Task<IEnumerable<Backtest>> GetBacktestsByUser(User user);
|
||||
Backtest GetBacktestByIdForUser(User user, string id);
|
||||
bool DeleteBacktestByUser(User user, string id);
|
||||
bool DeleteBacktestsByUser(User user);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,9 @@ public interface IExchangeService
|
||||
bool reduceOnly = false,
|
||||
bool isForPaperTrading = false,
|
||||
DateTime? currentDate = null,
|
||||
bool ioc = true);
|
||||
bool ioc = true,
|
||||
decimal? stopLossPrice = null,
|
||||
decimal? takeProfitPrice = null);
|
||||
|
||||
Task<decimal> GetBalance(Account account, bool isForPaperTrading = false);
|
||||
Task<List<Balance>> GetBalances(Account account, bool isForPaperTrading = false);
|
||||
@@ -47,6 +49,9 @@ public interface IExchangeService
|
||||
Task<List<Candle>> GetCandlesInflux(TradingExchanges exchange, Ticker ticker, DateTime startDate,
|
||||
Timeframe timeframe);
|
||||
|
||||
Task<List<Candle>> GetCandlesInflux(TradingExchanges exchange, Ticker ticker, DateTime startDate,
|
||||
Timeframe timeframe, DateTime endDate);
|
||||
|
||||
decimal GetBestPrice(Account account, Ticker ticker, decimal lastPrice, decimal quantity, TradeDirection direction);
|
||||
Orderbook GetOrderbook(Account account, Ticker ticker);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ public class BaseTests
|
||||
_moneyManagementService = new Mock<IMoneyManagementService>();
|
||||
MoneyManagement = new MoneyManagement()
|
||||
{
|
||||
BalanceAtRisk = 0.30m, // 30%
|
||||
BalanceAtRisk = 1m, // 30%
|
||||
Leverage = 2, // x2
|
||||
Timeframe = Timeframe.FifteenMinutes,
|
||||
StopLoss = 0.008m, // 0.8%
|
||||
@@ -32,7 +32,7 @@ public class BaseTests
|
||||
Name = "Default MM"
|
||||
};
|
||||
|
||||
_ = _moneyManagementService.Setup(m => m.GetMoneyMangement(It.IsAny<string>()))
|
||||
_ = _moneyManagementService.Setup(m => m.GetMoneyMangement(It.IsAny<Domain.Users.User>(), It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(MoneyManagement));
|
||||
|
||||
_accountService.Setup(a => a.GetAccount(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>()))
|
||||
|
||||
@@ -9,6 +9,7 @@ using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Scenarios;
|
||||
using MongoDB.Driver.Linq;
|
||||
using Moq;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
@@ -30,6 +31,7 @@ namespace Managing.Application.Tests
|
||||
{
|
||||
var backtestRepository = new Mock<IBacktestRepository>().Object;
|
||||
var discordService = new Mock<IMessengerService>().Object;
|
||||
var scenarioService = new Mock<IScenarioService>().Object;
|
||||
var tradingBotLogger = TradingBaseTests.CreateTradingBotLogger();
|
||||
var backtestLogger = TradingBaseTests.CreateBacktesterLogger();
|
||||
var botService = new Mock<IBotService>().Object;
|
||||
@@ -40,13 +42,14 @@ namespace Managing.Application.Tests
|
||||
_accountService.Object,
|
||||
_tradingService.Object,
|
||||
botService);
|
||||
_backtester = new Backtester(_exchangeService, _botFactory, backtestRepository, backtestLogger);
|
||||
_backtester = new Backtester(_exchangeService, _botFactory, backtestRepository, backtestLogger,
|
||||
scenarioService);
|
||||
_elapsedTimes = new List<double>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(Ticker.BTC, Timeframe.FifteenMinutes, -10)]
|
||||
public void SwingBot_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe,
|
||||
public async Task SwingBot_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe,
|
||||
int days)
|
||||
{
|
||||
// Arrange
|
||||
@@ -57,8 +60,9 @@ namespace Managing.Application.Tests
|
||||
FileHelpers.ReadJson<List<Candle>>($"{ticker.ToString()}-{timeframe.ToString()}-candles.json");
|
||||
|
||||
// Act
|
||||
var backtestResult = _backtester.RunFlippingBotBacktest(_account, MoneyManagement, ticker, scenario,
|
||||
timeframe, Convert.ToDouble(days), 1000, initialCandles: localCandles.TakeLast(500).ToList());
|
||||
var backtestResult = await _backtester.RunFlippingBotBacktest(_account, MoneyManagement, ticker, scenario,
|
||||
timeframe, 1000, new DateTime().AddDays(-3), DateTime.UtcNow,
|
||||
initialCandles: localCandles.TakeLast(500).ToList());
|
||||
|
||||
var json = JsonConvert.SerializeObject(backtestResult, Formatting.None);
|
||||
File.WriteAllText($"{ticker.ToString()}-{timeframe.ToString()}-{Guid.NewGuid()}.json", json);
|
||||
@@ -78,7 +82,8 @@ namespace Managing.Application.Tests
|
||||
//[InlineData(Enums.Exchanges.Binance, "BTCUSDT", Timeframe.ThirtyMinutes, -4)]
|
||||
//[InlineData(Enums.Exchanges.Binance, "BTCUSDT", Timeframe.FifteenMinutes, -4)]
|
||||
[InlineData(Ticker.BTC, Timeframe.FifteenMinutes, -14)]
|
||||
public void ScalpingBot_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe,
|
||||
public async Task ScalpingBot_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker,
|
||||
Timeframe timeframe,
|
||||
int days)
|
||||
{
|
||||
// Arrange
|
||||
@@ -87,8 +92,8 @@ namespace Managing.Application.Tests
|
||||
scenario.AddStrategy(strategy);
|
||||
|
||||
// Act
|
||||
var backtestResult = _backtester.RunScalpingBotBacktest(_account, MoneyManagement, ticker, scenario,
|
||||
timeframe, Convert.ToDouble(days), 1000);
|
||||
var backtestResult = await _backtester.RunScalpingBotBacktest(_account, MoneyManagement, ticker, scenario,
|
||||
timeframe, 1000, DateTime.UtcNow.AddDays(-6), DateTime.UtcNow, null);
|
||||
//WriteCsvReport(backtestResult.GetStringReport());
|
||||
|
||||
// Assert
|
||||
@@ -99,7 +104,7 @@ namespace Managing.Application.Tests
|
||||
|
||||
[Theory]
|
||||
[InlineData(Ticker.BTC, Timeframe.FifteenMinutes, -8)]
|
||||
public void MacdCross_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe,
|
||||
public async Task MacdCross_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe,
|
||||
int days)
|
||||
{
|
||||
// Arrange
|
||||
@@ -118,8 +123,8 @@ namespace Managing.Application.Tests
|
||||
};
|
||||
|
||||
// Act
|
||||
var backtestResult = _backtester.RunScalpingBotBacktest(_account, moneyManagement, ticker, scenario,
|
||||
timeframe, Convert.ToDouble(days), 1000);
|
||||
var backtestResult = await _backtester.RunScalpingBotBacktest(_account, moneyManagement, ticker, scenario,
|
||||
timeframe, 1000, DateTime.UtcNow.AddDays(-6), DateTime.UtcNow, null);
|
||||
WriteCsvReport(backtestResult.GetStringReport());
|
||||
|
||||
// Assert
|
||||
@@ -190,9 +195,9 @@ namespace Managing.Application.Tests
|
||||
{
|
||||
BotType.SimpleBot => throw new NotImplementedException(),
|
||||
BotType.ScalpingBot => _backtester.RunScalpingBotBacktest(_account, moneyManagement,
|
||||
scenario, timeframe, candles, 1000),
|
||||
scenario, timeframe, candles, 1000, null).Result,
|
||||
BotType.FlippingBot => _backtester.RunFlippingBotBacktest(_account, moneyManagement,
|
||||
scenario, timeframe, candles, 1000),
|
||||
scenario, timeframe, candles, 1000, null).Result,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
timer.Stop();
|
||||
@@ -299,9 +304,9 @@ namespace Managing.Application.Tests
|
||||
{
|
||||
BotType.SimpleBot => throw new NotImplementedException(),
|
||||
BotType.ScalpingBot => _backtester.RunScalpingBotBacktest(_account, moneyManagement,
|
||||
scenario, timeframe, candles, 1000),
|
||||
scenario, timeframe, candles, 1000, null).Result,
|
||||
BotType.FlippingBot => _backtester.RunFlippingBotBacktest(_account, moneyManagement,
|
||||
scenario, timeframe, candles, 1000),
|
||||
scenario, timeframe, candles, 1000, null).Result,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
|
||||
@@ -7,13 +7,16 @@ using Managing.Infrastructure.Databases.InfluxDb;
|
||||
using Managing.Infrastructure.Databases.InfluxDb.Models;
|
||||
using Managing.Infrastructure.Evm;
|
||||
using Managing.Infrastructure.Evm.Abstractions;
|
||||
using Managing.Infrastructure.Evm.Models.Privy;
|
||||
using Managing.Infrastructure.Evm.Services;
|
||||
using Managing.Infrastructure.Evm.Subgraphs;
|
||||
using Managing.Infrastructure.Exchanges;
|
||||
using Managing.Infrastructure.Exchanges.Abstractions;
|
||||
using Managing.Infrastructure.Exchanges.Exchanges;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Nethereum.Web3;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Tests
|
||||
@@ -34,7 +37,7 @@ namespace Managing.Application.Tests
|
||||
Chainlink,
|
||||
GbcFeed
|
||||
};
|
||||
var evmManager = new EvmManager(Subgraphs);
|
||||
var evmManager = new EvmManager(Subgraphs, CreateWebProxyService());
|
||||
var evmProcessor = new EvmProcessor(new Mock<ILogger<EvmProcessor>>().Object, evmManager);
|
||||
|
||||
var exchangeProcessors = new List<IExchangeProcessor>()
|
||||
@@ -44,7 +47,8 @@ namespace Managing.Application.Tests
|
||||
evmProcessor
|
||||
};
|
||||
|
||||
return new ExchangeService(loggerFactory.CreateLogger<ExchangeService>(), GetCandleRepository(), exchangeProcessors);
|
||||
return new ExchangeService(loggerFactory.CreateLogger<ExchangeService>(), GetCandleRepository(),
|
||||
exchangeProcessors);
|
||||
}
|
||||
|
||||
public static ILogger<TradingBot> CreateTradingBotLogger()
|
||||
@@ -72,14 +76,22 @@ namespace Managing.Application.Tests
|
||||
{
|
||||
var settings = new InfluxDbSettings()
|
||||
{
|
||||
Url = "http://localhost:8086/",
|
||||
Token = "6b-OjFNaZRprYroZEx8zeLScvPqvOp9la1lEksXl8xRT0d96UyuN18iKpB6jKYFt8JJEX1NaxVMXhk-Sgy8sgg==",
|
||||
Organization = "managing-org"
|
||||
Url = "http://localhost:8086/",
|
||||
Token = "6b-OjFNaZRprYroZEx8zeLScvPqvOp9la1lEksXl8xRT0d96UyuN18iKpB6jKYFt8JJEX1NaxVMXhk-Sgy8sgg==",
|
||||
Organization = "managing-org"
|
||||
};
|
||||
var influxdb = new InfluxDbRepository(settings);
|
||||
var candleRepository = new CandleRepository(influxdb, CreateCandleRepositoryLogger());
|
||||
|
||||
return candleRepository;
|
||||
}
|
||||
|
||||
// Helper method to create Web3ProxyService for tests
|
||||
public static IWeb3ProxyService CreateWebProxyService(string baseUrl = "http://localhost:4111/api/")
|
||||
{
|
||||
var settings = new Web3ProxySettings { BaseUrl = baseUrl };
|
||||
var options = Options.Create(settings);
|
||||
return new Web3ProxyService(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,16 +216,16 @@ public class StatisticService : IStatisticService
|
||||
MaxDegreeOfParallelism = 2
|
||||
};
|
||||
|
||||
_ = Parallel.ForEach(tickers, options, ticker =>
|
||||
_ = Parallel.ForEach(tickers, options, async ticker =>
|
||||
{
|
||||
spotlight.TickerSignals.Add(new TickerSignal
|
||||
{
|
||||
Ticker = ticker,
|
||||
FiveMinutes = GetSignals(account, scenario, ticker, Timeframe.FiveMinutes),
|
||||
FifteenMinutes = GetSignals(account, scenario, ticker, Timeframe.FifteenMinutes),
|
||||
OneHour = GetSignals(account, scenario, ticker, Timeframe.OneHour),
|
||||
FourHour = GetSignals(account, scenario, ticker, Timeframe.FourHour),
|
||||
OneDay = GetSignals(account, scenario, ticker, Timeframe.OneDay)
|
||||
FiveMinutes = await GetSignals(account, scenario, ticker, Timeframe.FiveMinutes),
|
||||
FifteenMinutes = await GetSignals(account, scenario, ticker, Timeframe.FifteenMinutes),
|
||||
OneHour = await GetSignals(account, scenario, ticker, Timeframe.OneHour),
|
||||
FourHour = await GetSignals(account, scenario, ticker, Timeframe.FourHour),
|
||||
OneDay = await GetSignals(account, scenario, ticker, Timeframe.OneDay)
|
||||
});
|
||||
});
|
||||
|
||||
@@ -237,7 +237,7 @@ public class StatisticService : IStatisticService
|
||||
_statisticRepository.UpdateSpotlightOverview(overview);
|
||||
}
|
||||
|
||||
private List<Signal> GetSignals(Account account, Scenario scenario, Ticker ticker, Timeframe timeframe)
|
||||
private async Task<List<Signal>> GetSignals(Account account, Scenario scenario, Ticker ticker, Timeframe timeframe)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -250,14 +250,15 @@ public class StatisticService : IStatisticService
|
||||
TakeProfit = 0.02m
|
||||
};
|
||||
|
||||
var backtest = _backtester.RunScalpingBotBacktest(
|
||||
var backtest = await _backtester.RunScalpingBotBacktest(
|
||||
account,
|
||||
moneyManagement,
|
||||
ticker,
|
||||
scenario,
|
||||
timeframe,
|
||||
CandleExtensions.GetMinimalDays(timeframe),
|
||||
1000,
|
||||
DateTime.Now.AddDays(-7),
|
||||
DateTime.Now,
|
||||
isForWatchingOnly: true);
|
||||
|
||||
return backtest.Signals;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -135,6 +135,10 @@ public static class ApiBootstrap
|
||||
services.AddSingleton<IBotService, BotService>();
|
||||
services.AddSingleton<IWorkerService, WorkerService>();
|
||||
services.AddTransient<IPrivyService, PrivyService>();
|
||||
|
||||
// Web3Proxy Configuration
|
||||
services.Configure<Web3ProxySettings>(configuration.GetSection("Web3Proxy"));
|
||||
services.AddTransient<IWeb3ProxyService, Web3ProxyService>();
|
||||
|
||||
// Stream
|
||||
services.AddSingleton<IBinanceSocketClient, BinanceSocketClient>();
|
||||
|
||||
@@ -6,14 +6,13 @@ namespace Managing.Domain.Accounts;
|
||||
|
||||
public class Account
|
||||
{
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
[Required]
|
||||
public TradingExchanges Exchange { get; set; }
|
||||
[Required]
|
||||
public AccountType Type { get; set; }
|
||||
[Required] public string Name { get; set; }
|
||||
[Required] public TradingExchanges Exchange { get; set; }
|
||||
[Required] public AccountType Type { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Secret { get; set; }
|
||||
public User User { get; set; }
|
||||
public List<Balance> Balances { get; set; }
|
||||
|
||||
public bool IsPrivyWallet => Type == AccountType.Privy;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Managing.Domain.Candles;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Domain.Users;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using static Managing.Common.Enums;
|
||||
@@ -29,6 +30,20 @@ public class Backtest
|
||||
Scenario = scenario;
|
||||
BotType = botType;
|
||||
AccountName = accountName;
|
||||
WalletBalances = new List<KeyValuePair<DateTime, decimal>>();
|
||||
StrategiesValues = new Dictionary<StrategyType, StrategiesResultBase>();
|
||||
|
||||
// Initialize start and end dates if candles are provided
|
||||
if (candles != null && candles.Count > 0)
|
||||
{
|
||||
StartDate = candles.Min(c => c.Date);
|
||||
EndDate = candles.Max(c => c.Date);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartDate = DateTime.UtcNow.AddDays(-30);
|
||||
EndDate = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
[Required] public string Id { get; set; }
|
||||
@@ -43,13 +58,15 @@ public class Backtest
|
||||
[Required] public Timeframe Timeframe { get; }
|
||||
[Required] public BotType BotType { get; }
|
||||
[Required] public string AccountName { get; }
|
||||
[Required] public List<Candle> Candles { get; }
|
||||
[Required] public List<Candle> Candles { get; set; }
|
||||
[Required] public DateTime StartDate { get; set; }
|
||||
[Required] public DateTime EndDate { get; set; }
|
||||
[Required] public PerformanceMetrics Statistics { get; set; }
|
||||
[Required] public decimal Fees { get; set; }
|
||||
[Required] public List<KeyValuePair<DateTime, decimal>> WalletBalances { get; set; }
|
||||
[Required] public MoneyManagement OptimizedMoneyManagement { get; set; }
|
||||
[Required] public MoneyManagement MoneyManagement { get; set; }
|
||||
|
||||
[Required] public User User { get; set; }
|
||||
[Required] public Dictionary<StrategyType, StrategiesResultBase> StrategiesValues { get; set; }
|
||||
[Required] public double Score { get; set; }
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using static Managing.Common.Enums;
|
||||
using System.Collections.Concurrent;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Bots
|
||||
{
|
||||
@@ -13,6 +15,7 @@ namespace Managing.Domain.Bots
|
||||
public string Name { get; set; }
|
||||
public int Interval { get; set; }
|
||||
public BotStatus Status { get; set; }
|
||||
public User User { get; set; }
|
||||
private CancellationTokenSource CancellationToken { get; set; }
|
||||
|
||||
public Bot(string name)
|
||||
@@ -22,6 +25,7 @@ namespace Managing.Domain.Bots
|
||||
Status = BotStatus.Down;
|
||||
CancellationToken = new CancellationTokenSource();
|
||||
ExecutionCount = 0;
|
||||
Interval = 3000;
|
||||
}
|
||||
|
||||
public virtual void Start()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.MoneyManagements
|
||||
@@ -17,6 +18,8 @@ namespace Managing.Domain.MoneyManagements
|
||||
public decimal TakeProfit { get; set; }
|
||||
[Required]
|
||||
public decimal Leverage { get; set; }
|
||||
|
||||
public User User { get; set; }
|
||||
|
||||
public void FormatPercentage()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Users;
|
||||
|
||||
namespace Managing.Domain.Scenarios
|
||||
{
|
||||
@@ -14,6 +15,7 @@ namespace Managing.Domain.Scenarios
|
||||
public string Name { get; set; }
|
||||
public List<Strategy> Strategies { get; set; }
|
||||
public int? LoopbackPeriod { get; set; }
|
||||
public User User { get; set; }
|
||||
|
||||
public void AddStrategy(Strategy strategy)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ public static class TradingBox
|
||||
// Ensure limitedCandles is ordered chronologically
|
||||
var orderedCandles = limitedCandles.OrderBy(c => c.Date).ToList();
|
||||
|
||||
var loopback = loopbackPeriod ?? 1;
|
||||
var loopback = loopbackPeriod.HasValue && loopbackPeriod > 1 ? loopbackPeriod.Value : 1;
|
||||
var candleLoopback = orderedCandles.TakeLast(loopback).ToList();
|
||||
|
||||
if (!candleLoopback.Any())
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies
|
||||
@@ -18,9 +19,10 @@ namespace Managing.Domain.Strategies
|
||||
[Required] public TradingExchanges Exchange { get; set; }
|
||||
[Required] public StrategyType StrategyType { get; set; }
|
||||
[Required] public SignalType SignalType { get; set; }
|
||||
public User User { get; set; }
|
||||
|
||||
public Signal(Ticker ticker, TradeDirection direction, Confidence confidence, Candle candle, DateTime date,
|
||||
TradingExchanges exchange, StrategyType strategyType, SignalType signalType)
|
||||
TradingExchanges exchange, StrategyType strategyType, SignalType signalType, User user = null)
|
||||
{
|
||||
Direction = direction;
|
||||
Confidence = confidence;
|
||||
@@ -30,6 +32,7 @@ namespace Managing.Domain.Strategies
|
||||
Exchange = exchange;
|
||||
Status = SignalStatus.WaitingForPosition;
|
||||
StrategyType = strategyType;
|
||||
User = user;
|
||||
|
||||
Identifier = $"{StrategyType}-{direction}-{ticker}-{candle?.Close}-{date:yyyyMMdd-HHmmss}";
|
||||
SignalType = signalType;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Managing.Core.FixedSizedQueue;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Managing.Domain.Users;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies
|
||||
@@ -14,6 +15,7 @@ namespace Managing.Domain.Strategies
|
||||
Name = name;
|
||||
Type = type;
|
||||
SignalType = ScenarioHelpers.GetSignalType(type);
|
||||
Candles = new FixedSizeQueue<Candle>(500);
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
@@ -29,6 +31,7 @@ namespace Managing.Domain.Strategies
|
||||
public int? SmoothPeriods { get; set; }
|
||||
public int? StochPeriods { get; set; }
|
||||
public int? CyclePeriods { get; set; }
|
||||
public User User { get; set; }
|
||||
|
||||
public virtual List<Signal> Run()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Users;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
@@ -43,6 +44,7 @@ namespace Managing.Domain.Trades
|
||||
public string Identifier { get; set; }
|
||||
[Required]
|
||||
public PositionInitiator Initiator { get; }
|
||||
public User User { get; set; }
|
||||
|
||||
public bool IsFinished()
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Users;
|
||||
using Managing.Infrastructure.Databases.MongoDb;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Abstractions;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
@@ -15,24 +16,54 @@ public class BacktestRepository : IBacktestRepository
|
||||
_backtestRepository = backtestRepository;
|
||||
}
|
||||
|
||||
public void DeleteAllBacktests()
|
||||
// User-specific operations
|
||||
public void InsertBacktestForUser(User user, Backtest backtest)
|
||||
{
|
||||
_backtestRepository.DropCollection();
|
||||
backtest.User = user;
|
||||
_backtestRepository.InsertOne(MongoMappers.Map(backtest));
|
||||
}
|
||||
|
||||
public void DeleteBacktestById(string id)
|
||||
public IEnumerable<Backtest> GetBacktestsByUser(User user)
|
||||
{
|
||||
_backtestRepository.DeleteById(id);
|
||||
}
|
||||
var backtests = _backtestRepository.AsQueryable()
|
||||
.Where(b => b.User != null && b.User.Name == user.Name)
|
||||
.ToList();
|
||||
|
||||
public IEnumerable<Backtest> GetBacktests()
|
||||
{
|
||||
var backtests = _backtestRepository.FindAll();
|
||||
return backtests.Select(b => MongoMappers.Map(b));
|
||||
}
|
||||
|
||||
public void InsertBacktest(Backtest backtest)
|
||||
public Backtest GetBacktestByIdForUser(User user, string id)
|
||||
{
|
||||
_backtestRepository.InsertOne(MongoMappers.Map(backtest));
|
||||
var backtest = _backtestRepository.FindById(id);
|
||||
|
||||
// Check if backtest exists and belongs to the user
|
||||
if (backtest != null && backtest.User != null && backtest.User.Name == user.Name)
|
||||
{
|
||||
return MongoMappers.Map(backtest);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DeleteBacktestByIdForUser(User user, string id)
|
||||
{
|
||||
var backtest = _backtestRepository.FindById(id);
|
||||
|
||||
if (backtest != null && backtest.User != null && backtest.User.Name == user.Name)
|
||||
{
|
||||
_backtestRepository.DeleteById(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteAllBacktestsForUser(User user)
|
||||
{
|
||||
var backtests = _backtestRepository.AsQueryable()
|
||||
.Where(b => b.User != null && b.User.Name == user.Name)
|
||||
.ToList();
|
||||
|
||||
foreach (var backtest in backtests)
|
||||
{
|
||||
_backtestRepository.DeleteById(backtest.Id.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,29 @@ public class CandleRepository : ICandleRepository
|
||||
return results;
|
||||
}
|
||||
|
||||
public async Task<IList<Candle>> GetCandles(
|
||||
TradingExchanges exchange,
|
||||
Ticker ticker,
|
||||
Timeframe timeframe,
|
||||
DateTime start,
|
||||
DateTime end)
|
||||
{
|
||||
var results = await _influxDbRepository.QueryAsync(async query =>
|
||||
{
|
||||
var flux = $"from(bucket:\"{_priceBucket}\") " +
|
||||
$"|> range(start: {start:s}Z, stop: {end:s}Z) " +
|
||||
$"|> filter(fn: (r) => r[\"exchange\"] == \"{exchange}\")" +
|
||||
$"|> filter(fn: (r) => r[\"ticker\"] == \"{ticker}\")" +
|
||||
$"|> filter(fn: (r) => r[\"timeframe\"] == \"{timeframe}\")" +
|
||||
$"|> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")";
|
||||
|
||||
var prices = await query.QueryAsync<PriceDto>(flux, _influxDbRepository.Organization);
|
||||
return prices.Select(price => PriceHelpers.Map(price)).ToList();
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public async Task<IList<Ticker>> GetTickersAsync(
|
||||
TradingExchanges exchange,
|
||||
Timeframe timeframe,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using Exilion.TradingAtomics;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
@@ -19,8 +20,13 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
public RiskLevel RiskLevel { get; set; }
|
||||
public string AccountName { get; set; }
|
||||
public List<CandleDto> Candles { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
public BotType BotType { get; set; }
|
||||
public MoneyManagementDto MoneyManagement { get; internal set; }
|
||||
public MoneyManagementDto OptimizedMoneyManagement { get; internal set; }
|
||||
public UserDto User { get; set; }
|
||||
public PerformanceMetrics Statistics { get; set; }
|
||||
public double Score { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
public decimal TakeProfit { get; set; }
|
||||
public decimal Leverage { get; set; }
|
||||
public string Name { get; internal set; }
|
||||
public UserDto User { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,5 +23,6 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
public string AccountName { get; set; }
|
||||
public MoneyManagementDto MoneyManagement { get; set; }
|
||||
public PositionInitiator Initiator { get; set; }
|
||||
public UserDto User { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
public string Name { get; set; }
|
||||
public List<StrategyDto> Strategies { get; set; }
|
||||
public int LoopbackPeriod { get; set; }
|
||||
public UserDto User { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -17,5 +17,6 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public StrategyType Type { get; set; }
|
||||
public SignalType SignalType { get; set; }
|
||||
public UserDto User { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
public StrategyType Type { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int MinimumHistory { get; set; }
|
||||
public int? Period { get; set; }
|
||||
public int? FastPeriods { get; set; }
|
||||
public int? SlowPeriods { get; set; }
|
||||
@@ -19,5 +20,6 @@ namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
public int? SmoothPeriods { get; set; }
|
||||
public int? CyclePeriods { get; set; }
|
||||
public SignalType SignalType { get; set; }
|
||||
public UserDto User { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Candles;
|
||||
@@ -12,6 +13,8 @@ using Managing.Domain.Workers;
|
||||
using Managing.Domain.Workflows.Synthetics;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
using static Managing.Common.Enums;
|
||||
using MongoDB.Bson;
|
||||
using Managing.Domain.Shared.Helpers;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb;
|
||||
|
||||
@@ -126,47 +129,63 @@ public static class MongoMappers
|
||||
|
||||
internal static Backtest Map(BacktestDto b)
|
||||
{
|
||||
return new Backtest(
|
||||
ticker: b.Ticker,
|
||||
scenario: b.Scenario,
|
||||
positions: b.Positions.ConvertAll(bPosition => Map(bPosition)),
|
||||
signals: b.Signals != null ? b.Signals.ConvertAll(bSignal => Map(bSignal)) : null,
|
||||
timeframe: b.Timeframe,
|
||||
candles: b.Candles.ConvertAll(bCandle => Map(bCandle)),
|
||||
accountName: b.AccountName,
|
||||
botType: b.BotType)
|
||||
if (b == null)
|
||||
return null;
|
||||
|
||||
var bTest = new Backtest(
|
||||
b.Ticker,
|
||||
b.Scenario,
|
||||
b.Positions?.Select(p => Map(p)).ToList() ?? new List<Position>(),
|
||||
b.Signals?.Select(s => Map(s)).ToList() ?? new List<Signal>(),
|
||||
b.Timeframe,
|
||||
b.Candles?.Select(c => Map(c)).ToList() ?? new List<Candle>(),
|
||||
b.BotType,
|
||||
b.AccountName)
|
||||
{
|
||||
Id = b.Id.ToString(),
|
||||
FinalPnl = b.FinalPnl,
|
||||
WinRate = b.WinRate,
|
||||
GrowthPercentage = b.GrowthPercentage,
|
||||
HodlPercentage = b.HodlPercentage,
|
||||
Id = b.Id.ToString(),
|
||||
MoneyManagement = Map(b.MoneyManagement),
|
||||
OptimizedMoneyManagement = Map(b.OptimizedMoneyManagement)
|
||||
OptimizedMoneyManagement = Map(b.OptimizedMoneyManagement),
|
||||
User = b.User != null ? Map(b.User) : null,
|
||||
Statistics = b.Statistics,
|
||||
StartDate = b.StartDate,
|
||||
EndDate = b.EndDate,
|
||||
Score = b.Score
|
||||
};
|
||||
|
||||
return bTest;
|
||||
}
|
||||
|
||||
internal static BacktestDto Map(Backtest result)
|
||||
{
|
||||
var backtest = new BacktestDto
|
||||
if (result == null)
|
||||
return null;
|
||||
|
||||
return new BacktestDto
|
||||
{
|
||||
Id = (!string.IsNullOrEmpty(result.Id)) ? ObjectId.Parse(result.Id) : ObjectId.GenerateNewId(),
|
||||
FinalPnl = result.FinalPnl,
|
||||
WinRate = result.WinRate,
|
||||
GrowthPercentage = result.GrowthPercentage,
|
||||
HodlPercentage = result.HodlPercentage,
|
||||
Candles = Map(result.Candles),
|
||||
Positions = Map(result.Positions),
|
||||
Signals = result.Signals.Select(s => Map(s)).ToList(),
|
||||
Ticker = result.Ticker,
|
||||
Scenario = result.Scenario,
|
||||
AccountName = result.AccountName,
|
||||
BotType = result.BotType,
|
||||
Timeframe = result.Timeframe,
|
||||
MoneyManagement = Map(result.MoneyManagement),
|
||||
OptimizedMoneyManagement = Map(result.OptimizedMoneyManagement),
|
||||
User = result.User != null ? Map(result.User) : null,
|
||||
Statistics = result.Statistics,
|
||||
StartDate = result.StartDate,
|
||||
EndDate = result.EndDate,
|
||||
Score = result.Score
|
||||
};
|
||||
|
||||
backtest.Timeframe = result.Timeframe;
|
||||
backtest.Ticker = result.Ticker;
|
||||
backtest.Scenario = result.Scenario;
|
||||
|
||||
return backtest;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -237,7 +256,8 @@ public static class MongoMappers
|
||||
AccountName = position.AccountName,
|
||||
MoneyManagement = Map(position.MoneyManagement),
|
||||
Initiator = position.Initiator,
|
||||
Ticker = position.Ticker
|
||||
Ticker = position.Ticker,
|
||||
User = position.User != null ? Map(position.User) : null
|
||||
};
|
||||
|
||||
if (position.StopLoss != null)
|
||||
@@ -284,7 +304,8 @@ public static class MongoMappers
|
||||
ProfitAndLoss = new ProfitAndLoss { Realized = dto.ProfitAndLoss },
|
||||
Status = dto.Status,
|
||||
SignalIdentifier = dto.SignalIdentifier,
|
||||
Identifier = dto.Identifier
|
||||
Identifier = dto.Identifier,
|
||||
User = dto.User != null ? Map(dto.User) : null
|
||||
};
|
||||
|
||||
if (dto.StopLoss != null)
|
||||
@@ -327,26 +348,42 @@ public static class MongoMappers
|
||||
{
|
||||
return new SignalDto
|
||||
{
|
||||
Identifier = signal.Identifier,
|
||||
Direction = signal.Direction,
|
||||
Candle = Map(signal.Candle),
|
||||
Confidence = signal.Confidence,
|
||||
Date = signal.Date,
|
||||
Candle = Map(signal.Candle),
|
||||
Identifier = signal.Identifier,
|
||||
Ticker = signal.Ticker,
|
||||
Status = signal.Status,
|
||||
Timeframe = signal.Timeframe,
|
||||
Type = signal.StrategyType
|
||||
Type = signal.StrategyType,
|
||||
User = signal.User != null ? Map(signal.User) : null
|
||||
};
|
||||
}
|
||||
|
||||
internal static Signal Map(SignalDto bSignal)
|
||||
{
|
||||
return new Signal(ticker: bSignal.Ticker, direction: bSignal.Direction, confidence: bSignal.Confidence,
|
||||
candle: Map(bSignal.Candle), date: bSignal.Date, exchange: default,
|
||||
strategyType: bSignal.Type, signalType: bSignal.SignalType)
|
||||
var candle = Map(bSignal.Candle);
|
||||
var signal = new Signal(
|
||||
bSignal.Ticker,
|
||||
bSignal.Direction,
|
||||
bSignal.Confidence,
|
||||
candle,
|
||||
bSignal.Date,
|
||||
TradingExchanges.Binance, //TODO FIXME When the signal status is modified from controller
|
||||
bSignal.Type,
|
||||
bSignal.SignalType,
|
||||
bSignal.User != null ? Map(bSignal.User) : null)
|
||||
{
|
||||
Status = bSignal.Status
|
||||
};
|
||||
|
||||
if (bSignal.User != null)
|
||||
{
|
||||
signal.User = Map(bSignal.User);
|
||||
}
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -355,11 +392,15 @@ public static class MongoMappers
|
||||
|
||||
public static ScenarioDto Map(Scenario scenario)
|
||||
{
|
||||
return new ScenarioDto()
|
||||
if (scenario == null)
|
||||
return null;
|
||||
|
||||
return new ScenarioDto
|
||||
{
|
||||
Name = scenario.Name,
|
||||
Strategies = Map(scenario.Strategies),
|
||||
LoopbackPeriod = scenario.LoopbackPeriod ?? 1
|
||||
LoopbackPeriod = scenario.LoopbackPeriod ?? 1,
|
||||
User = scenario.User != null ? Map(scenario.User) : null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -370,12 +411,15 @@ public static class MongoMappers
|
||||
|
||||
internal static Scenario Map(ScenarioDto d)
|
||||
{
|
||||
return new Scenario(d.Name)
|
||||
if (d == null)
|
||||
return null;
|
||||
|
||||
var scenario = new Scenario(d.Name, d.LoopbackPeriod)
|
||||
{
|
||||
Name = d.Name,
|
||||
Strategies = Map(d.Strategies).ToList(),
|
||||
LoopbackPeriod = d.LoopbackPeriod > 0 ? d.LoopbackPeriod : 1
|
||||
Strategies = d.Strategies.Select(s => Map(s)).ToList(),
|
||||
User = d.User != null ? Map(d.User) : null
|
||||
};
|
||||
return scenario;
|
||||
}
|
||||
|
||||
private static List<StrategyDto> Map(List<Strategy> strategies)
|
||||
@@ -385,8 +429,13 @@ public static class MongoMappers
|
||||
|
||||
internal static Strategy Map(StrategyDto strategyDto)
|
||||
{
|
||||
return new Strategy(name: strategyDto.Name, type: strategyDto.Type)
|
||||
if (strategyDto == null)
|
||||
return null;
|
||||
|
||||
return new Strategy(strategyDto.Name, strategyDto.Type)
|
||||
{
|
||||
SignalType = strategyDto.SignalType,
|
||||
MinimumHistory = strategyDto.MinimumHistory,
|
||||
Period = strategyDto.Period,
|
||||
FastPeriods = strategyDto.FastPeriods,
|
||||
SlowPeriods = strategyDto.SlowPeriods,
|
||||
@@ -395,64 +444,31 @@ public static class MongoMappers
|
||||
SmoothPeriods = strategyDto.SmoothPeriods,
|
||||
StochPeriods = strategyDto.StochPeriods,
|
||||
CyclePeriods = strategyDto.CyclePeriods,
|
||||
SignalType = strategyDto.SignalType
|
||||
User = strategyDto.User != null ? Map(strategyDto.User) : null
|
||||
};
|
||||
}
|
||||
|
||||
internal static StrategyDto Map(Strategy strategy)
|
||||
{
|
||||
var dto = new StrategyDto
|
||||
if (strategy == null)
|
||||
return null;
|
||||
|
||||
return new StrategyDto
|
||||
{
|
||||
Type = strategy.Type,
|
||||
Name = strategy.Name,
|
||||
Type = strategy.Type,
|
||||
SignalType = strategy.SignalType,
|
||||
CyclePeriods = strategy.CyclePeriods,
|
||||
FastPeriods = strategy.FastPeriods,
|
||||
Multiplier = strategy.Multiplier,
|
||||
MinimumHistory = strategy.MinimumHistory,
|
||||
Period = strategy.Period,
|
||||
SignalPeriods = strategy.SignalPeriods,
|
||||
FastPeriods = strategy.FastPeriods,
|
||||
SlowPeriods = strategy.SlowPeriods,
|
||||
SignalPeriods = strategy.SignalPeriods,
|
||||
Multiplier = strategy.Multiplier,
|
||||
SmoothPeriods = strategy.SmoothPeriods,
|
||||
StochPeriods = strategy.StochPeriods
|
||||
StochPeriods = strategy.StochPeriods,
|
||||
CyclePeriods = strategy.CyclePeriods,
|
||||
User = strategy.User != null ? Map(strategy.User) : null
|
||||
};
|
||||
|
||||
// switch (strategy.Type)
|
||||
// {
|
||||
// case StrategyType.RsiDivergenceConfirm:
|
||||
// case StrategyType.RsiDivergence:
|
||||
// case StrategyType.EmaCross:
|
||||
// case StrategyType.EmaTrend:
|
||||
// case StrategyType.StDev:
|
||||
// dto.Period = strategy.Period;
|
||||
// break;
|
||||
// case StrategyType.MacdCross:
|
||||
// dto.SlowPeriods = strategy.SlowPeriods;
|
||||
// dto.FastPeriods = strategy.FastPeriods;
|
||||
// dto.SignalPeriods = strategy.SignalPeriods;
|
||||
// break;
|
||||
// case StrategyType.ThreeWhiteSoldiers:
|
||||
// break;
|
||||
// case StrategyType.ChandelierExit:
|
||||
// case StrategyType.SuperTrend:
|
||||
// dto.Period = strategy.Period;
|
||||
// dto.Multiplier = strategy.Multiplier;
|
||||
// break;
|
||||
// case StrategyType.StochRsiTrend:
|
||||
// dto.Period = strategy.Period;
|
||||
// dto.StochPeriods = strategy.StochPeriods;
|
||||
// dto.SignalPeriods = strategy.SignalPeriods;
|
||||
// dto.SmoothPeriods = strategy.SmoothPeriods;
|
||||
// break;
|
||||
// case StrategyType.Stc:
|
||||
// dto.SlowPeriods = strategy.SlowPeriods;
|
||||
// dto.FastPeriods = strategy.FastPeriods;
|
||||
// dto.CyclePeriods = strategy.CyclePeriods;
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
internal static IEnumerable<Strategy> Map(IEnumerable<StrategyDto> strategies)
|
||||
@@ -474,7 +490,8 @@ public static class MongoMappers
|
||||
StopLoss = request.StopLoss,
|
||||
TakeProfit = request.TakeProfit,
|
||||
Leverage = request.Leverage,
|
||||
Name = request.Name
|
||||
Name = request.Name,
|
||||
User = request.User != null ? Map(request.User) : null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -490,7 +507,8 @@ public static class MongoMappers
|
||||
StopLoss = request.StopLoss,
|
||||
TakeProfit = request.TakeProfit,
|
||||
Leverage = request.Leverage,
|
||||
Name = request.Name
|
||||
Name = request.Name,
|
||||
User = request.User != null ? Map(request.User) : null
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Users;
|
||||
using Managing.Infrastructure.Databases.MongoDb;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Abstractions;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
@@ -50,4 +51,49 @@ public class SettingsRepository : ISettingsRepository
|
||||
dto.Id = mm.Id;
|
||||
_moneyManagementRepository.Update(dto);
|
||||
}
|
||||
|
||||
// User-specific implementations
|
||||
public async Task<MoneyManagement> GetMoneyManagementByUser(User user, string name)
|
||||
{
|
||||
var moneyManagement = await _moneyManagementRepository.FindOneAsync(m =>
|
||||
m.Name == name &&
|
||||
m.User != null &&
|
||||
m.User.Name == user.Name);
|
||||
|
||||
return MongoMappers.Map(moneyManagement);
|
||||
}
|
||||
|
||||
public IEnumerable<MoneyManagement> GetMoneyManagementsByUser(User user)
|
||||
{
|
||||
var moneyManagements = _moneyManagementRepository.AsQueryable()
|
||||
.Where(m => m.User != null && m.User.Name == user.Name)
|
||||
.ToList();
|
||||
|
||||
return moneyManagements.Select(m => MongoMappers.Map(m));
|
||||
}
|
||||
|
||||
public void DeleteMoneyManagementByUser(User user, string name)
|
||||
{
|
||||
var moneyManagement = _moneyManagementRepository.FindOne(m =>
|
||||
m.Name == name &&
|
||||
m.User != null &&
|
||||
m.User.Name == user.Name);
|
||||
|
||||
if (moneyManagement != null)
|
||||
{
|
||||
_moneyManagementRepository.DeleteById(moneyManagement.Id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteMoneyManagementsByUser(User user)
|
||||
{
|
||||
var moneyManagements = _moneyManagementRepository.AsQueryable()
|
||||
.Where(m => m.User != null && m.User.Name == user.Name)
|
||||
.ToList();
|
||||
|
||||
foreach (var moneyManagement in moneyManagements)
|
||||
{
|
||||
_moneyManagementRepository.DeleteById(moneyManagement.Id.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Domain.Users;
|
||||
using Managing.Infrastructure.Databases.MongoDb;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Abstractions;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
@@ -100,23 +101,98 @@ public class TradingRepository : ITradingRepository
|
||||
|
||||
public void InsertPosition(Position position)
|
||||
{
|
||||
_positionRepository.InsertOne(MongoMappers.Map(position));
|
||||
// Check if position already exists for the same user
|
||||
var existingPosition = _positionRepository.FindOne(p =>
|
||||
p.Identifier == position.Identifier &&
|
||||
(position.User == null || (p.User != null && p.User.Name == position.User.Name)));
|
||||
|
||||
if (existingPosition != null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Position with identifier '{position.Identifier}' already exists for user '{position.User?.Name}'");
|
||||
}
|
||||
|
||||
var dto = MongoMappers.Map(position);
|
||||
_positionRepository.InsertOne(dto);
|
||||
}
|
||||
|
||||
public void InsertScenario(Scenario scenario)
|
||||
{
|
||||
_scenarioRepository.CreateIndex(nameof(Scenario.Name));
|
||||
_scenarioRepository.InsertOne(MongoMappers.Map(scenario));
|
||||
// Check if scenario already exists for the same user
|
||||
var existingScenario = _scenarioRepository.FindOne(s =>
|
||||
s.Name == scenario.Name &&
|
||||
(scenario.User == null || (s.User != null && s.User.Name == scenario.User.Name)));
|
||||
|
||||
if (existingScenario != null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Scenario with name '{scenario.Name}' already exists for user '{scenario.User?.Name}'");
|
||||
}
|
||||
|
||||
var strategyDtos = new List<StrategyDto>();
|
||||
foreach (var strategy in scenario.Strategies)
|
||||
{
|
||||
var dto = _strategyRepository.FindOne(s => s.Name == strategy.Name);
|
||||
strategyDtos.Add(dto);
|
||||
}
|
||||
|
||||
var scenarioDto = new ScenarioDto
|
||||
{
|
||||
Name = scenario.Name,
|
||||
Strategies = strategyDtos,
|
||||
User = scenario.User != null ? MongoMappers.Map(scenario.User) : null
|
||||
};
|
||||
|
||||
_scenarioRepository.InsertOne(scenarioDto);
|
||||
}
|
||||
|
||||
public void InsertSignal(Signal signal)
|
||||
{
|
||||
_signalRepository.InsertOne(MongoMappers.Map(signal));
|
||||
// Check if signal already exists with the same identifier, date, and user
|
||||
var existingSignal = _signalRepository.FindOne(s =>
|
||||
s.Identifier == signal.Identifier &&
|
||||
s.Date == signal.Date &&
|
||||
((s.User == null && signal.User == null) ||
|
||||
(s.User != null && signal.User != null && s.User.Name == signal.User.Name)));
|
||||
|
||||
if (existingSignal != null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Signal with identifier '{signal.Identifier}' and date '{signal.Date}' already exists for this user");
|
||||
}
|
||||
|
||||
var dto = MongoMappers.Map(signal);
|
||||
_signalRepository.InsertOne(dto);
|
||||
}
|
||||
|
||||
public void InsertStrategy(Strategy strategy)
|
||||
{
|
||||
_strategyRepository.InsertOne(MongoMappers.Map(strategy));
|
||||
// Check if strategy already exists for the same user
|
||||
var existingStrategy = _strategyRepository.FindOne(s =>
|
||||
s.Name == strategy.Name &&
|
||||
(strategy.User == null || (s.User != null && s.User.Name == strategy.User.Name)));
|
||||
|
||||
if (existingStrategy != null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Strategy with name '{strategy.Name}' already exists for user '{strategy.User?.Name}'");
|
||||
}
|
||||
|
||||
var dto = MongoMappers.Map(strategy);
|
||||
_strategyRepository.InsertOne(dto);
|
||||
}
|
||||
|
||||
public void InsertFee(Fee fee)
|
||||
{
|
||||
// Check if fee for this exchange already exists (fee is global, not user-specific)
|
||||
var existingFee = _feeRepository.FindOne(f => f.Exchange == fee.Exchange);
|
||||
if (existingFee != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Fee for exchange '{fee.Exchange}' already exists");
|
||||
}
|
||||
|
||||
var dto = MongoMappers.Map(fee);
|
||||
_feeRepository.InsertOne(dto);
|
||||
}
|
||||
|
||||
public void UpdatePosition(Position position)
|
||||
@@ -133,9 +209,41 @@ public class TradingRepository : ITradingRepository
|
||||
return MongoMappers.Map(fee);
|
||||
}
|
||||
|
||||
public void InsertFee(Fee fee)
|
||||
public IEnumerable<Signal> GetSignalsByUser(User user)
|
||||
{
|
||||
_feeRepository.InsertOne(MongoMappers.Map(fee));
|
||||
IEnumerable<SignalDto> signals;
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
signals = _signalRepository.FilterBy(s => s.User == null);
|
||||
}
|
||||
else
|
||||
{
|
||||
signals = _signalRepository.FilterBy(s => s.User != null && s.User.Name == user.Name);
|
||||
}
|
||||
|
||||
return signals.Select(MongoMappers.Map);
|
||||
}
|
||||
|
||||
public Signal GetSignalByIdentifier(string identifier, User user = null)
|
||||
{
|
||||
SignalDto signal;
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
signal = _signalRepository.FindOne(s =>
|
||||
s.Identifier == identifier &&
|
||||
s.User == null);
|
||||
}
|
||||
else
|
||||
{
|
||||
signal = _signalRepository.FindOne(s =>
|
||||
s.Identifier == identifier &&
|
||||
s.User != null &&
|
||||
s.User.Name == user.Name);
|
||||
}
|
||||
|
||||
return MongoMappers.Map(signal);
|
||||
}
|
||||
|
||||
public void UpdateFee(Fee fee)
|
||||
|
||||
@@ -25,4 +25,19 @@ public class UserRepository : IUserRepository
|
||||
{
|
||||
await _userRepository.InsertOneAsync(MongoMappers.Map(user));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateUser(User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dto = await _userRepository.FindOneAsync(u => u.Name == user.Name);
|
||||
dto.Name = user.Name;
|
||||
_userRepository.Update(dto);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw new Exception("Cannot update user");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Managing.Infrastructure.Evm.Models.Privy;
|
||||
|
||||
public class PrivyApproveTokenResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Hash { get; set; }
|
||||
public string? Error { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Managing.Infrastructure.Evm.Models.Privy;
|
||||
|
||||
public class PrivyInitAddressResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? UsdcHash { get; set; }
|
||||
public string? OrderVaultHash { get; set; }
|
||||
public string? Error { get; set; }
|
||||
}
|
||||
@@ -22,7 +22,9 @@ public interface IExchangeProcessor
|
||||
bool reduceOnly = false,
|
||||
bool isForPaperTrading = false,
|
||||
DateTime? currentDate = null,
|
||||
bool ioc = true);
|
||||
bool ioc = true,
|
||||
decimal? stopLossPrice = null,
|
||||
decimal? takeProfitPrice = null);
|
||||
Task<decimal> GetBalance(Account account, bool isForPaperTrading = false);
|
||||
Task<List<Balance>> GetBalances(Account account, bool isForPaperTrading = false);
|
||||
decimal GetPrice(Account account, Ticker ticker, DateTime date);
|
||||
|
||||
@@ -37,10 +37,12 @@ namespace Managing.Infrastructure.Exchanges
|
||||
bool reduceOnly = false,
|
||||
bool isForPaperTrading = false,
|
||||
DateTime? currentDate = null,
|
||||
bool ioc = true)
|
||||
bool ioc = true,
|
||||
decimal? stopLossPrice = null,
|
||||
decimal? takeProfitPrice = null)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
$"OpenMarketTrade - {ticker} - Type: {tradeType} - {direction} - Price: {price} - Quantity: {quantity} - Leverage: {leverage}");
|
||||
$"OpenMarketTrade - {ticker} - Type: {tradeType} - {direction} - Price: {price} - Quantity: {quantity} - Leverage: {leverage} - SL: {stopLossPrice} - TP: {takeProfitPrice}");
|
||||
|
||||
if (isForPaperTrading)
|
||||
{
|
||||
@@ -50,7 +52,7 @@ namespace Managing.Infrastructure.Exchanges
|
||||
|
||||
var processor = GetProcessor(account);
|
||||
return await processor.OpenTrade(account, ticker, direction, price, quantity, leverage, tradeType,
|
||||
reduceOnly, isForPaperTrading, currentDate, ioc);
|
||||
reduceOnly, isForPaperTrading, currentDate, ioc, stopLossPrice, takeProfitPrice);
|
||||
}
|
||||
|
||||
private IExchangeProcessor GetProcessor(Account account)
|
||||
@@ -205,6 +207,13 @@ namespace Managing.Infrastructure.Exchanges
|
||||
return candlesFromRepo.ToList();
|
||||
}
|
||||
|
||||
public async Task<List<Candle>> GetCandlesInflux(TradingExchanges exchange, Ticker ticker, DateTime startDate,
|
||||
Timeframe timeframe, DateTime endDate)
|
||||
{
|
||||
var candlesFromRepo = await _candleRepository.GetCandles(exchange, ticker, timeframe, startDate, endDate);
|
||||
return candlesFromRepo.ToList();
|
||||
}
|
||||
|
||||
public async Task<decimal> GetBalance(Account account, bool isForPaperTrading = false)
|
||||
{
|
||||
if (isForPaperTrading)
|
||||
|
||||
@@ -22,7 +22,20 @@ namespace Managing.Infrastructure.Exchanges.Exchanges
|
||||
public abstract Task<Trade> GetTrade(Account account, string order, Ticker ticker);
|
||||
public abstract Task<List<Trade>> GetTrades(Account account, Ticker ticker);
|
||||
public abstract decimal GetVolume(Account account, Ticker ticker);
|
||||
public abstract Task<Trade> OpenTrade(Account account, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage = null, Enums.TradeType tradeType = Enums.TradeType.Limit, bool reduceOnly = false, bool isForPaperTrading = false, DateTime? currentDate = null, bool ioc = true);
|
||||
public abstract Task<Trade> OpenTrade(
|
||||
Account account,
|
||||
Ticker ticker,
|
||||
TradeDirection direction,
|
||||
decimal price,
|
||||
decimal quantity,
|
||||
decimal? leverage = null,
|
||||
TradeType tradeType = TradeType.Limit,
|
||||
bool reduceOnly = false,
|
||||
bool isForPaperTrading = false,
|
||||
DateTime? currentDate = null,
|
||||
bool ioc = true,
|
||||
decimal? stopLossPrice = null,
|
||||
decimal? takeProfitPrice = null);
|
||||
public abstract Orderbook GetOrderbook(Account account, Ticker ticker);
|
||||
public abstract Task<List<Balance>> GetBalances(Account account, bool isForPaperTrading = false);
|
||||
public abstract Task<List<Trade>> GetOrders(Account account, Ticker ticker);
|
||||
|
||||
@@ -27,7 +27,8 @@ public class BinanceProcessor : BaseProcessor
|
||||
|
||||
public override async Task<bool> CancelOrder(Account account, Ticker ticker)
|
||||
{
|
||||
var binanceResult = await _binanceClient.UsdFuturesApi.Trading.CancelAllOrdersAsync(BinanceHelpers.ToBinanceTicker(ticker));
|
||||
var binanceResult =
|
||||
await _binanceClient.UsdFuturesApi.Trading.CancelAllOrdersAsync(BinanceHelpers.ToBinanceTicker(ticker));
|
||||
return binanceResult.Success;
|
||||
}
|
||||
|
||||
@@ -41,6 +42,7 @@ public class BinanceProcessor : BaseProcessor
|
||||
{
|
||||
balance += item.AvailableBalance;
|
||||
}
|
||||
|
||||
return balance;
|
||||
}
|
||||
|
||||
@@ -54,12 +56,15 @@ public class BinanceProcessor : BaseProcessor
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override async Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe timeframe)
|
||||
public override async Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate,
|
||||
Timeframe timeframe)
|
||||
{
|
||||
var binanceCandles = await _binanceClient.UsdFuturesApi.ExchangeData.GetKlinesAsync(BinanceHelpers.ToBinanceTicker(ticker),
|
||||
BinanceHelpers.Map(timeframe), startDate);
|
||||
var binanceCandles = await _binanceClient.UsdFuturesApi.ExchangeData.GetKlinesAsync(
|
||||
BinanceHelpers.ToBinanceTicker(ticker),
|
||||
BinanceHelpers.Map(timeframe), startDate);
|
||||
|
||||
return (List<Candle>)binanceCandles.Data.Select(binanceKline => BinanceHelpers.Map(binanceKline, ticker, account.Exchange));
|
||||
return (List<Candle>)binanceCandles.Data.Select(binanceKline =>
|
||||
BinanceHelpers.Map(binanceKline, ticker, account.Exchange));
|
||||
}
|
||||
|
||||
public override decimal GetFee(Account account, bool isForPaperTrading = false)
|
||||
@@ -79,7 +84,8 @@ public class BinanceProcessor : BaseProcessor
|
||||
|
||||
public override decimal GetPrice(Account account, Ticker ticker, DateTime date)
|
||||
{
|
||||
var binancePrice = _binanceClient.UsdFuturesApi.ExchangeData.GetPriceAsync(BinanceHelpers.ToBinanceTicker(ticker)).Result.Data;
|
||||
var binancePrice = _binanceClient.UsdFuturesApi.ExchangeData
|
||||
.GetPriceAsync(BinanceHelpers.ToBinanceTicker(ticker)).Result.Data;
|
||||
return binancePrice.Price;
|
||||
}
|
||||
|
||||
@@ -90,7 +96,9 @@ public class BinanceProcessor : BaseProcessor
|
||||
|
||||
public override async Task<Trade> GetTrade(Account account, string order, Ticker ticker)
|
||||
{
|
||||
var binanceOrder = await _binanceClient.UsdFuturesApi.Trading.GetOrderAsync(BinanceHelpers.ToBinanceTicker(ticker), origClientOrderId: order);
|
||||
var binanceOrder =
|
||||
await _binanceClient.UsdFuturesApi.Trading.GetOrderAsync(BinanceHelpers.ToBinanceTicker(ticker),
|
||||
origClientOrderId: order);
|
||||
return BinanceHelpers.Map(binanceOrder.Data);
|
||||
}
|
||||
|
||||
@@ -107,7 +115,7 @@ public class BinanceProcessor : BaseProcessor
|
||||
public override async Task<List<Trade>> GetTrades(Account account, Ticker ticker)
|
||||
{
|
||||
var binanceOrder =
|
||||
await _binanceClient.UsdFuturesApi.Trading.GetOrdersAsync(BinanceHelpers.ToBinanceTicker(ticker));
|
||||
await _binanceClient.UsdFuturesApi.Trading.GetOrdersAsync(BinanceHelpers.ToBinanceTicker(ticker));
|
||||
return (List<Trade>)binanceOrder.Data.Select(o => BinanceHelpers.Map(o));
|
||||
}
|
||||
|
||||
@@ -122,9 +130,14 @@ public class BinanceProcessor : BaseProcessor
|
||||
_binanceClient = new BinanceRestClient((options) => { options.ApiCredentials = credentials; });
|
||||
}
|
||||
|
||||
public override async Task<Trade> OpenTrade(Account account, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage = null, TradeType tradeType = TradeType.Limit, bool reduceOnly = false, bool isForPaperTrading = false, DateTime? currentDate = null, bool ioc = true)
|
||||
public override async Task<Trade> OpenTrade(Account account, Ticker ticker, TradeDirection direction, decimal price,
|
||||
decimal quantity, decimal? leverage = null, TradeType tradeType = TradeType.Limit, bool reduceOnly = false,
|
||||
bool isForPaperTrading = false, DateTime? currentDate = null, bool ioc = true,
|
||||
decimal? stopLossPrice = null,
|
||||
decimal? takeProfitPrice = null)
|
||||
{
|
||||
var trade = new Trade(DateTime.Now, direction, TradeStatus.PendingOpen, tradeType, ticker, quantity, price, leverage, "", "");
|
||||
var trade = new Trade(DateTime.Now, direction, TradeStatus.PendingOpen, tradeType, ticker, quantity, price,
|
||||
leverage, "", "");
|
||||
trade.SetQuantity(quantity, GetQuantityPrecision(account, ticker));
|
||||
trade.SetPrice(price, GetPricePrecision(account, ticker));
|
||||
|
||||
@@ -158,6 +171,7 @@ public class BinanceProcessor : BaseProcessor
|
||||
trade.SetExchangeOrderId("");
|
||||
trade.SetMessage("");
|
||||
}
|
||||
|
||||
return trade;
|
||||
}
|
||||
|
||||
@@ -172,7 +186,8 @@ public class BinanceProcessor : BaseProcessor
|
||||
private int GetQuantityPrecision(Account account, Ticker ticker)
|
||||
{
|
||||
var binanceFutureInfo = _binanceClient.UsdFuturesApi.ExchangeData.GetExchangeInfoAsync().Result.Data;
|
||||
var precision = binanceFutureInfo.Symbols.Single(p => p.Name == BinanceHelpers.ToBinanceTicker(ticker)).QuantityPrecision;
|
||||
var precision = binanceFutureInfo.Symbols.Single(p => p.Name == BinanceHelpers.ToBinanceTicker(ticker))
|
||||
.QuantityPrecision;
|
||||
return Convert.ToInt32(precision);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,8 @@ public class EvmProcessor : BaseProcessor
|
||||
return _evmManager.GetCandle(SubgraphProvider.Gbc, ticker).Result;
|
||||
}
|
||||
|
||||
public override async Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval)
|
||||
public override async Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate,
|
||||
Timeframe interval)
|
||||
{
|
||||
return await _evmManager.GetCandles(SubgraphProvider.Gbc, ticker, startDate, interval);
|
||||
}
|
||||
@@ -119,7 +120,9 @@ public class EvmProcessor : BaseProcessor
|
||||
bool reduceOnly = false,
|
||||
bool isForPaperTrading = false,
|
||||
DateTime? currentDate = null,
|
||||
bool ioc = true)
|
||||
bool ioc = true,
|
||||
decimal? stopLossPrice = null,
|
||||
decimal? takeProfitPrice = null)
|
||||
{
|
||||
Trade trade;
|
||||
if (reduceOnly)
|
||||
@@ -128,7 +131,8 @@ public class EvmProcessor : BaseProcessor
|
||||
or TradeType.StopLoss)
|
||||
{
|
||||
// If trade type is TP or SL we create DecreaseOrder
|
||||
trade = await _evmManager.DecreaseOrder(account, tradeType, ticker, direction, price, quantity, leverage);
|
||||
trade = await _evmManager.DecreaseOrder(account, tradeType, ticker, direction, price, quantity,
|
||||
leverage);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -140,24 +144,23 @@ public class EvmProcessor : BaseProcessor
|
||||
}
|
||||
else
|
||||
{
|
||||
trade = await _evmManager.IncreasePosition(account, ticker, direction, price, quantity, leverage);
|
||||
trade = await _evmManager.IncreasePosition(account, ticker, direction, price, quantity, leverage,
|
||||
stopLossPrice, takeProfitPrice);
|
||||
}
|
||||
|
||||
return trade;
|
||||
return trade;
|
||||
}
|
||||
|
||||
public override async Task<List<Trade>> GetOrders(Account account, Ticker ticker)
|
||||
{
|
||||
return await _evmManager.GetOrders(account, ticker);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Not implemented
|
||||
|
||||
public override void LoadClient(Account account)
|
||||
{
|
||||
{
|
||||
// No client needed
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -175,4 +178,4 @@ public class EvmProcessor : BaseProcessor
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ public class FtxProcessor : BaseProcessor
|
||||
{
|
||||
balance += item.UsdValue;
|
||||
}
|
||||
|
||||
return balance;
|
||||
}
|
||||
|
||||
@@ -61,7 +62,7 @@ public class FtxProcessor : BaseProcessor
|
||||
LoadClient(account);
|
||||
|
||||
var ftxKlines = _ftxClient.TradeApi.ExchangeData.GetKlinesAsync(FtxHelpers.ToFtxTicker(ticker),
|
||||
FTX.Net.Enums.KlineInterval.OneMinute, date.AddHours(-2.5)).Result.Data;
|
||||
FTX.Net.Enums.KlineInterval.OneMinute, date.AddHours(-2.5)).Result.Data;
|
||||
if (ftxKlines != null && ftxKlines.Any())
|
||||
{
|
||||
var lastCandle = ftxKlines.ToList().LastOrDefault();
|
||||
@@ -71,16 +72,18 @@ public class FtxProcessor : BaseProcessor
|
||||
return null;
|
||||
}
|
||||
|
||||
public override async Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe timeframe)
|
||||
public override async Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate,
|
||||
Timeframe timeframe)
|
||||
{
|
||||
LoadClient(account);
|
||||
|
||||
var candles = new List<Candle>();
|
||||
var ftxCandles = await _ftxClient.TradeApi.ExchangeData.GetKlinesAsync(FtxHelpers.ToFtxTicker(ticker),
|
||||
FtxHelpers.Map(timeframe), startDate);
|
||||
FtxHelpers.Map(timeframe), startDate);
|
||||
|
||||
if (ftxCandles.Success)
|
||||
candles.AddRange(ftxCandles.Data.SkipLast(1).Select(ftxKline => FtxHelpers.Map(ftxKline, ticker, account.Exchange, timeframe)));
|
||||
candles.AddRange(ftxCandles.Data.SkipLast(1)
|
||||
.Select(ftxKline => FtxHelpers.Map(ftxKline, ticker, account.Exchange, timeframe)));
|
||||
|
||||
return candles;
|
||||
}
|
||||
@@ -96,11 +99,12 @@ public class FtxProcessor : BaseProcessor
|
||||
{
|
||||
LoadClient(account);
|
||||
var ftxKlines = _ftxClient.TradeApi.ExchangeData.GetKlinesAsync(FtxHelpers.ToFtxTicker(ticker),
|
||||
FTX.Net.Enums.KlineInterval.OneMinute, date.AddHours(-2.5)).Result.Data;
|
||||
FTX.Net.Enums.KlineInterval.OneMinute, date.AddHours(-2.5)).Result.Data;
|
||||
if (ftxKlines != null && ftxKlines.Any())
|
||||
{
|
||||
return ftxKlines.ToList().LastOrDefault().ClosePrice;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -126,29 +130,36 @@ public class FtxProcessor : BaseProcessor
|
||||
|
||||
public override decimal GetVolume(Account account, Ticker ticker)
|
||||
{
|
||||
var futureStats = _ftxClient.TradeApi.ExchangeData.GetFutureStatsAsync(FtxHelpers.ToFtxTicker(ticker)).Result.Data;
|
||||
var futureStats = _ftxClient.TradeApi.ExchangeData.GetFutureStatsAsync(FtxHelpers.ToFtxTicker(ticker)).Result
|
||||
.Data;
|
||||
return futureStats.Volume;
|
||||
}
|
||||
|
||||
public override async Task<Trade> OpenTrade(Account account, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage = null, TradeType tradeType = TradeType.Limit, bool reduceOnly = false, bool isForPaperTrading = false, DateTime? currentDate = null, bool ioc = true)
|
||||
public override async Task<Trade> OpenTrade(Account account, Ticker ticker, TradeDirection direction, decimal price,
|
||||
decimal quantity, decimal? leverage = null, TradeType tradeType = TradeType.Limit, bool reduceOnly = false,
|
||||
bool isForPaperTrading = false, DateTime? currentDate = null, bool ioc = true,
|
||||
decimal? stopLossPrice = null,
|
||||
decimal? takeProfitPrice = null)
|
||||
{
|
||||
LoadClient(account);
|
||||
|
||||
var trade = new Trade(DateTime.Now, direction, TradeStatus.PendingOpen, tradeType, ticker, quantity, price, leverage, "", "");
|
||||
var trade = new Trade(DateTime.Now, direction, TradeStatus.PendingOpen, tradeType, ticker, quantity, price,
|
||||
leverage, "", "");
|
||||
|
||||
trade.SetQuantity(quantity, 6);
|
||||
|
||||
Trade ftxOrder;
|
||||
if (tradeType == TradeType.StopLoss || tradeType == TradeType.TakeProfitLimit || tradeType == TradeType.StopMarket)
|
||||
if (tradeType == TradeType.StopLoss || tradeType == TradeType.TakeProfitLimit ||
|
||||
tradeType == TradeType.StopMarket)
|
||||
{
|
||||
var ftxTriggerOrderType = FtxHelpers.FtxTriggerOrderTypeMap(tradeType);
|
||||
var ftxResult = await _ftxClient.TradeApi.Trading.PlaceTriggerOrderAsync(FtxHelpers.ToFtxTicker(ticker),
|
||||
direction != TradeDirection.Long ? FTX.Net.Enums.OrderSide.Sell : FTX.Net.Enums.OrderSide.Buy,
|
||||
ftxTriggerOrderType,
|
||||
triggerPrice: price,
|
||||
reduceOnly: true,
|
||||
retryUntilFilled: false,
|
||||
quantity: quantity);
|
||||
direction != TradeDirection.Long ? FTX.Net.Enums.OrderSide.Sell : FTX.Net.Enums.OrderSide.Buy,
|
||||
ftxTriggerOrderType,
|
||||
triggerPrice: price,
|
||||
reduceOnly: true,
|
||||
retryUntilFilled: false,
|
||||
quantity: quantity);
|
||||
_logger.LogInformation("Exchange result : {0}", JsonConvert.SerializeObject(ftxResult));
|
||||
ftxOrder = FtxHelpers.Map(ftxResult, leverage);
|
||||
}
|
||||
@@ -177,7 +188,8 @@ public class FtxProcessor : BaseProcessor
|
||||
public override Orderbook GetOrderbook(Account account, Ticker ticker)
|
||||
{
|
||||
LoadClient(account);
|
||||
var ftxOrderBook = _ftxClient.TradeApi.ExchangeData.GetOrderBookAsync(FtxHelpers.ToFtxTicker(ticker), 100).Result;
|
||||
var ftxOrderBook = _ftxClient.TradeApi.ExchangeData.GetOrderBookAsync(FtxHelpers.ToFtxTicker(ticker), 100)
|
||||
.Result;
|
||||
return FtxHelpers.Map(ftxOrderBook);
|
||||
}
|
||||
|
||||
@@ -200,4 +212,4 @@ public class FtxProcessor : BaseProcessor
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public class KrakenProcessor : BaseProcessor
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override Task<bool> CancelOrder(Account account, Ticker ticker)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@@ -48,7 +49,8 @@ public class KrakenProcessor : BaseProcessor
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval)
|
||||
public override Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate,
|
||||
Timeframe interval)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -72,7 +74,7 @@ public class KrakenProcessor : BaseProcessor
|
||||
{
|
||||
LoadClient(account);
|
||||
var krakenKline = _krakenClient.SpotApi.ExchangeData.GetKlinesAsync(ticker.ToString(),
|
||||
Kraken.Net.Enums.KlineInterval.OneMinute, date).Result.Data.Data.ToList()[0];
|
||||
Kraken.Net.Enums.KlineInterval.OneMinute, date).Result.Data.Data.ToList()[0];
|
||||
return (krakenKline.HighPrice + krakenKline.ClosePrice) / 2;
|
||||
}
|
||||
|
||||
@@ -120,10 +122,15 @@ public class KrakenProcessor : BaseProcessor
|
||||
_krakenClient = new KrakenRestClient((options) => { options.ApiCredentials = krakenConfig.ApiCredentials; });
|
||||
}
|
||||
|
||||
public override async Task<Trade> OpenTrade(Account account, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage = null, TradeType tradeType = TradeType.Limit, bool reduceOnly = false, bool isForPaperTrading = false, DateTime? currentDate = null, bool ioc = true)
|
||||
public override async Task<Trade> OpenTrade(Account account, Ticker ticker, TradeDirection direction, decimal price,
|
||||
decimal quantity, decimal? leverage = null, TradeType tradeType = TradeType.Limit, bool reduceOnly = false,
|
||||
bool isForPaperTrading = false, DateTime? currentDate = null, bool ioc = true,
|
||||
decimal? stopLossPrice = null,
|
||||
decimal? takeProfitPrice = null)
|
||||
{
|
||||
LoadClient(account);
|
||||
var trade = new Trade(DateTime.Now, direction, TradeStatus.PendingOpen, tradeType, ticker, quantity, price, leverage, "", "");
|
||||
var trade = new Trade(DateTime.Now, direction, TradeStatus.PendingOpen, tradeType, ticker, quantity, price,
|
||||
leverage, "", "");
|
||||
trade.SetQuantity(quantity, 6);
|
||||
trade.SetPrice(price, 1);
|
||||
|
||||
@@ -141,4 +148,4 @@ public class KrakenProcessor : BaseProcessor
|
||||
trade.SetMessage(krakenOrderExecuted.Message);
|
||||
return trade;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using Managing.Application.Trading.Commands;
|
||||
using Managing.Application.Workers.Abstractions;
|
||||
using Managing.Common;
|
||||
using Managing.Core;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Statistics;
|
||||
using Managing.Domain.Trades;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
@@ -262,9 +263,12 @@ namespace Managing.Infrastructure.Messengers.Discord
|
||||
var accountService = (IAccountService)_services.GetService(typeof(IAccountService));
|
||||
var tradingService = (ITradingService)_services.GetService(typeof(ITradingService));
|
||||
|
||||
// Create default user for Discord bot operations
|
||||
var defaultUser = new Domain.Users.User { Name = "DiscordBot" };
|
||||
|
||||
var tradeCommand = new OpenPositionRequest(
|
||||
accountName,
|
||||
await moneyManagementService.GetMoneyMangement(moneyManagement),
|
||||
await moneyManagementService.GetMoneyMangement(defaultUser, moneyManagement),
|
||||
direction,
|
||||
ticker,
|
||||
initiator,
|
||||
@@ -403,8 +407,21 @@ namespace Managing.Infrastructure.Messengers.Discord
|
||||
var builder = new ComponentBuilder();
|
||||
|
||||
var moneyManagementService = (IMoneyManagementService)_services.GetService(typeof(IMoneyManagementService));
|
||||
var moneyManagements = moneyManagementService.GetMoneyMangements();
|
||||
// var moneyManagements = moneyManagementService.GetMoneyMangements();
|
||||
|
||||
// TODO Update this to get the money management from the account
|
||||
var moneyManagements = new List<MoneyManagement>
|
||||
{
|
||||
new MoneyManagement
|
||||
{
|
||||
Name = "MediumRisk",
|
||||
BalanceAtRisk = 0.05m,
|
||||
Leverage = 1,
|
||||
StopLoss = 0.021m,
|
||||
TakeProfit = 0.042m,
|
||||
Timeframe = Timeframe.FifteenMinutes
|
||||
}
|
||||
};
|
||||
foreach (var mm in moneyManagements)
|
||||
{
|
||||
var data = new CopyTradeData
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Managing.Infrastructure.MongoDb.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public class BsonCollectionAttribute : Attribute
|
||||
{
|
||||
public string CollectionName { get; }
|
||||
|
||||
public BsonCollectionAttribute(string collectionName)
|
||||
{
|
||||
CollectionName = collectionName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("Accounts")]
|
||||
public class AccountDto : Document
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Exchanges Exchanges { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Secret { get; set; }
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Backtests")]
|
||||
public class BacktestDto : Document
|
||||
{
|
||||
public decimal FinalPnl { get; set; }
|
||||
public int WinRate { get; set; }
|
||||
public decimal GrowthPercentage { get; set; }
|
||||
public decimal HodlPercentage { get; set; }
|
||||
public string Ticker { get; set; }
|
||||
public string Scenario { get; set; }
|
||||
public List<PositionDto> Positions { get; set; }
|
||||
public List<SignalDto> Signals { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public RiskLevel RiskLevel { get; set; }
|
||||
public string AccountName { get; set; }
|
||||
public List<CandleDto> Candles { get; set; }
|
||||
public BotType BotType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Candles")]
|
||||
public class CandleDto : Document
|
||||
{
|
||||
public Exchanges Exchange { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public string Ticker { get; set; }
|
||||
[BsonDateTimeOptions]
|
||||
public DateTime OpenTime { get; set; }
|
||||
[BsonDateTimeOptions]
|
||||
public DateTime CloseTime { get; set; }
|
||||
public decimal Open { get; set; }
|
||||
public decimal Close { get; set; }
|
||||
public decimal High { get; set; }
|
||||
public decimal Low { get; set; }
|
||||
public decimal BaseVolume { get; set; }
|
||||
public decimal QuoteVolume { get; set; }
|
||||
public int TradeCount { get; set; }
|
||||
public decimal TakerBuyBaseVolume { get; set; }
|
||||
public decimal TakerBuyQuoteVolume { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("MoneyManagement")]
|
||||
public class MoneyManagementDto : Document
|
||||
{
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public RiskLevel RiskLevel { get; set; }
|
||||
public decimal BalanceAtRisk { get; set; }
|
||||
public decimal StopLoss { get; set; }
|
||||
public decimal TakeProfit { get; set; }
|
||||
public decimal QuantityTakeProfit { get; set; }
|
||||
public decimal Leverage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Positions")]
|
||||
public class PositionDto : Document
|
||||
{
|
||||
[BsonDateTimeOptions]
|
||||
public DateTime Date { get; set; }
|
||||
public TradeDto Open { get; set; }
|
||||
public TradeDto StopLoss { get; set; }
|
||||
public TradeDto TakeProfit1 { get; set; }
|
||||
public TradeDto TakeProfit2 { get; set; }
|
||||
public decimal ProfitAndLoss { get; set; }
|
||||
public TradeDirection OriginDirection { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public TradeStatus Status { get; set; }
|
||||
public string SignalIdentifier { get; set; }
|
||||
public string AccountName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Scenarios")]
|
||||
public class ScenarioDto : Document
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public List<StrategyDto> Strategies { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Signals")]
|
||||
public class SignalDto : Document
|
||||
{
|
||||
public TradeDirection Direction { get; set; }
|
||||
public Confidence Confidence { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public CandleDto Candle { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public string Ticker { get; set; }
|
||||
public SignalStatus Status { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public StrategyType Type { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Strategies")]
|
||||
public class StrategyDto : Document
|
||||
{
|
||||
public StrategyType Type { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int? Period { get; set; }
|
||||
public int? FastPeriods { get; set; }
|
||||
public int? SlowPeriods { get; set; }
|
||||
public int? SignalPeriods { get; set; }
|
||||
public double? Multiplier { get; set; }
|
||||
public int? StochPeriods { get; set; }
|
||||
public int? SmoothPeriods { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("TopVolumeTickers")]
|
||||
public class TopVolumeTickerDto : Document
|
||||
{
|
||||
public Ticker Ticker { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public decimal Volume { get; set; }
|
||||
public int Rank { get; set; }
|
||||
public Exchanges Exchange { get; set; }
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Trades")]
|
||||
public class TradeDto : Document
|
||||
{
|
||||
[BsonDateTimeOptions]
|
||||
public DateTime Date { get; set; }
|
||||
public TradeDirection Direction { get; set; }
|
||||
public TradeStatus Status { get; set; }
|
||||
public TradeType TradeType { get; set; }
|
||||
public string Ticker { get; set; }
|
||||
public decimal Fee { get; set; }
|
||||
public decimal Quantity { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public decimal Leverage { get; set; }
|
||||
public string ExchangeOrderId { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("Workers")]
|
||||
public class WorkerDto : Document
|
||||
{
|
||||
public WorkerType WorkerType { get; set; }
|
||||
public DateTime StartTime { get; set; }
|
||||
public DateTime? LastRunTime { get; set; }
|
||||
public int ExecutionCount { get; set; }
|
||||
public TimeSpan Delay { get; set; }
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Configurations
|
||||
{
|
||||
public abstract class Document : IDocument
|
||||
{
|
||||
[BsonId]
|
||||
public ObjectId Id { get; set; }
|
||||
[BsonDateTimeOptions]
|
||||
public DateTime CreatedAt => Id.CreationTime;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb.Configurations
|
||||
{
|
||||
public interface IDocument
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.String)]
|
||||
ObjectId Id { get; set; }
|
||||
|
||||
DateTime CreatedAt { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Managing.Infrastructure.MongoDb
|
||||
{
|
||||
public interface IManagingDatabaseSettings
|
||||
{
|
||||
string ConnectionString { get; set; }
|
||||
string DatabaseName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Managing.Infrastructure.MongoDb;
|
||||
|
||||
public class ManagingDatabaseSettings : IManagingDatabaseSettings
|
||||
{
|
||||
public string ConnectionString { get; set; }
|
||||
public string DatabaseName { get; set; }
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb
|
||||
{
|
||||
public interface IMongoRepository<TDocument> where TDocument : IDocument
|
||||
{
|
||||
IQueryable<TDocument> AsQueryable();
|
||||
|
||||
IEnumerable<TDocument> FilterBy(
|
||||
Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
IEnumerable<TDocument> FindAll();
|
||||
|
||||
IEnumerable<TProjected> FilterBy<TProjected>(
|
||||
Expression<Func<TDocument, bool>> filterExpression,
|
||||
Expression<Func<TDocument, TProjected>> projectionExpression);
|
||||
|
||||
TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
TDocument FindById(string id);
|
||||
|
||||
Task<TDocument> FindByIdAsync(string id);
|
||||
|
||||
void InsertOne(TDocument document);
|
||||
|
||||
Task InsertOneAsync(TDocument document);
|
||||
|
||||
void InsertMany(ICollection<TDocument> documents);
|
||||
|
||||
Task InsertManyAsync(ICollection<TDocument> documents);
|
||||
|
||||
void ReplaceOne(TDocument document);
|
||||
|
||||
Task ReplaceOneAsync(TDocument document);
|
||||
|
||||
void DeleteOne(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
Task DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
void DeleteById(string id);
|
||||
|
||||
Task DeleteByIdAsync(string id);
|
||||
|
||||
void DeleteMany(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
void Update(TDocument entity);
|
||||
void CreateIndex(string column);
|
||||
void DropCollection();
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.25.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Managing.Common\Managing.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,20 +0,0 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb
|
||||
{
|
||||
public static class MongoHelpers
|
||||
{
|
||||
public static async Task EnsureIndexExists(this IMongoDatabase database, string collectionName, string indexName)
|
||||
{
|
||||
var collection = database.GetCollection<BsonDocument>(collectionName);
|
||||
var index = new BsonDocument
|
||||
{
|
||||
{indexName, 1}
|
||||
};
|
||||
|
||||
var indexModel = new CreateIndexModel<BsonDocument>(index, new CreateIndexOptions { Unique = true });
|
||||
await collection.Indexes.CreateOneAsync(indexModel).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
using Managing.Infrastructure.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.MongoDb.Configurations;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Managing.Infrastructure.MongoDb
|
||||
{
|
||||
public class MongoRepository<TDocument> : IMongoRepository<TDocument>
|
||||
where TDocument : IDocument
|
||||
{
|
||||
private readonly IMongoCollection<TDocument> _collection;
|
||||
private readonly IMongoDatabase _database;
|
||||
|
||||
public MongoRepository(IManagingDatabaseSettings settings)
|
||||
{
|
||||
_database = new MongoClient(settings.ConnectionString).GetDatabase(settings.DatabaseName);
|
||||
_collection = _database.GetCollection<TDocument>(GetCollectionName(typeof(TDocument)));
|
||||
}
|
||||
|
||||
private protected string GetCollectionName(Type documentType)
|
||||
{
|
||||
return ((BsonCollectionAttribute)documentType.GetCustomAttributes(
|
||||
typeof(BsonCollectionAttribute),
|
||||
true)
|
||||
.FirstOrDefault())?.CollectionName;
|
||||
}
|
||||
|
||||
public virtual IQueryable<TDocument> AsQueryable()
|
||||
{
|
||||
return _collection.AsQueryable();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TDocument> FilterBy(
|
||||
Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
return _collection.Find(filterExpression).ToEnumerable();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TDocument> FindAll()
|
||||
{
|
||||
return _collection.Find(_ => true).ToEnumerable();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TProjected> FilterBy<TProjected>(
|
||||
Expression<Func<TDocument, bool>> filterExpression,
|
||||
Expression<Func<TDocument, TProjected>> projectionExpression)
|
||||
{
|
||||
return _collection.Find(filterExpression).Project(projectionExpression).ToEnumerable();
|
||||
}
|
||||
|
||||
public virtual TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
return _collection.Find(filterExpression).FirstOrDefault();
|
||||
}
|
||||
|
||||
public virtual Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync());
|
||||
}
|
||||
|
||||
public virtual TDocument FindById(string id)
|
||||
{
|
||||
var objectId = new ObjectId(id);
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
|
||||
return _collection.Find(filter).SingleOrDefault();
|
||||
}
|
||||
|
||||
public virtual Task<TDocument> FindByIdAsync(string id)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var objectId = new ObjectId(id);
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
|
||||
return _collection.Find(filter).SingleOrDefaultAsync();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public virtual void InsertOne(TDocument document)
|
||||
{
|
||||
_collection.InsertOne(document);
|
||||
}
|
||||
|
||||
public virtual Task InsertOneAsync(TDocument document)
|
||||
{
|
||||
return Task.Run(() => _collection.InsertOneAsync(document));
|
||||
}
|
||||
|
||||
public void InsertMany(ICollection<TDocument> documents)
|
||||
{
|
||||
_collection.InsertMany(documents);
|
||||
}
|
||||
|
||||
public void DropCollection()
|
||||
{
|
||||
_database.DropCollection(GetCollectionName(typeof(TDocument)));
|
||||
}
|
||||
|
||||
public virtual async Task InsertManyAsync(ICollection<TDocument> documents)
|
||||
{
|
||||
await _collection.InsertManyAsync(documents);
|
||||
}
|
||||
|
||||
public void ReplaceOne(TDocument document)
|
||||
{
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, document.Id);
|
||||
_collection.FindOneAndReplace(filter, document);
|
||||
}
|
||||
|
||||
public virtual async Task ReplaceOneAsync(TDocument document)
|
||||
{
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, document.Id);
|
||||
await _collection.FindOneAndReplaceAsync(filter, document);
|
||||
}
|
||||
|
||||
public void Update(TDocument entity)
|
||||
{
|
||||
if (entity.Id == ObjectId.Empty)
|
||||
{
|
||||
entity.Id = ObjectId.GenerateNewId();
|
||||
}
|
||||
|
||||
var option = new ReplaceOptions { IsUpsert = true };
|
||||
_collection.ReplaceOne(x => entity != null && x.Id == entity.Id, entity, option);
|
||||
}
|
||||
|
||||
public void DeleteOne(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
_collection.FindOneAndDelete(filterExpression);
|
||||
}
|
||||
|
||||
public Task DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
return Task.Run(() => _collection.FindOneAndDeleteAsync(filterExpression));
|
||||
}
|
||||
|
||||
public void DeleteById(string id)
|
||||
{
|
||||
var objectId = new ObjectId(id);
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
|
||||
_collection.FindOneAndDelete(filter);
|
||||
}
|
||||
|
||||
public Task DeleteByIdAsync(string id)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var objectId = new ObjectId(id);
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
|
||||
_collection.FindOneAndDeleteAsync(filter);
|
||||
});
|
||||
}
|
||||
|
||||
public void DeleteMany(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
_collection.DeleteMany(filterExpression);
|
||||
}
|
||||
|
||||
public Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
return _collection.DeleteManyAsync(filterExpression);
|
||||
}
|
||||
|
||||
public virtual void CreateIndex(string column)
|
||||
{
|
||||
var keys = Builders<TDocument>.IndexKeys.Ascending(column);
|
||||
var indexOptions = new CreateIndexOptions { Unique = true };
|
||||
var model = new CreateIndexModel<TDocument>(keys, indexOptions);
|
||||
_collection.Indexes.CreateOne(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user