private async Task <AssetRealisedPnL> CalculateAsync(TradeData tradeData, string walletId, string assetId) { AssetRealisedPnL prevAssetPnL = await _assetRealisedPnLRepository.GetLastAsync(walletId, assetId) ?? new AssetRealisedPnL(); bool inverted = tradeData.QuoteAsset == assetId; string crossAssetId = inverted ? tradeData.BaseAsset : tradeData.QuoteAsset; Quote quote = await _quoteService.GetAsync(assetId, QuoteAssetId); Quote crossQuote = await _quoteService.GetAsync(crossAssetId, QuoteAssetId); RealisedPnLResult realisedPnLResult = RealisedPnLCalculator.Calculate( tradeData.Price, tradeData.Volume, inverted, tradeData.Type == TradeType.Sell ? -1 : 1, prevAssetPnL.CumulativeVolume, prevAssetPnL.CumulativeOppositeVolume, quote.Mid, prevAssetPnL.AvgPrice, crossQuote.Mid); return(new AssetRealisedPnL { WalletId = walletId, AssetId = assetId, Time = tradeData.Time, Exchange = tradeData.Exchange, TradeId = tradeData.Id, TradeAssetPair = tradeData.AssetPair, TradePrice = tradeData.Price, TradeVolume = tradeData.Volume, TradeType = tradeData.Type, CrossAssetPair = crossQuote.AssetPair, CrossPrice = crossQuote.Mid, Price = realisedPnLResult.Price, Volume = realisedPnLResult.Volume, OppositeVolume = realisedPnLResult.OppositeVolume, Inverted = inverted, PrevAvgPrice = prevAssetPnL.AvgPrice, PrevCumulativeVolume = prevAssetPnL.CumulativeVolume, PrevCumulativeOppositeVolume = prevAssetPnL.CumulativeOppositeVolume, OpenPrice = prevAssetPnL.AvgPrice, ClosePrice = realisedPnLResult.Price, CloseVolume = realisedPnLResult.ClosedVolume, RealisedPnL = realisedPnLResult.RealisedPnL, AvgPrice = realisedPnLResult.AvgPrice, CumulativeVolume = realisedPnLResult.CumulativeVolume, CumulativeOppositeVolume = realisedPnLResult.CumulativeOppositeVolume, CumulativeRealisedPnL = prevAssetPnL.CumulativeRealisedPnL + realisedPnLResult.RealisedPnL, Rate = quote.Mid, UnrealisedPnL = realisedPnLResult.UnrealisedPnL, LimitOrderId = tradeData.LimitOrderId, OppositeClientId = tradeData.OppositeClientId, OppositeLimitOrderId = tradeData.OppositeLimitOrderId }); }
public static RealisedPnLResult Calculate( decimal tradeRate, decimal tradeVolume, bool inverted, int direction, decimal currentVolume, decimal currentOppositeVolume, decimal rate, decimal openRate, decimal crossRate) { var pnl = new RealisedPnLResult { Price = inverted ? 1 / tradeRate * crossRate : tradeRate * crossRate, Volume = inverted ? tradeRate * tradeVolume : tradeVolume, OppositeVolume = inverted ? tradeVolume * crossRate : tradeRate * tradeVolume * crossRate }; if (inverted) { direction *= -1; } if (currentVolume >= 0 && direction > 0 || currentVolume <= 0 && direction < 0) { pnl.CumulativeVolume = currentVolume + pnl.Volume * direction; pnl.CumulativeOppositeVolume = currentOppositeVolume + pnl.OppositeVolume * -1 * direction; pnl.AvgPrice = pnl.CumulativeVolume != 0 ? Math.Abs(pnl.CumulativeOppositeVolume / pnl.CumulativeVolume) : 0; } else { if (pnl.Volume > Math.Abs(currentVolume)) { pnl.ClosedVolume = Math.Abs(currentVolume); decimal openVolume = pnl.Volume - pnl.ClosedVolume; pnl.CumulativeVolume = openVolume * direction; pnl.CumulativeOppositeVolume = openVolume * pnl.Price * -1 * direction; pnl.AvgPrice = pnl.Price; } else { pnl.ClosedVolume = pnl.Volume; pnl.CumulativeVolume = currentVolume + pnl.Volume * direction; pnl.CumulativeOppositeVolume = currentOppositeVolume + pnl.Volume * openRate * -1 * direction; pnl.AvgPrice = openRate; } pnl.RealisedPnL = (pnl.Price - openRate) * pnl.ClosedVolume; } pnl.UnrealisedPnL = (rate - pnl.AvgPrice) * pnl.CumulativeVolume; return(pnl); }