Tracking Realized vs Unrealized Profit in Power BI for Investment Portfolio

To Share 2026 - Content Fb + Web (6)

Tracking realized unrealized profit Power BI là kỹ năng quan trọng nhất sau khi đã có data model tốt.

Biết danh mục đang lãi hay lỗ chưa đủ. Portfolio Manager cần biết lãi nào đã chốt, lãi nào vẫn còn trên giấy, và lãi từ tài sản nào.

Bài viết hướng dẫn toàn bộ — từ định nghĩa, phương pháp tính cost basis, đến DAX measures hoàn chỉnh. Tham khảo thêm tại Microsoft DAX Function Reference.

📌 Đây là bài số 8 trong series Power BI Financial Analytics tại MCNA:


Mục Lục

  1. Phân biệt Realized và Unrealized Profit
  2. Phương pháp tính Cost Basis
  3. FIFO — First In, First Out
  4. AVCO — Average Cost Method
  5. DAX cho Unrealized PnL
  6. DAX cho Realized PnL
  7. PnL Attribution theo Asset Class
  8. Tax Reporting và Gain/Loss Classification
  9. PnL Dashboard Layout
  10. Kết luận

1. Phân Biệt Realized và Unrealized Profit

Định nghĩa cơ bản

Unrealized Profit (Lãi chưa chốt) — tài sản vẫn đang nắm giữ. Lãi chỉ tồn tại trên giấy tờ.

Realized Profit (Lãi đã chốt) — tài sản đã được bán. Tiền thực sự đã vào tài khoản.

Hai con số này có thể khác nhau rất lớn. Và mỗi con số phục vụ một mục đích khác nhau.

Tại sao cần tách biệt hai chỉ số này?

Mục đích Dùng Unrealized Dùng Realized
Báo cáo tài chính nội bộ ✅ Giá trị danh mục hiện tại ✅ Lãi/lỗ đã ghi nhận
Khai thuế thu nhập ❌ Chưa phát sinh nghĩa vụ thuế ✅ Căn cứ tính thuế
Đánh giá fund manager ✅ Hiệu suất thực tế danh mục ⚠️ Bị ảnh hưởng bởi quyết định chốt lời
Quản lý dòng tiền ❌ Chưa có tiền thực ✅ Tiền đã về tài khoản

💡 Nguyên tắc: Unrealized PnL đo hiệu suất đầu tư. Realized PnL đo dòng tiền thực tế. Không thay thế nhau được.


2. Phương Pháp Tính Cost Basis

Cost basis là gì?

Cost basis là giá vốn bình quân của tài sản đang nắm giữ.

Đây là nền tảng để tính cả Unrealized và Realized PnL. Tính sai cost basis thì mọi con số phía sau đều sai.

Hai phương pháp phổ biến nhất

FIFO (First In, First Out): Bán cổ phiếu nào mua trước, tính giá vốn của lô đó trước.

AVCO (Average Cost): Tính giá vốn bình quân của toàn bộ lô đang nắm giữ.

Tiêu chí FIFO AVCO
Độ phức tạp Cao — phải track từng lô Thấp — chỉ cần bình quân
Độ chính xác Cao — sát thực tế từng giao dịch Trung bình — pha trộn các lô
Dùng khi nào Yêu cầu thuế, báo cáo pháp lý Báo cáo nội bộ, tracking nhanh
Phổ biến tại VN Yêu cầu theo Thông tư 86 Nhiều fund sử dụng nội bộ

3. FIFO — First In, First Out

Logic FIFO hoạt động thế nào?

Khi bán, FIFO giả định bán lô mua sớm nhất trước.

Ví dụ: Mua 100 cp VNM lúc 80k, sau đó mua thêm 100 cp lúc 90k. Bán 100 cp → FIFO tính giá vốn = 80k.

Data model cho FIFO

-- FactTrade cần thêm các cột sau để track FIFO:
TradeID          INT           -- PK
AssetCode        VARCHAR(10)
TradeDate        DATE
TradeType        VARCHAR(4)    -- BUY / SELL
Quantity         DECIMAL(18,4)
Price            DECIMAL(18,4)
CostValue        DECIMAL(18,2) -- = Quantity × Price
RemainingQty     DECIMAL(18,4) -- Số lượng còn lại (sau khi match)
LinkedTradeID    INT           -- SELL record link đến BUY record
MatchedQty       DECIMAL(18,4) -- Số lượng đã match
MatchedCostBasis DECIMAL(18,2) -- Cost basis đã match

DAX tính FIFO Cost Basis

-- FIFO Average Cost cho vị thế đang mở
FIFO Cost Basis =
CALCULATE(
    DIVIDE(
        SUMX(
            FILTER(FactTrade,
                FactTrade[TradeType] = "BUY"
                && FactTrade[RemainingQty] > 0
                && FactTrade[AssetCode] = MAX(DimAsset[AssetCode])
            ),
            FactTrade[RemainingQty] * FactTrade[Price]
        ),
        SUMX(
            FILTER(FactTrade,
                FactTrade[TradeType] = "BUY"
                && FactTrade[RemainingQty] > 0
                && FactTrade[AssetCode] = MAX(DimAsset[AssetCode])
            ),
            FactTrade[RemainingQty]
        )
    )
)

⚠️ Lưu ý: FIFO matching phức tạp hơn AVCO rất nhiều. Nên xử lý FIFO ở tầng Power Query hoặc SQL.

DAX chỉ dùng để tổng hợp kết quả đã match — không gánh logic matching.


4. AVCO — Average Cost Method

Logic AVCO đơn giản hơn nhiều

AVCO tính giá vốn bình quân của toàn bộ cổ phiếu đang nắm giữ.

Mỗi lần mua thêm, giá bình quân được tính lại. Khi bán, dùng giá bình quân tại thời điểm đó.

DAX cho AVCO Cost Basis

-- Average Cost Basis (AVCO) đơn giản
AVCO Cost Basis =
DIVIDE(
    CALCULATE(
        SUMX(FactTrade,
            FactTrade[Quantity] * FactTrade[Price]),
        FactTrade[TradeType] = "BUY"
    ),
    CALCULATE(
        SUM(FactTrade[Quantity]),
        FactTrade[TradeType] = "BUY"
    )
)

-- AVCO rolling — cập nhật mỗi lần mua thêm
AVCO Rolling =
VAR TotalCost =
    CALCULATE(
        SUMX(FactTrade, FactTrade[CostValue]),
        FactTrade[TradeType] = "BUY",
        FactTrade[TradeDate] <= MAX(DimDate[Date])
    )
VAR TotalQty =
    CALCULATE(
        SUM(FactTrade[Quantity]),
        FactTrade[TradeType] = "BUY",
        FactTrade[TradeDate] <= MAX(DimDate[Date])
    )
RETURN DIVIDE(TotalCost, TotalQty)

5. DAX Cho Unrealized PnL

Unrealized PnL — vị thế đang mở

Unrealized PnL chỉ tính cho các vị thế chưa đóng.

Nếu tính cả vị thế đã bán, kết quả sẽ sai. Cần filter TradeStatus = "OPEN" trước.

DAX measures Unrealized PnL

-- Unrealized PnL (số tiền tuyệt đối)
Unrealized PnL =
CALCULATE(
    SUMX(
        FactTrade,
        ([Current Market Price] - FactTrade[AvgCostPrice])
            * FactTrade[HoldingQty]
    ),
    FactTrade[TradeStatus] = "OPEN"
)

-- Unrealized PnL %
Unrealized PnL % =
DIVIDE(
    [Unrealized PnL],
    CALCULATE(
        SUM(FactTrade[CostValue]),
        FactTrade[TradeStatus] = "OPEN"
    )
)

-- Unrealized PnL per Asset
Asset Unrealized PnL =
SUMX(
    VALUES(DimAsset[AssetCode]),
    ([Current Market Price] - [AVCO Cost Basis])
        * [Total Open Quantity]
)

-- Flag lãi/lỗ
PnL Flag =
SWITCH(
    TRUE(),
    [Unrealized PnL] > 0,  "🟢 Lãi",
    [Unrealized PnL] < 0,  "🔴 Lỗ",
                           "⚪ Hòa vốn"
)

Phân loại theo mức lãi/lỗ

-- Tier phân loại Unrealized PnL %
Unrealized Tier =
SWITCH(
    TRUE(),
    [Unrealized PnL %] >= 0.20,  "🟢 Strong Gain (>20%)",
    [Unrealized PnL %] >= 0.10,  "🟡 Moderate Gain (10-20%)",
    [Unrealized PnL %] >= 0,     "⚪ Small Gain (0-10%)",
    [Unrealized PnL %] >= -0.10, "🟠 Small Loss (0-10%)",
                                 "🔴 Significant Loss (>10%)"
)

6. DAX Cho Realized PnL

Realized PnL — vị thế đã đóng

Realized PnL chỉ tính khi đã có giao dịch SELL khớp với BUY.

Công thức: Realized PnL = (Sell Price - Cost Basis) × Quantity Sold

DAX measures Realized PnL

-- Realized PnL (số tiền tuyệt đối)
Realized PnL =
CALCULATE(
    SUMX(
        FactTrade,
        (FactTrade[SellPrice] - FactTrade[MatchedCostBasis])
            * FactTrade[MatchedQty]
    ),
    FactTrade[TradeType] = "SELL"
)

-- Realized PnL %
Realized PnL % =
DIVIDE(
    [Realized PnL],
    CALCULATE(
        SUMX(FactTrade, FactTrade[MatchedCostBasis] * FactTrade[MatchedQty]),
        FactTrade[TradeType] = "SELL"
    )
)

-- Realized PnL YTD
Realized PnL YTD =
CALCULATE(
    [Realized PnL],
    DATESYTD(DimDate[Date])
)

-- Realized PnL theo tháng
Realized PnL MTD =
CALCULATE(
    [Realized PnL],
    DATESMTD(DimDate[Date])
)

Total PnL — kết hợp cả hai

-- Total PnL = Unrealized + Realized
Total PnL =
[Unrealized PnL] + [Realized PnL]

Total PnL % =
DIVIDE(
    [Total PnL],
    [Total Cost Value]
)

-- Display measure cho KPI Card
Disp Total PnL =
IF([Total PnL] >= 0, "▲ ", "▼ ")
    & FORMAT(ABS([Total PnL])/1000000, "#,##0.0") & "M"
    & "  ("
    & FORMAT([Total PnL %], "+0.00%;-0.00%")
    & ")"

✅ Best practice: Luôn hiển thị cả Unrealized và Realized riêng biệt trên dashboard — không cộng gộp. Portfolio Manager cần thấy được từng phần để ra quyết định đúng.


7. PnL Attribution Theo Asset Class

PnL Attribution cho thấy điều gì?

Attribution phân tích xem tài sản nào đang đóng góp nhiều nhất vào tổng PnL.

Đây là câu hỏi quan trọng nhất sau khi biết tổng PnL là bao nhiêu.

DAX cho PnL Attribution

-- % đóng góp của từng Asset Class vào Total PnL
Asset Class PnL Contribution % =
DIVIDE(
    [Asset Class Unrealized PnL],
    CALCULATE([Total PnL], ALL(DimAsset[AssetClass]))
)

-- Top Gainers (tài sản lãi nhiều nhất)
-- Dùng TOPN trong visual hoặc DAX:
Top 5 Gainers =
TOPN(5,
    VALUES(DimAsset[AssetCode]),
    [Asset Unrealized PnL], DESC
)

-- Attribution table measure
PnL Summary Row =
DimAsset[AssetClass]
    & " | Unrealized: " & FORMAT([Unrealized PnL]/1000000, "#,##0.0M")
    & " | Realized: "   & FORMAT([Realized PnL]/1000000,   "#,##0.0M")
    & " | Total: "      & FORMAT([Total PnL]/1000000,       "#,##0.0M")

Waterfall Chart cho PnL Attribution

-- Breakdown measure cho Waterfall Chart
-- Mỗi Asset Class = một cột trong waterfall

PnL Waterfall Value =
SWITCH(
    SELECTEDVALUE(DimAsset[AssetClass]),
    "Equities",    [Equities PnL],
    "Bonds",       [Bonds PnL],
    "Cash",        [Cash PnL],
    "Alt Assets",  [Alt PnL],
    [Total PnL]    -- Sentinel: tổng cuối
)

8. Tax Reporting và Gain/Loss Classification

Phân loại Short-term vs Long-term

Tại Việt Nam, lãi vốn từ cổ phiếu bị khấu trừ thuế 0.1% trên giá bán (không phân biệt lãi/lỗ).

Với các tổ chức đầu tư nước ngoài hoặc báo cáo theo chuẩn quốc tế, cần phân loại thêm.

Short-term và Long-term gains có mức thuế suất khác nhau tùy quốc gia.

DAX phân loại Holding Period

-- Số ngày nắm giữ
Holding Days =
DATEDIFF(
    MIN(FactTrade[TradeDate]),  -- Ngày mua
    TODAY(),
    DAY
)

-- Phân loại Short/Long term (theo chuẩn US: 1 năm)
Gain Type =
IF(
    [Holding Days] <= 365,
    "Short-term",
    "Long-term"
)

-- Tax amount (VN: 0.1% trên giá bán)
Estimated Tax VN =
CALCULATE(
    SUMX(FactTrade,
        FactTrade[SellPrice] * FactTrade[MatchedQty] * 0.001),
    FactTrade[TradeType] = "SELL"
)

-- Net Realized PnL sau thuế
Net Realized PnL =
[Realized PnL] - [Estimated Tax VN]

Gain/Loss Harvesting Flag

-- Flag tài sản đang lỗ sâu — ứng viên cho tax-loss harvesting
Tax Loss Harvest Flag =
IF(
    [Unrealized PnL %] <= -0.10
        && [Holding Days] > 30,
    "⚠️ Consider Harvesting",
    "—"
)

9. PnL Dashboard Layout

Cấu trúc dashboard PnL chuẩn

Một PnL dashboard tốt trả lời 3 câu hỏi ngay khi mở.

Câu 1: Tổng PnL là bao nhiêu và xu hướng thế nào?

Câu 2: Unrealized vs Realized phân chia ra sao?

Câu 3: Tài sản nào đang đóng góp nhiều nhất — và âm nhiều nhất?

Layout đề xuất

  • Row 1 — KPI Cards: Total PnL | Unrealized PnL | Realized PnL | PnL % | Win Rate
  • Row 2 — Line Chart: Unrealized PnL trend theo thời gian
  • Row 3 trái — Waterfall: PnL attribution theo Asset Class
  • Row 3 phải — Bar Chart: Top 10 gainers / losers theo tài sản
  • Row 4 — Table: Chi tiết từng position: Cost Basis | Current Price | Qty | Unrealized | Realized | Flag

DAX cho KPI Cards

-- Card: Win Rate (% tài sản đang lãi)
Win Rate % =
DIVIDE(
    COUNTROWS(FILTER(VALUES(DimAsset[AssetCode]),
        [Asset Unrealized PnL] > 0)),
    COUNTROWS(VALUES(DimAsset[AssetCode]))
)

-- Card: Best Performer
Best Performer =
TOPN(1,
    VALUES(DimAsset[AssetCode]),
    [Asset Unrealized PnL %], DESC
)

-- Conditional color cho PnL card
PnL Card Color =
IF([Total PnL] >= 0, "#00C896", "#FF4455")

⚠️ Production tip: Thêm slicer Client Segment và AssetClass. Portfolio Manager thường cần xem PnL theo từng nhóm khách hàng riêng biệt, không phải tổng toàn bộ.


10. Kết Luận

PnL tracking là nền tảng của mọi báo cáo đầu tư

Tracking realized unrealized profit Power BI không chỉ là viết DAX.

Đó là hiểu được vòng đời của một giao dịch — từ lúc mua, đến khi nắm giữ, rồi đến khi bán.

Khi Unrealized và Realized được tách biệt rõ ràng, mọi báo cáo đều trở nên đáng tin cậy hơn.

Ba điều cốt lõi cần nhớ

  1. Xác định phương pháp cost basis trước. FIFO hay AVCO phải nhất quán trong toàn bộ pipeline — không trộn lẫn.
  2. Không cộng Unrealized vào Realized cho một mục đích duy nhất. Mỗi con số phục vụ khác nhau.
  3. Trade matching nên được xử lý ở Power Query hoặc SQL. Đừng để DAX gánh toàn bộ logic FIFO matching.

Tài liệu tham khảo

Bài viết liên quan tại MCNA


Về tác giả

Triều Đặng — Middle Data Analyst tại MCNA Technology School. Chuyên về financial data analytics, Power BI và automated reporting trong Wealth Management.

MCNA Technology School là đơn vị tiên phong đào tạo AI, Big Data, Business Intelligence tại Việt Nam.

Chuyên sâu về Power BI, Python, SQL và Data Analytics — đối tác đào tạo của 300+ doanh nghiệp.

Đối tác đào tạo: Viettel Global, Masan Group, Techcombank, VPBank, Daikin.

Chỉ mục