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:
Oda
2025-04-20 22:18:27 +07:00
committed by GitHub
parent 0ae96a3278
commit 528c62a0a1
400 changed files with 94446 additions and 1635 deletions

BIN
src/.DS_Store vendored

Binary file not shown.

View File

@@ -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/"]

View File

@@ -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/"]

View File

@@ -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/"]

View File

@@ -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;
}

View File

@@ -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
});
}

View File

@@ -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));

View File

@@ -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));
}
}

View File

@@ -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,

View File

@@ -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}");
}
}
}

View File

@@ -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(

View File

@@ -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/"]

View File

@@ -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; }
}
}
}

View File

@@ -12,6 +12,9 @@
"AppId": "cm6f47n1l003jx7mjwaembhup",
"AppSecret": "63Chz2z5M8TgR5qc8dznSLRAGTHTyPU4cjdQobrBF1Cx5tszZpTuFgyrRd7hZ2k6HpwDz3GEwQZzsCqHb8Z311bF"
},
"Web3Proxy": {
"BaseUrl": "http://srv-captain--web3-proxy:4111"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",

View File

@@ -21,6 +21,9 @@
"Organization": "",
"Token": ""
},
"Web3Proxy": {
"BaseUrl": "http://localhost:3000"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -6,4 +6,5 @@ public interface IUserRepository
{
Task<User> GetUserByNameAsync(string name);
Task InsertUserAsync(User user);
}
Task UpdateUser(User user);
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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>()))

View File

@@ -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(),
};

View File

@@ -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);
}
}
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -4,4 +4,5 @@ public interface ISettingsService
{
bool SetupSettings();
Task<bool> ResetSettings();
Task<bool> CreateDefaultConfiguration(Domain.Users.User user);
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -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");
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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:

View File

@@ -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)

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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>();

View File

@@ -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;
}

View File

@@ -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; }

View File

@@ -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()

View File

@@ -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()
{

View File

@@ -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)
{

View File

@@ -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())

View File

@@ -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;

View File

@@ -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()
{

View File

@@ -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()
{

View File

@@ -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());
}
}
}

View File

@@ -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,

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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
};
}

View File

@@ -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());
}
}
}

View File

@@ -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)

View File

@@ -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");
}
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}

View File

@@ -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;
}
}

View File

@@ -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; }
}
}

View File

@@ -1,8 +0,0 @@
namespace Managing.Infrastructure.MongoDb
{
public interface IManagingDatabaseSettings
{
string ConnectionString { get; set; }
string DatabaseName { get; set; }
}
}

View File

@@ -1,7 +0,0 @@
namespace Managing.Infrastructure.MongoDb;
public class ManagingDatabaseSettings : IManagingDatabaseSettings
{
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

Some files were not shown because too many files have changed in this diff Show More