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

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