Appearance
Расчёт прибыли и ключевых показателей
Обзор процесса
Расчёт выполняется в TransactionAssets.getAssetData(). Транзакции сортируются по дате (от старых к новым), затем итеративно обрабатываются через TransactionAggregator.getTotalData(), который делегирует расчёт в класс конкретного типа.
mermaid
flowchart TD
A["TransactionAssets.getAssetData()"] --> B["Сортировка транзакций по дате<br/>(от старых к новым)"]
B --> C{"Итерация по<br/>каждой транзакции"}
C --> D["TransactionAggregator.getTotalData()"]
D --> E{"Определение типа"}
E -->|"Buy (2)"| BUY["TransactionBuy.getTotalData()"]
E -->|"Sell (1)"| SELL["TransactionSell.getTotalData()"]
E -->|"Transfer In (3)"| TIN["TransactionTransferIn.getTotalData()"]
E -->|"Transfer Out (4)"| TOUT["TransactionTransferOut.getTotalData()"]
E -->|"Mining (5)"| MINE["TransactionMining.getTotalData()"]
BUY --> C
SELL --> C
TIN --> C
TOUT --> C
MINE --> C
C -->|"Все обработаны"| FINAL["Финальные расчёты"]
FINAL --> DONE["Готовые данные ассета"]Логика по типам транзакций
Buy (Покупка)
total.invested += quantity × transactionPrice
total.buyCoins += quantity
total.buyCash += quantity × marketPriceByCurrency
avg = средневзвешенная:
(prevAvg × prevAmount + transactionPrice × quantity) / (prevAmount + quantity)
holdings.buyOrSell += quantity
holdings.coins += quantitySell (Продажа)
Если НЕ coin-to-coin:
total.incomeCash += quantity × transactionPrice
profit.realised += (transactionPrice − avg) × quantity
holdings.buyOrSell -= quantity
holdings.coins -= quantityОбмен монета↔монета НЕ считается реализованной прибылью и не добавляет в
incomeCash.
Transfer In (Ввод средств)
holdings.transfer += quantity
holdings.coins += quantityМонеты считаются «бесплатными» — не влияют на avg и invested.
Transfer Out (Вывод средств)
Сложная логика с учётом баланса «бесплатных» монет:
transferDifference = holdings.transfer − quantity
Если transferDifference ≥ 0:
// Хватает бесплатных монет
holdings.transfer -= quantity
Иначе:
// Бесплатных не хватает → забираем из buyOrSell
profit.realised -= |transferDifference| × avg
holdings.buyOrSell -= |transferDifference|
holdings.transfer = 0
holdings.coins -= quantityПри выводе «платных» монет фиксируется убыток, т.к. монеты отдаются бесплатно.
Mining/Airdrop
Двойственное поведение зависит от поля invested:
Если invested > 0 (есть затраты):
// Ведёт себя как Buy
transactionPrice = invested / quantity
total.invested += invested
avg = пересчёт средневзвешенной
holdings.buyOrSell += quantity
Если invested = 0 (бесплатно):
// Ведёт себя как Transfer In
holdings.transfer += quantity
holdings.coins += quantityФинальные расчёты (после итерации)
Текущая стоимость холдингов
holdings.cash = marketPrice × (holdings.buyOrSell + holdings.transfer)Общая прибыль
totalHoldings = total.incomeCash + holdings.cash
total.profit = totalHoldings − total.invested
total.profitPercent = (totalHoldings − total.invested) / total.investedОсобые случаи:
- Если
totalHoldings = 0→profitPercent = 0 - Если
invested = 0→profitPercent = 1(100%)
Нереализованная прибыль
avgProfit = (marketPrice − avg) × holdings.buyOrSell
transferCoinsCash = holdings.transfer × marketPrice
profit.unrealised = avgProfit + transferCoinsCash
profit.current = avgProfit + transferCoinsCash«Бесплатные» монеты учитываются отдельным слагаемым.
Реализованная прибыль
Накапливается при каждой продаже за фиат:
profit.realised += (sellPrice − avg) × sellQuantityИ при выводе платных монет:
profit.realised -= |transferDifference| × avgАгрегация по всему портфолио
Геттер totalAssetsData в Vuex store суммирует данные всех ассетов:
mermaid
flowchart LR
A1["Ассет BTC"] --> SUM["totalAssetsData"]
A2["Ассет ETH"] --> SUM
A3["Ассет N"] --> SUM
SUM --> R1["cash = Σ holdings.cash"]
SUM --> R2["invested = Σ total.invested"]
SUM --> R3["realised = Σ profit.realised"]
SUM --> R4["unrealised = Σ profit.unrealised"]
SUM --> R5["incomeCash = Σ total.incomeCash"]
SUM --> R6["profit.percent = (incomeCash + cash − invested) / invested"]
SUM --> R7["profit.cash = incomeCash + cash − invested"]Расчёт прибыли на отдельной транзакции
Формулы зависят от типа и пары:
Покупка за фиат
profitCash = (marketPrice − transactionPrice) × quantity
profitPercent = (marketPrice / transactionPrice − 1) × 100Продажа за фиат
profitCash = (transactionPrice − marketPrice) × quantity
profitPercent = (1 − marketPrice / transactionPrice) × 100Покупка/продажа coin vs coin
totalBuyValue = buyQuantity × buyMarketPrice
potentialSellCoins = totalBuyValue / sellMarketPrice
profitInCoins = potentialSellCoins − originalSellCoins
profitCash = profitInCoins × sellMarketPrice
profitPercent = profitInCoins / originalSellCoins × 100Майнинг/аирдроп
Если invested > 0:
profitCash = marketPrice × quantity − invested
profitPercent = (1 − invested / (marketPrice × quantity)) × 100
Если invested = 0:
profitCash = marketPrice × quantity
profitPercent = 100%Средняя цена актива
Формула из TransactionMath.getAvgValue():
avg = (prevAvg × prevAmount + newPrice × newAmount) / (prevAmount + newAmount)Средняя цена пересчитывается при каждой транзакции типа Buy и Mining (с инвестициями). Transfer In и Transfer Out не влияют на avg.