docker files fixes from liaqat
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Trading.Commands;
|
||||
using Managing.Domain.Shared.Helpers;
|
||||
using Managing.Domain.Trades;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Trading;
|
||||
|
||||
public class ClosePositionCommandHandler : ICommandHandler<ClosePositionCommand, Position>
|
||||
{
|
||||
private readonly IExchangeService _exchangeService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly ITradingService _tradingService;
|
||||
|
||||
public ClosePositionCommandHandler(
|
||||
IExchangeService exchangeService,
|
||||
IAccountService accountService,
|
||||
ITradingService tradingService)
|
||||
{
|
||||
_exchangeService = exchangeService;
|
||||
_accountService = accountService;
|
||||
_tradingService = tradingService;
|
||||
}
|
||||
|
||||
public async Task<Position> Handle(ClosePositionCommand request)
|
||||
{
|
||||
// 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 = _exchangeService.ClosePosition(account, request.Position, lastPrice, isForPaperTrading).Result;
|
||||
var closeRequestedOrders = isForPaperTrading ? true : _exchangeService.CancelOrder(account, request.Position.Ticker).Result;
|
||||
|
||||
if (closeRequestedOrders || closedPosition.Status == (TradeStatus.PendingOpen | TradeStatus.Filled))
|
||||
{
|
||||
request.Position.Status = PositionStatus.Finished;
|
||||
request.Position.ProfitAndLoss = TradingBox.GetProfitAndLoss(request.Position, closedPosition.Quantity, lastPrice);
|
||||
_tradingService.UpdatePosition(request.Position);
|
||||
}
|
||||
|
||||
return request.Position;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Managing.Domain.Trades;
|
||||
using MediatR;
|
||||
|
||||
namespace Managing.Application.Trading.Commands
|
||||
{
|
||||
public class ClosePositionCommand : IRequest<Position>
|
||||
{
|
||||
public ClosePositionCommand(Position position, decimal? executionPrice = null)
|
||||
{
|
||||
Position = position;
|
||||
ExecutionPrice = executionPrice;
|
||||
}
|
||||
|
||||
public Position Position { get; }
|
||||
public decimal? ExecutionPrice { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
20
src/Managing.Application/Trading/Commands/GetTradeCommand.cs
Normal file
20
src/Managing.Application/Trading/Commands/GetTradeCommand.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Managing.Domain.Trades;
|
||||
using MediatR;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Trading.Commands
|
||||
{
|
||||
public class GetTradeCommand : IRequest<Trade>
|
||||
{
|
||||
public GetTradeCommand(string accountName, string exchangeOrderId, Ticker ticker)
|
||||
{
|
||||
AccountName = accountName;
|
||||
ExchangeOrderId = exchangeOrderId;
|
||||
Ticker = ticker;
|
||||
}
|
||||
|
||||
public string AccountName { get; }
|
||||
public string ExchangeOrderId { get; }
|
||||
public Ticker Ticker { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Managing.Domain.Trades;
|
||||
using MediatR;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Trading.Commands
|
||||
{
|
||||
public class GetTradesCommand : IRequest<List<Trade>>
|
||||
{
|
||||
public GetTradesCommand(Ticker ticker, string accountName)
|
||||
{
|
||||
Ticker = ticker;
|
||||
AccountName = accountName;
|
||||
}
|
||||
|
||||
public string AccountName { get; }
|
||||
public Ticker Ticker { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Trades;
|
||||
using MediatR;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Trading.Commands
|
||||
{
|
||||
public class OpenPositionRequest : IRequest<Position>
|
||||
{
|
||||
public OpenPositionRequest(
|
||||
string accountName,
|
||||
MoneyManagement moneyManagement,
|
||||
TradeDirection direction,
|
||||
Ticker ticker,
|
||||
PositionInitiator initiator,
|
||||
DateTime date,
|
||||
bool isForPaperTrading = false,
|
||||
decimal? price = null,
|
||||
decimal? balance = 1000,
|
||||
decimal? fee = null,
|
||||
bool? ignoreSLTP = false)
|
||||
{
|
||||
AccountName = accountName;
|
||||
MoneyManagement = moneyManagement;
|
||||
Direction = direction;
|
||||
Ticker = ticker;
|
||||
IsForPaperTrading = isForPaperTrading;
|
||||
Price = price;
|
||||
Date = date;
|
||||
Balance = balance;
|
||||
Initiator = initiator;
|
||||
Fee = fee;
|
||||
IgnoreSLTP = ignoreSLTP;
|
||||
}
|
||||
|
||||
public string AccountName { get; }
|
||||
public MoneyManagement MoneyManagement { get; }
|
||||
public TradeDirection Direction { get; }
|
||||
public Ticker Ticker { get; }
|
||||
public bool IsForPaperTrading { get; }
|
||||
public decimal? Price { get; }
|
||||
public decimal? Fee { get; }
|
||||
public bool? IgnoreSLTP { get; }
|
||||
public decimal? Balance { get; }
|
||||
public DateTime Date { get; set; }
|
||||
public PositionInitiator Initiator { get; internal set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/Managing.Application/Trading/GetTradeCommandHandler.cs
Normal file
23
src/Managing.Application/Trading/GetTradeCommandHandler.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Trading.Commands;
|
||||
using Managing.Domain.Trades;
|
||||
using MediatR;
|
||||
|
||||
namespace Managing.Application.Trading;
|
||||
|
||||
public class GetTradeCommandHandler : IRequestHandler<GetTradeCommand, Trade>
|
||||
{
|
||||
private readonly IExchangeService _exchangeService;
|
||||
private readonly IAccountService _accountService;
|
||||
public GetTradeCommandHandler(IExchangeService exchangeService, IAccountService accountService)
|
||||
{
|
||||
_exchangeService = exchangeService;
|
||||
_accountService = accountService;
|
||||
}
|
||||
|
||||
public Task<Trade> Handle(GetTradeCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var account = _accountService.GetAccount(request.AccountName, true, false).Result;
|
||||
return _exchangeService.GetTrade(account, request.ExchangeOrderId, request.Ticker);
|
||||
}
|
||||
}
|
||||
25
src/Managing.Application/Trading/GetTradesCommandHandler.cs
Normal file
25
src/Managing.Application/Trading/GetTradesCommandHandler.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Trading.Commands;
|
||||
using Managing.Domain.Trades;
|
||||
using MediatR;
|
||||
|
||||
namespace Managing.Application.Trading
|
||||
{
|
||||
public class GetTradesCommandHandler : IRequestHandler<GetTradesCommand, List<Trade>>
|
||||
{
|
||||
private readonly IExchangeService _exchangeService;
|
||||
private readonly IAccountService _accountService;
|
||||
|
||||
public GetTradesCommandHandler(IExchangeService exchangeService, IAccountService accountService)
|
||||
{
|
||||
_exchangeService = exchangeService;
|
||||
_accountService = accountService;
|
||||
}
|
||||
|
||||
public Task<List<Trade>> Handle(GetTradesCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var account = _accountService.GetAccount(request.AccountName, true, false).Result;
|
||||
return _exchangeService.GetTrades(account, request.Ticker);
|
||||
}
|
||||
}
|
||||
}
|
||||
128
src/Managing.Application/Trading/OpenPositionCommandHandler.cs
Normal file
128
src/Managing.Application/Trading/OpenPositionCommandHandler.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Trading.Commands;
|
||||
using Managing.Domain.Shared.Helpers;
|
||||
using Managing.Domain.Trades;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Trading
|
||||
{
|
||||
public class OpenPositionCommandHandler : ICommandHandler<OpenPositionRequest, Position>
|
||||
{
|
||||
private readonly IExchangeService _exchangeService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly ITradingService _tradingService;
|
||||
|
||||
public OpenPositionCommandHandler(
|
||||
IExchangeService exchangeService,
|
||||
IAccountService accountService,
|
||||
ITradingService tradingService)
|
||||
{
|
||||
_exchangeService = exchangeService;
|
||||
_accountService = accountService;
|
||||
_tradingService = tradingService;
|
||||
}
|
||||
|
||||
public Task<Position> Handle(OpenPositionRequest request)
|
||||
{
|
||||
var account = _accountService.GetAccount(request.AccountName, hideSecrets: false, getBalance: false).Result;
|
||||
if (!request.IsForPaperTrading && !_exchangeService.CancelOrder(account, request.Ticker).Result)
|
||||
{
|
||||
throw new Exception($"Not able to close all orders for {request.Ticker}");
|
||||
}
|
||||
|
||||
var initiator = request.IsForPaperTrading ? PositionInitiator.PaperTrading : request.Initiator;
|
||||
var position = new Position(request.AccountName, request.Direction, request.Ticker, request.MoneyManagement, initiator, request.Date);
|
||||
var balance = request.IsForPaperTrading ? request.Balance.GetValueOrDefault() : _exchangeService.GetBalance(account, request.IsForPaperTrading).Result;
|
||||
var balanceAtRisk = RiskHelpers.GetBalanceAtRisk(balance, request.MoneyManagement);
|
||||
|
||||
if (balanceAtRisk < 13)
|
||||
{
|
||||
throw new Exception($"Try to risk {balanceAtRisk} $ but inferior to minimum to trade");
|
||||
}
|
||||
|
||||
var price = request.IsForPaperTrading && request.Price.HasValue ?
|
||||
request.Price.Value :
|
||||
_exchangeService.GetPrice(account, request.Ticker, DateTime.Now);
|
||||
var quantity = balanceAtRisk / price;
|
||||
var fee = request.IsForPaperTrading ? request.Fee.GetValueOrDefault() : _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);
|
||||
|
||||
var trade = _exchangeService.OpenTrade(
|
||||
account,
|
||||
request.Ticker,
|
||||
request.Direction,
|
||||
openPrice,
|
||||
quantity,
|
||||
request.MoneyManagement.Leverage,
|
||||
TradeType.Limit,
|
||||
isForPaperTrading: request.IsForPaperTrading,
|
||||
currentDate: request.Date).Result;
|
||||
|
||||
trade.Fee = TradingHelpers.GetFeeAmount(fee, openPrice * quantity, account.Exchange);
|
||||
return trade;
|
||||
});
|
||||
|
||||
|
||||
if (IsOpenTradeHandled(position.Open.Status, account.Exchange) && !request.IgnoreSLTP.GetValueOrDefault())
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
position.Status = IsOpenTradeHandled(position.Open.Status, account.Exchange) ? position.Status : PositionStatus.Rejected;
|
||||
_tradingService.InsertPosition(position);
|
||||
|
||||
return Task.FromResult(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
|
||||
|| (exchange == TradingExchanges.Evm && tradeStatus == TradeStatus.Requested);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/Managing.Application/Trading/TradingPolicies.cs
Normal file
18
src/Managing.Application/Trading/TradingPolicies.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Managing.Domain.Trades;
|
||||
using Polly;
|
||||
using Polly.Retry;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Trading;
|
||||
|
||||
public static class TradingPolicies
|
||||
{
|
||||
public static RetryPolicy<Trade> OpenPosition(TradeStatus tradeStatus)
|
||||
{
|
||||
var policy = Policy
|
||||
.HandleResult<Trade>(res => res.Status != tradeStatus)
|
||||
.WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(3));
|
||||
|
||||
return policy;
|
||||
}
|
||||
}
|
||||
352
src/Managing.Application/Trading/TradingService.cs
Normal file
352
src/Managing.Application/Trading/TradingService.cs
Normal file
@@ -0,0 +1,352 @@
|
||||
using DnsClient.Internal;
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Statistics;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Domain.Shared.Helpers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Driver;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Trading;
|
||||
|
||||
public class TradingService : ITradingService
|
||||
{
|
||||
private readonly ITradingRepository _tradingRepository;
|
||||
private readonly IExchangeService _exchangeService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly ICacheService _cacheService;
|
||||
private readonly IMessengerService _messengerService;
|
||||
private readonly IStatisticRepository _statisticRepository;
|
||||
private readonly ILogger<TradingService> _logger;
|
||||
|
||||
public TradingService(
|
||||
ITradingRepository tradingRepository,
|
||||
IExchangeService exchangeService,
|
||||
ILogger<TradingService> logger,
|
||||
IAccountService accountService,
|
||||
ICacheService cacheService,
|
||||
IMessengerService messengerService,
|
||||
IStatisticRepository statisticRepository)
|
||||
{
|
||||
_tradingRepository = tradingRepository;
|
||||
_exchangeService = exchangeService;
|
||||
_logger = logger;
|
||||
_accountService = accountService;
|
||||
_cacheService = cacheService;
|
||||
_messengerService = messengerService;
|
||||
_statisticRepository = statisticRepository;
|
||||
}
|
||||
|
||||
public void DeleteScenario(string name)
|
||||
{
|
||||
_tradingRepository.DeleteScenario(name);
|
||||
}
|
||||
|
||||
public void DeleteScenarios()
|
||||
{
|
||||
_tradingRepository.DeleteScenarios();
|
||||
}
|
||||
|
||||
public void DeleteStrategies()
|
||||
{
|
||||
_tradingRepository.DeleteStrategies();
|
||||
}
|
||||
|
||||
public void DeleteStrategy(string name)
|
||||
{
|
||||
_tradingRepository.DeleteStrategy(name);
|
||||
}
|
||||
|
||||
public Position GetPositionByIdentifier(string identifier)
|
||||
{
|
||||
return _tradingRepository.GetPositionByIdentifier(identifier);
|
||||
}
|
||||
|
||||
public IEnumerable<Position> GetPositions(PositionInitiator positionInitiator)
|
||||
{
|
||||
return _tradingRepository.GetPositions(positionInitiator);
|
||||
}
|
||||
|
||||
public IEnumerable<Position> GetPositionsByStatus(PositionStatus postionStatus)
|
||||
{
|
||||
return _tradingRepository.GetPositionsByStatus(postionStatus);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Scenario GetScenarioByName(string scenario)
|
||||
{
|
||||
return _tradingRepository.GetScenarioByName(scenario);
|
||||
}
|
||||
|
||||
public IEnumerable<Scenario> GetScenarios()
|
||||
{
|
||||
return _tradingRepository.GetScenarios();
|
||||
}
|
||||
|
||||
public IEnumerable<Strategy> GetStrategies()
|
||||
{
|
||||
return _tradingRepository.GetStrategies();
|
||||
}
|
||||
|
||||
public Strategy GetStrategyByName(string strategy)
|
||||
{
|
||||
return _tradingRepository.GetStrategyByName(strategy);
|
||||
}
|
||||
|
||||
public void InsertPosition(Position position)
|
||||
{
|
||||
_tradingRepository.InsertPosition(position);
|
||||
}
|
||||
|
||||
public void InsertScenario(Scenario scenario)
|
||||
{
|
||||
_tradingRepository.InsertScenario(scenario);
|
||||
}
|
||||
|
||||
public void InsertSignal(Signal signal)
|
||||
{
|
||||
_tradingRepository.InsertSignal(signal);
|
||||
}
|
||||
|
||||
public void InsertStrategy(Strategy strategy)
|
||||
{
|
||||
_tradingRepository.InsertStrategy(strategy);
|
||||
}
|
||||
|
||||
public async Task<Position> ManagePosition(Account account, Position position)
|
||||
{
|
||||
var lastPrice = _exchangeService.GetPrice(account, position.Ticker, DateTime.UtcNow);
|
||||
var quantityInPosition = await _exchangeService.GetQuantityInPosition(account, position.Ticker);
|
||||
var orders = await _exchangeService.GetOpenOrders(account, position.Ticker);
|
||||
|
||||
if (quantityInPosition > 0)
|
||||
{
|
||||
// Position still open
|
||||
position.ProfitAndLoss = TradingBox.GetProfitAndLoss(position, position.Open.Quantity, lastPrice);
|
||||
_logger.LogInformation($"Position is still open - PNL : {position.ProfitAndLoss.Realized} $");
|
||||
_logger.LogInformation($"Requested trades : {orders.Count}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// No quantity in position = SL/TP hit
|
||||
if (orders.All(o => o.TradeType != TradeType.StopLoss))
|
||||
{
|
||||
// SL hit
|
||||
_logger.LogInformation($"Stop loss is filled on exchange.");
|
||||
position.StopLoss.SetStatus(TradeStatus.Filled);
|
||||
position.ProfitAndLoss = TradingBox.GetProfitAndLoss(position, position.StopLoss.Quantity, position.StopLoss.Price);
|
||||
_ = _exchangeService.CancelOrder(account, position.Ticker);
|
||||
}
|
||||
else if (orders.All(o => o.TradeType != TradeType.TakeProfit))
|
||||
{
|
||||
// TP Hit
|
||||
if (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit2 != null)
|
||||
{
|
||||
position.TakeProfit2.SetStatus(TradeStatus.Filled);
|
||||
position.ProfitAndLoss = TradingBox.GetProfitAndLoss(position, position.TakeProfit2.Quantity, position.TakeProfit2.Price);
|
||||
_logger.LogInformation($"TakeProfit 2 is filled on exchange.");
|
||||
}
|
||||
else
|
||||
{
|
||||
position.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||
position.ProfitAndLoss = TradingBox.GetProfitAndLoss(position, position.TakeProfit1.Quantity, position.TakeProfit1.Price);
|
||||
_logger.LogInformation($"TakeProfit 1 is filled on exchange.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Position closed manually or forced close by exchange because quantity in position is below 0.");
|
||||
position.Status = PositionStatus.Finished;
|
||||
|
||||
if (orders.Any()) await _exchangeService.CancelOrder(account, position.Ticker);
|
||||
}
|
||||
}
|
||||
|
||||
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 decimal GetFee(Account account, bool isForPaperTrading = false)
|
||||
{
|
||||
if (isForPaperTrading && account.Exchange != TradingExchanges.Evm)
|
||||
{
|
||||
return 0.000665M;
|
||||
}
|
||||
|
||||
return _cacheService.GetOrSave($"Fee-{account.Exchange}", () =>
|
||||
{
|
||||
return _tradingRepository.GetFee(TradingExchanges.Evm).Cost;
|
||||
}, TimeSpan.FromHours(2));
|
||||
}
|
||||
|
||||
public void UpdatePosition(Position position)
|
||||
{
|
||||
_tradingRepository.UpdatePosition(position);
|
||||
}
|
||||
|
||||
public IEnumerable<Position> GetPositions()
|
||||
{
|
||||
var positions = new List<Position>();
|
||||
positions.AddRange(GetPositionsByStatus(PositionStatus.New));
|
||||
positions.AddRange(GetPositionsByStatus(PositionStatus.Filled));
|
||||
positions.AddRange(GetPositionsByStatus(PositionStatus.PartiallyFilled));
|
||||
return positions;
|
||||
}
|
||||
|
||||
|
||||
public async Task WatchTrader()
|
||||
{
|
||||
var availableTickers = new List<Ticker> { Ticker.BTC, Ticker.ETH, Ticker.UNI, Ticker.LINK };
|
||||
var watchAccount = GetTradersWatch();
|
||||
var key = $"AccountsQuantityInPosition";
|
||||
var aqip = _cacheService.GetValue<List<TraderFollowup>>(key);
|
||||
|
||||
if (aqip == null)
|
||||
{
|
||||
aqip = GetAccountsQuantityInPosition(watchAccount);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var a in watchAccount.Where(w => !aqip.Any(a => a.Account.Address == w.Address)))
|
||||
{
|
||||
var newAccount = SetupFollowUp(a);
|
||||
aqip.Add(newAccount);
|
||||
}
|
||||
|
||||
foreach (var a in aqip)
|
||||
{
|
||||
await ManageTrader(a, availableTickers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_cacheService.SaveValue(key, aqip, TimeSpan.FromMinutes(10));
|
||||
}
|
||||
|
||||
public IEnumerable<Trader> GetTradersWatch()
|
||||
{
|
||||
var watchAccount = _statisticRepository.GetBestTraders();
|
||||
var customWatchAccount = _accountService.GetAccounts(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))));
|
||||
return watchAccount;
|
||||
}
|
||||
|
||||
private async Task ManageTrader(TraderFollowup a, List<Ticker> tickers)
|
||||
{
|
||||
var shortAddress = a.Account.Address.Substring(0, 6);
|
||||
|
||||
foreach (var ticker in tickers)
|
||||
{
|
||||
try
|
||||
{
|
||||
var newTrade = await _exchangeService.GetTrade(a.Account.Address, "", ticker);
|
||||
var oldTrade = a.Trades.SingleOrDefault(t => t.Ticker == ticker);
|
||||
if (newTrade == null)
|
||||
{
|
||||
if (oldTrade != null)
|
||||
{
|
||||
_logger.LogInformation($"[{shortAddress}][{ticker}] Trader previously got a position open but the position was close by trader");
|
||||
await _messengerService.SendClosedPosition(a.Account.Address, oldTrade);
|
||||
a.Trades.Remove(oldTrade);
|
||||
}
|
||||
}
|
||||
else if ((newTrade != null && oldTrade == null) || (newTrade.Quantity > oldTrade.Quantity))
|
||||
{
|
||||
_logger.LogInformation($"[{shortAddress}][{ticker}] Trader increase {newTrade.Direction} by {newTrade.Quantity - (oldTrade?.Quantity ?? 0)} with leverage {newTrade.Leverage} at {newTrade.Price} leverage.");
|
||||
|
||||
var index = a.Trades.IndexOf(oldTrade);
|
||||
if (index != -1)
|
||||
{
|
||||
a.Trades[index] = newTrade;
|
||||
}
|
||||
else
|
||||
{
|
||||
a.Trades.Add(newTrade);
|
||||
}
|
||||
|
||||
// Open position
|
||||
await _messengerService.SendIncreasePosition(a.Account.Address, newTrade, "Test6", oldTrade);
|
||||
// Save position to cache
|
||||
}
|
||||
else if (newTrade.Quantity < oldTrade.Quantity && newTrade.Quantity > 0)
|
||||
{
|
||||
var decreaseAmount = oldTrade.Quantity - newTrade.Quantity;
|
||||
var index = a.Trades.IndexOf(oldTrade);
|
||||
a.Trades[index] = newTrade;
|
||||
_logger.LogInformation($"[{a.Account.Address.Substring(0, 6)}][{ticker}] Trader decrease position but didnt close it {decreaseAmount}");
|
||||
await _messengerService.SendDecreasePosition(a.Account.Address, newTrade, decreaseAmount);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"[{shortAddress}][{ticker}] No change - Quantity still {newTrade.Quantity}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"[{shortAddress}][{ticker}] Impossible to fetch trader");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<TraderFollowup> GetAccountsQuantityInPosition(IEnumerable<Trader> watchAccount)
|
||||
{
|
||||
var result = new List<TraderFollowup> ();
|
||||
foreach (var account in watchAccount)
|
||||
{
|
||||
var trader = SetupFollowUp(account);
|
||||
result.Add(trader);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static TraderFollowup SetupFollowUp(Trader account)
|
||||
{
|
||||
var trader = new TraderFollowup
|
||||
{
|
||||
Account = account,
|
||||
Trades = new List<Trade>(),
|
||||
PositionIdentifiers = new List<string>()
|
||||
};
|
||||
|
||||
return trader;
|
||||
}
|
||||
|
||||
public class TraderFollowup
|
||||
{
|
||||
public Trader Account { get; set; }
|
||||
public List<Trade> Trades { get; set; }
|
||||
public List<string> PositionIdentifiers { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user