Postgres (#30)

* Add postgres

* Migrate users

* Migrate geneticRequest

* Try to fix Concurrent call

* Fix asyncawait

* Fix async and concurrent

* Migrate backtests

* Add cache for user by address

* Fix backtest migration

* Fix not open connection

* Fix backtest command error

* Fix concurrent

* Fix all concurrency

* Migrate TradingRepo

* Fix scenarios

* Migrate statistic repo

* Save botbackup

* Add settings et moneymanagement

* Add bot postgres

* fix a bit more backups

* Fix bot model

* Fix loading backup

* Remove cache market for read positions

* Add workers to postgre

* Fix workers api

* Reduce get Accounts for workers

* Migrate synth to postgre

* Fix backtest saved

* Remove mongodb

* botservice decorrelation

* Fix tradingbot scope call

* fix tradingbot

* fix concurrent

* Fix scope for genetics

* Fix account over requesting

* Fix bundle backtest worker

* fix a lot of things

* fix tab backtest

* Remove optimized moneymanagement

* Add light signal to not use User and too much property

* Make money management lighter

* insert indicators to awaitable

* Migrate add strategies to await

* Refactor scenario and indicator retrieval to use asynchronous methods throughout the application

* add more async await

* Add services

* Fix and clean

* Fix bot a bit

* Fix bot and add message for cooldown

* Remove fees

* Add script to deploy db

* Update dfeeploy script

* fix script

* Add idempotent script and backup

* finish script migration

* Fix did user and agent name on start bot
This commit is contained in:
Oda
2025-07-27 15:42:17 +02:00
committed by GitHub
parent 361bfbf6e8
commit 422fecea7b
294 changed files with 23953 additions and 7272 deletions

View File

@@ -23,7 +23,7 @@ public class ClosePositionCommandHandler(
var account = await accountService.GetAccount(request.Position.AccountName, false, false);
if (request.Position == null)
{
_ = exchangeService.CancelOrder(account, request.Position.Ticker).Result;
_ = await exchangeService.CancelOrder(account, request.Position.Ticker);
return request.Position;
}
@@ -31,7 +31,7 @@ public class ClosePositionCommandHandler(
var lastPrice = request.Position.Initiator == PositionInitiator.PaperTrading
? request.ExecutionPrice.GetValueOrDefault()
: exchangeService.GetPrice(account, request.Position.Ticker, DateTime.UtcNow);
: await exchangeService.GetPrice(account, request.Position.Ticker, DateTime.UtcNow);
// Check if position still open
if (!request.IsForBacktest)
@@ -46,7 +46,7 @@ public class ClosePositionCommandHandler(
request.Position.ProfitAndLoss =
TradingBox.GetProfitAndLoss(request.Position, request.Position.Open.Quantity, lastPrice,
request.Position.Open.Leverage);
tradingService.UpdatePosition(request.Position);
await tradingService.UpdatePositionAsync(request.Position);
return request.Position;
}
}
@@ -67,7 +67,7 @@ public class ClosePositionCommandHandler(
request.Position.Open.Leverage);
if (!request.IsForBacktest)
tradingService.UpdatePosition(request.Position);
await tradingService.UpdatePositionAsync(request.Position);
}
return request.Position;

View File

@@ -1,16 +0,0 @@
using Managing.Common;
using Managing.Domain.Trades;
using MediatR;
namespace Managing.Application.Trading.Commands
{
public class GetPositionsCommand : IRequest<List<Position>>
{
public GetPositionsCommand(Enums.PositionInitiator initiator)
{
Initiator = initiator;
}
public Enums.PositionInitiator Initiator { get; internal set; }
}
}

View File

@@ -1,5 +1,4 @@
using Managing.Domain.MoneyManagements;
using Managing.Domain.Trades;
using Managing.Domain.Trades;
using Managing.Domain.Users;
using MediatR;
using static Managing.Common.Enums;
@@ -10,7 +9,7 @@ namespace Managing.Application.Trading.Commands
{
public OpenPositionRequest(
string accountName,
MoneyManagement moneyManagement,
LightMoneyManagement moneyManagement,
TradeDirection direction,
Ticker ticker,
PositionInitiator initiator,
@@ -43,7 +42,7 @@ namespace Managing.Application.Trading.Commands
public string SignalIdentifier { get; set; }
public string AccountName { get; }
public MoneyManagement MoneyManagement { get; }
public LightMoneyManagement MoneyManagement { get; }
public TradeDirection Direction { get; }
public Ticker Ticker { get; }
public bool IsForPaperTrading { get; }

View File

@@ -1,23 +0,0 @@
using Managing.Application.Abstractions.Services;
using Managing.Application.Trading.Commands;
using Managing.Domain.Trades;
using MediatR;
namespace Managing.Application.Trading
{
public class GetPositionsCommandHandler : IRequestHandler<GetPositionsCommand, List<Position>>
{
private readonly ITradingService _tradingService;
public GetPositionsCommandHandler(ITradingService tradingService)
{
_tradingService = tradingService;
}
public Task<List<Position>> Handle(GetPositionsCommand request, CancellationToken cancellationToken)
{
var positions = _tradingService.GetPositions(request.Initiator);
return Task.FromResult(positions.ToList());
}
}
}

View File

@@ -15,9 +15,9 @@ public class GetTradeCommandHandler : IRequestHandler<GetTradeCommand, Trade>
_accountService = accountService;
}
public Task<Trade> Handle(GetTradeCommand request, CancellationToken cancellationToken)
public async Task<Trade> Handle(GetTradeCommand request, CancellationToken cancellationToken)
{
var account = _accountService.GetAccount(request.AccountName, true, false).Result;
return _exchangeService.GetTrade(account, request.ExchangeOrderId, request.Ticker);
var account = await _accountService.GetAccount(request.AccountName, true, false);
return await _exchangeService.GetTrade(account, request.ExchangeOrderId, request.Ticker);
}
}

View File

@@ -16,10 +16,10 @@ namespace Managing.Application.Trading
_accountService = accountService;
}
public Task<List<Trade>> Handle(GetTradesCommand request, CancellationToken cancellationToken)
public async Task<List<Trade>> Handle(GetTradesCommand request, CancellationToken cancellationToken)
{
var account = _accountService.GetAccount(request.AccountName, true, false).Result;
return _exchangeService.GetTrades(account, request.Ticker);
var account = await _accountService.GetAccount(request.AccountName, true, false);
return await _exchangeService.GetTrades(account, request.Ticker);
}
}
}

View File

@@ -49,12 +49,12 @@ namespace Managing.Application.Trading
var price = request.IsForPaperTrading && request.Price.HasValue
? request.Price.Value
: exchangeService.GetPrice(account, request.Ticker, DateTime.Now);
: await exchangeService.GetPrice(account, request.Ticker, DateTime.Now);
var quantity = balanceToRisk / price;
var openPrice = request.IsForPaperTrading || request.Price.HasValue
? request.Price.Value
: exchangeService.GetBestPrice(account, request.Ticker, price, quantity, request.Direction);
: await exchangeService.GetBestPrice(account, request.Ticker, price, quantity, request.Direction);
// Determine SL/TP Prices
var stopLossPrice = RiskHelpers.GetStopLossPrice(request.Direction, openPrice, request.MoneyManagement);
@@ -107,22 +107,12 @@ namespace Managing.Application.Trading
if (!request.IsForPaperTrading)
{
tradingService.InsertPosition(position);
await tradingService.InsertPositionAsync(position);
}
return position;
}
private static TradeStatus GetExpectedStatus(OpenPositionRequest request)
{
if (request.IsForPaperTrading)
{
return TradeStatus.Filled;
}
return TradeStatus.Requested;
}
private static bool IsOpenTradeHandled(TradeStatus tradeStatus, TradingExchanges exchange)
{
return tradeStatus == TradeStatus.Filled

View File

@@ -51,85 +51,70 @@ public class TradingService : ITradingService
_synthPredictionService = synthPredictionService;
}
public void DeleteScenario(string name)
public async Task DeleteScenarioAsync(string name)
{
_tradingRepository.DeleteScenario(name);
await _tradingRepository.DeleteScenarioAsync(name);
}
public void DeleteScenarios()
public async Task DeleteStrategyAsync(string name)
{
_tradingRepository.DeleteScenarios();
await _tradingRepository.DeleteIndicatorAsync(name);
}
public void DeleteStrategies()
public async Task<Position> GetPositionByIdentifierAsync(string identifier)
{
_tradingRepository.DeleteIndicators();
return await _tradingRepository.GetPositionByIdentifierAsync(identifier);
}
public void DeleteStrategy(string name)
public async Task<IEnumerable<Position>> GetPositionsAsync(PositionInitiator positionInitiator)
{
_tradingRepository.DeleteIndicator(name);
return await _tradingRepository.GetPositionsAsync(positionInitiator);
}
public Position GetPositionByIdentifier(string identifier)
public async Task<IEnumerable<Position>> GetPositionsByStatusAsync(PositionStatus postionStatus)
{
return _tradingRepository.GetPositionByIdentifier(identifier);
}
public IEnumerable<Position> GetPositions(PositionInitiator positionInitiator)
{
return _tradingRepository.GetPositions(positionInitiator);
}
public IEnumerable<Position> GetPositionsByStatus(PositionStatus postionStatus)
{
return _tradingRepository.GetPositionsByStatus(postionStatus);
return await _tradingRepository.GetPositionsByStatusAsync(postionStatus);
}
public Scenario GetScenarioByName(string scenario)
public async Task<Scenario> GetScenarioByNameAsync(string scenario)
{
return _tradingRepository.GetScenarioByName(scenario);
return await _tradingRepository.GetScenarioByNameAsync(scenario);
}
public IEnumerable<Scenario> GetScenarios()
public async Task<IEnumerable<Scenario>> GetScenariosAsync()
{
return _tradingRepository.GetScenarios();
return await _tradingRepository.GetScenariosAsync();
}
public IEnumerable<Indicator> GetStrategies()
public async Task<IEnumerable<Indicator>> GetStrategiesAsync()
{
return _tradingRepository.GetIndicators();
return await _tradingRepository.GetStrategiesAsync();
}
public Indicator GetStrategyByName(string strategy)
public async Task<Indicator> GetStrategyByNameAsync(string strategy)
{
return _tradingRepository.GetStrategyByName(strategy);
return await _tradingRepository.GetStrategyByNameAsync(strategy);
}
public void InsertPosition(Position position)
public async Task InsertPositionAsync(Position position)
{
_tradingRepository.InsertPosition(position);
await _tradingRepository.InsertPositionAsync(position);
}
public void InsertScenario(Scenario scenario)
public async Task InsertScenarioAsync(Scenario scenario)
{
_tradingRepository.InsertScenario(scenario);
await _tradingRepository.InsertScenarioAsync(scenario);
}
public void InsertSignal(Signal signal)
public async Task InsertStrategyAsync(Indicator indicator)
{
_tradingRepository.InsertSignal(signal);
}
public void InsertStrategy(Indicator indicator)
{
_tradingRepository.InsertStrategy(indicator);
await _tradingRepository.InsertStrategyAsync(indicator);
}
public async Task<Position> ManagePosition(Account account, Position position)
{
var lastPrice = _exchangeService.GetPrice(account, position.Ticker, DateTime.UtcNow);
var lastPrice = await _exchangeService.GetPrice(account, position.Ticker, DateTime.UtcNow);
var quantityInPosition = await _exchangeService.GetQuantityInPosition(account, position.Ticker);
var orders = await _exchangeService.GetOpenOrders(account, position.Ticker);
@@ -184,54 +169,19 @@ public class TradingService : ITradingService
return position;
}
public void UpdateFee(TradingExchanges exchange)
{
var lastFee = _tradingRepository.GetFee(exchange);
var account = _accountService.GetAccounts(false, false).FirstOrDefault(a => a.Exchange == exchange);
if (lastFee != null)
{
if (DateTime.UtcNow.AddHours(-6) >= lastFee.LastUpdate)
{
lastFee.Cost = _exchangeService.GetFee(account);
lastFee.LastUpdate = DateTime.UtcNow;
_tradingRepository.UpdateFee(lastFee);
}
}
else
{
lastFee = new Fee
{
Cost = _exchangeService.GetFee(account),
Exchange = exchange,
LastUpdate = DateTime.UtcNow
};
_tradingRepository.InsertFee(lastFee);
}
public async Task UpdatePositionAsync(Position position)
{
await _tradingRepository.UpdatePositionAsync(position);
}
public decimal GetFee(Account account, bool isForPaperTrading = false)
{
if (isForPaperTrading && account.Exchange != TradingExchanges.Evm)
{
return 0.000665M;
}
return _cacheService.GetOrSave($"Fee-{account.Exchange}",
() => { return (decimal)_tradingRepository.GetFee(TradingExchanges.Evm)?.Cost; }, TimeSpan.FromHours(2));
}
public void UpdatePosition(Position position)
{
_tradingRepository.UpdatePosition(position);
}
public IEnumerable<Position> GetPositions()
public async Task<IEnumerable<Position>> GetPositionsAsync()
{
var positions = new List<Position>();
positions.AddRange(GetPositionsByStatus(PositionStatus.New));
positions.AddRange(GetPositionsByStatus(PositionStatus.Filled));
positions.AddRange(GetPositionsByStatus(PositionStatus.PartiallyFilled));
positions.AddRange(await GetPositionsByStatusAsync(PositionStatus.New));
positions.AddRange(await GetPositionsByStatusAsync(PositionStatus.Filled));
positions.AddRange(await GetPositionsByStatusAsync(PositionStatus.PartiallyFilled));
return positions;
}
@@ -239,7 +189,7 @@ public class TradingService : ITradingService
public async Task WatchTrader()
{
var availableTickers = new List<Ticker> { Ticker.BTC, Ticker.ETH, Ticker.UNI, Ticker.LINK };
var watchAccount = GetTradersWatch();
var watchAccount = await GetTradersWatch();
var key = $"AccountsQuantityInPosition";
var aqip = _cacheService.GetValue<List<TraderFollowup>>(key);
@@ -264,10 +214,11 @@ public class TradingService : ITradingService
_cacheService.SaveValue(key, aqip, TimeSpan.FromMinutes(10));
}
public IEnumerable<Trader> GetTradersWatch()
public async Task<IEnumerable<Trader>> GetTradersWatch()
{
var watchAccount = _statisticRepository.GetBestTraders();
var customWatchAccount = _accountService.GetAccounts(true, false).Where(a => a.Type == AccountType.Watch)
var watchAccount = await _statisticRepository.GetBestTradersAsync();
var customWatchAccount = (await _accountService.GetAccountsAsync(true, false))
.Where(a => a.Type == AccountType.Watch)
.ToList().MapToTraders();
watchAccount.AddRange(customWatchAccount.Where(a =>
!watchAccount.Any(w => w.Address.Equals(a.Address, StringComparison.InvariantCultureIgnoreCase))));
@@ -279,14 +230,14 @@ public class TradingService : ITradingService
var fundingRates = _exchangeService.GetFundingRates();
}
public void UpdateScenario(Scenario scenario)
public async Task UpdateScenarioAsync(Scenario scenario)
{
_tradingRepository.UpdateScenario(scenario);
await _tradingRepository.UpdateScenarioAsync(scenario);
}
public void UpdateStrategy(Indicator indicator)
public async Task UpdateStrategyAsync(Indicator indicator)
{
_tradingRepository.UpdateStrategy(indicator);
await _tradingRepository.UpdateStrategyAsync(indicator);
}
public async Task<IEnumerable<Position>> GetBrokerPositions(Account account)
@@ -407,7 +358,7 @@ public class TradingService : ITradingService
}
// Synth API integration methods
public async Task<SignalValidationResult> ValidateSynthSignalAsync(Signal signal, decimal currentPrice,
public async Task<SignalValidationResult> ValidateSynthSignalAsync(LightSignal signal, decimal currentPrice,
TradingBotConfig botConfig, bool isBacktest)
{
return await _synthPredictionService.ValidateSignalAsync(signal, currentPrice, botConfig, isBacktest);
@@ -433,12 +384,12 @@ public class TradingService : ITradingService
/// <param name="scenario">The scenario containing indicators.</param>
/// <param name="candles">The candles to calculate indicators for.</param>
/// <returns>A dictionary of indicator types to their calculated values.</returns>
public async Task<Dictionary<IndicatorType, IndicatorsResultBase>> CalculateIndicatorsValuesAsync(
Scenario scenario,
public Dictionary<IndicatorType, IndicatorsResultBase> CalculateIndicatorsValuesAsync(
Scenario scenario,
List<Candle> candles)
{
var indicatorsValues = new Dictionary<IndicatorType, IndicatorsResultBase>();
if (scenario?.Indicators == null || scenario.Indicators.Count == 0)
{
return indicatorsValues;
@@ -459,13 +410,13 @@ public class TradingService : ITradingService
// Build the indicator using ScenarioHelpers
var builtIndicator = ScenarioHelpers.BuildIndicator(indicator, 10000);
builtIndicator.Candles = fixedCandles;
indicatorsValues[indicator.Type] = builtIndicator.GetIndicatorValues();
}
catch (Exception ex)
{
// Log the error but continue with other indicators
_logger.LogError(ex, "Error calculating indicator {IndicatorName}: {ErrorMessage}",
_logger.LogError(ex, "Error calculating indicator {IndicatorName}: {ErrorMessage}",
indicator.Name, ex.Message);
}
}