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
            });
        }
예제 #2
0
        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);
        }