(P3) CW Dashboard – User Guide

Bài trước mô tả cách CW Dashboard theo dõi lãi lỗ sổ chứng quyền có bảo đảm (CW). Bài này đi vào phần làm: bộ code gồm những file nào, cần chuẩn bị dữ liệu gì, chạy theo thứ tự nào, và engine tính ra từng con số ra sao. Làm theo từ đầu đến cuối, bạn có một dashboard chạy trên máy mình với dữ liệu mẫu trong vài phút, rồi thay bằng dữ liệu thật.

1. Bộ code gồm những gì

Bộ code tách làm hai chương trình độc lập, chạy nối tiếp nhau:

Chương trìnhChạy khi nàoĐầu vàoĐầu ra
Engine (cwpnl/engine.py)Mỗi ngày sau phiên, khi có giao dịch và giá mớiCác file CSV trong data/input/File SQLite data/pnl.db: số luỹ kế theo ngày × mã
Dashboard (cwpnl/server.py + static/index.html)Bất cứ lúc nào cần xemdata/pnl.db + điều khoản CWTrang web tại http://127.0.0.1:8060, 5 tab

Tách như vậy để logic P&L chỉ nằm ở một chỗ (engine). Dashboard chỉ đọc, cộng gộp theo bộ lọc và tính Greeks; mở bao nhiêu người cùng lúc cũng không làm thay đổi số liệu.

Hai cách đo P&L được tính song song:

  • Mark-to-Market: CW đang lưu hành định giá theo giá đóng cửa trên sàn. Khớp với số liệu kế toán.
  • Mark-to-Model: CW đang lưu hành định giá theo Black-Scholes với IV định giá nội bộ. Phản ánh hiệu quả kinh tế của sổ phòng hộ, không bị nhiễu bởi giá CW trên sàn lệch khỏi giá trị lý thuyết.

2. Luồng dữ liệu

BướcViệc làmFile
1Xuất giao dịch CW và cổ phiếu phòng hộ, giá đóng cửa, điều khoản CW, IV nội bộ ra CSVdata/input/*.csv
2Engine đọc CSV, xử lý từng phiên theo thứ tự thời gian: khớp lệnh, cổ tức, đáo hạn; ghi trạng thái luỹ kế từng mãrun_engine.bat → data/pnl.db
3Server đọc pnl.db ở chế độ chỉ đọc; tự đọc lại khi file đổirun_dashboard.bat
4Trình duyệt gọi API theo bộ lọc, vẽ biểu đồ và bảngcwpnl/static/index.html
Engine ghi ra file tạm rồi mới thay thế pnl.db, nên có thể chạy engine trong lúc dashboard đang mở. Sau khi engine xong, chỉ cần bấm F5 trên trình duyệt.

3. Cây thư mục

cw_pnl_dashboard/
├── run_demo.bat          tạo dữ liệu mẫu -> engine -> dashboard (chạy thử)
├── run_engine.bat        chạy engine
├── run_dashboard.bat     mở dashboard
├── run.sh                bản cho macOS / Linux: ./run.sh demo | engine | dashboard | test
├── config.json           tham số + định nghĩa Lô phát hành
├── requirements.txt      pandas
├── README.md             hướng dẫn chi tiết
├── cwpnl/
│   ├── pricing.py        Black-Scholes, Greeks, giải IV (chỉ dùng thư viện chuẩn)
│   ├── common.py         đọc cấu hình và các file đầu vào
│   ├── engine.py         tính P&L luỹ kế -> data/pnl.db
│   ├── server.py         API JSON + phục vụ giao diện
│   └── static/
│       ├── index.html    giao diện 5 tab
│       └── vendor/       Chart.js 4.4.4 (giấy phép MIT)
├── tools/
│   ├── make_sample_data.py   sinh dữ liệu giả lập để chạy thử
│   ├── self_test.py          kiểm tra công thức bằng ví dụ tính tay
│   └── find_python.bat       tìm Python, tự cài thư viện còn thiếu
└── data/
    ├── input/            dữ liệu đầu vào (CSV)
    └── pnl.db            engine tạo ra

4. Dữ liệu cần chuẩn bị

Tất cả là file CSV đặt trong data/input/, dòng đầu là tên cột, mã hoá UTF-8. Ngày dạng YYYY-MM-DD. Giá và tiền tính bằng đồng (không phải nghìn đồng). Ba file đầu là bắt buộc.

4.1 trades.csv — giao dịch của sổ (bắt buộc)

CộtÝ nghĩaVí dụ
dateNgày giao dịch2026-07-15
legCW = chứng quyền; UL = cổ phiếu phòng hộCW
symbolMã CW hoặc mã cổ phiếuCGHI2701
sideSELL hoặc BUY. Với CW: SELL = bán cho nhà đầu tư, BUY = mua lạiSELL
qtyKhối lượng (số dương)50000
priceGiá khớp (đồng)1250
feePhí + thuế của lệnh (đồng); 0 nếu không có19
subaccountTiểu khoản phòng hộ (bắt buộc với UL, để trống với CW)HEDGE02
date,leg,symbol,side,qty,price,fee,subaccount
2026-07-15,CW,CGHI2701,SELL,50000,1250,19,
2026-07-15,UL,GHI,BUY,8000,31200,374,HEDGE02
2026-07-20,CW,CGHI2701,BUY,10000,1180,4,

Mỗi dòng có thể là một lệnh khớp hoặc tổng khớp trong ngày theo mã và chiều. Kết quả như nhau vì engine dùng giá bình quân gia quyền.

4.2 prices.csv — giá đóng cửa (bắt buộc)

CộtÝ nghĩa
dateNgày
symbolMã CW, mã cổ phiếu cơ sở, và chỉ số tham chiếu (mặc định VN30)
closeGiá đóng cửa (đồng; chỉ số tính theo điểm)
Engine lấy các ngày có trong prices.csv làm lịch phiên. Ngày lễ chỉ cần không có dòng giá. Mã không có giá ở một ngày sẽ dùng giá phiên trước.

4.3 cw_metadata.csv — điều khoản CW (bắt buộc)

CộtÝ nghĩaVí dụ
cwMã CWCGHI2701
underlyingMã cổ phiếu cơ sởGHI
issuerTổ chức phát hànhK
strikeGiá thực hiện ban đầu (đồng)35700
ratioTỷ lệ chuyển đổi (số CW đổi 1 cổ phiếu)3
maturityNgày đáo hạn2027-01-18
qttyKhối lượng phát hành3000000
is_call1 = CW mua (mặc định), 0 = CW bán1

4.4 cw_iv.csv — IV định giá nội bộ (nên có)

Ba cột date, cw, iv: IV áp dụng từ ngày date, viết dạng thập phân (0.42 = 42%). Mỗi lần bộ phận định giá đổi IV thì thêm một dòng. CW chưa có IV: engine dùng default_iv; dashboard tính Greeks bằng IV giải từ giá thị trường, không được thì IV mặc định và gắn nhãn cảnh báo.

4.5 File tuỳ chọn

FileCộtDùng khi
cw_adjustments.csvcw, ex_date, strike_before, ratio_before, strike_after, ratio_afterCổ phiếu cơ sở có sự kiện quyền làm điều chỉnh giá thực hiện, tỷ lệ chuyển đổi
dividends.csvsymbol, ex_date, cashCổ tức tiền mặt trên cổ phiếu phòng hộ (đồng/cổ phiếu)
owner.csvcw, ownerGán trader phụ trách để lọc theo người

5. Cấu hình — config.json

{
  "input_dir": "data/input",
  "db_path": "data/pnl.db",
  "risk_free_rate": 0.08,
  "cof_rate": 0.08,
  "default_iv": 0.40,
  "index_symbol": "VN30",
  "start_date": "2025-10-16",
  "end_date": null,
  "port": 8050,
  "host": "127.0.0.1",
  "batches": {
    "Lô 1": { "start_date": "2025-10-16", "subaccounts": ["HEDGE01"],
              "cws": ["CABC2601", "CABC2602", "CDEF2601", "CDEF2602"] },
    "Lô 2": { "start_date": "2026-07-01", "subaccounts": ["HEDGE02"],
              "cws": ["CGHI2701", "CGHI2702", "CJKL2701", "CJKL2702"] }
  }
}
KhoáÝ nghĩa
risk_free_rateLãi suất phi rủi ro trong Black-Scholes
cof_rateLãi suất vốn/năm để tính COF trên giá vốn cổ phiếu phòng hộ
default_ivIV dùng khi CW chưa có IV nội bộ
start_date, end_dateKhoảng ngày engine tính; null = đến ngày giá cuối cùng
host, portĐịa chỉ server. Giữ 127.0.0.1 để chỉ máy mình truy cập được
batchesMỗi Lô phát hành: ngày bắt đầu, tiểu khoản phòng hộ, danh sách CW. Mọi CW và tiểu khoản phải thuộc một Lô
Các số trong ví dụ (8%, 40%, mã HEDGE01…) chỉ để minh hoạ. Hãy thay bằng tham số của sổ thật.

6. Các bước chạy

6.1 Lần đầu

  1. Cài Python 3.10+ từ python.org. Trên Windows, tick Add python.exe to PATH khi cài.
  2. Giải nén bộ code vào một thư mục bất kỳ, ví dụ D:\cw_pnl_dashboard.
  3. Bấm đúp run_demo.bat (macOS/Linux: ./run.sh demo). Lần đầu script tự cài pandas, sinh dữ liệu giả lập (4 mã cơ sở, 8 CW, 2 Lô, khoảng một năm), chạy engine rồi mở trình duyệt.
  4. Kiểm tra công thức: python tools/self_test.py phải in TẤT CẢ ĐỀU ĐÚNG.

6.2 Chuyển sang dữ liệu thật

  1. Xoá các file mẫu trong data/input/.
  2. Đặt 3 file bắt buộc trades.csv, prices.csv, cw_metadata.csv (mục 4), thêm cw_iv.csv và các file tuỳ chọn nếu có.
  3. Sửa config.json: ngày bắt đầu, lãi suất, danh sách Lô với đúng mã CW và tiểu khoản phòng hộ.
  4. Bấm đúp run_engine.bat. Đọc log: số phiên, số giao dịch, các dòng CẢNH BÁO.
  5. Bấm đúp run_dashboard.bat.

6.3 Hằng ngày

BướcViệc làm
1Sau phiên: thêm giao dịch mới vào trades.csv, giá đóng cửa mới vào prices.csv; thêm dòng vào cw_iv.csv nếu đổi IV; thêm CW mới vào cw_metadata.csv và config.json nếu có đợt phát hành mới.
2Chạy run_engine.bat (vài giây).
3Mở hoặc F5 dashboard. Góc trên ghi “Số liệu tới <ngày>” phải là phiên vừa đóng.

7. Engine tính gì: ví dụ tính tay

Engine xử lý từng phiên theo thứ tự thời gian, giữ trạng thái cho mỗi CW và mỗi cặp (cổ phiếu, tiểu khoản):

Sự kiệnCách ghi nhận
Bán CWCập nhật giá bán bình quân gia quyền; KL lưu hành tăng; tiền thu ròng tăng
Mua lại CWRealized += (giá bán BQ − giá mua) × KL; KL lưu hành giảm
Mua cổ phiếuCập nhật giá vốn bình quân
Bán cổ phiếuRealized += (giá bán − giá vốn BQ) × KL
Cổ tức tiền mặtRealized phòng hộ += cổ tức × KL nắm giữ cuối phiên trước
Đáo hạnThanh toán tiền mặt theo S5 = bình quân giá đóng cửa cơ sở 5 phiên liền trước ngày đáo hạn; mỗi CW nhận max(S5 − K, 0) / CR
Cuối mỗi phiênUnrealized Mark-to-Market = (giá bán BQ − giá đóng cửa CW) × KL lưu hành; Unrealized Mark-to-Model = (giá bán BQ − giá Black-Scholes) × KL lưu hành; Unrealized cổ phiếu = (giá − giá vốn BQ) × KL
Mỗi ngày lịchCOF của Lô = cof_rate / 365 × giá vốn cổ phiếu phòng hộ của Lô

Ví dụ dưới đây chính là bộ kiểm tra trong tools/self_test.py: CW giá thực hiện 10.000 đ, tỷ lệ 2:1, đáo hạn 12/01/2026; cof_rate 3,65%/năm.

NgàySự kiệnKết quả
05/01Bán 1.000 CW giá 300; mua 500 cổ phiếu giá 10.000Giá bán BQ 300; giá vốn 5.000.000 đ; COF ngày = 3,65% / 365 × 5.000.000 = 500 đ
06/01Bán thêm 1.000 CW giá 500Giá bán BQ = (300 × 1.000 + 500 × 1.000) / 2.000 = 400
07/01Mua lại 500 CW giá 450Realized CW = (400 − 450) × 500 = −25.000 đ; còn 1.500 CW
08/01Bán 200 cổ phiếu giá 10.600; CW đóng cửa 450Realized cổ phiếu = (10.600 − 10.000) × 200 = 120.000 đ; Unrealized CW = (400 − 450) × 1.500 = −75.000 đ
12/01Đáo hạn. Giá cơ sở 5 phiên trước: 10.000; 10.200; 10.400; 10.600; 10.800S5 = 10.400; thanh toán (10.400 − 10.000) / 2 = 200 đ/CW; Realized CW luỹ kế = −25.000 + (400 − 200) × 1.500 = 275.000 đ

Dashboard cộng các số luỹ kế này theo ngày rồi tính:

PBT (Mark-to-Market) = CW realized + CW unrealized + Hedge realized + Hedge unrealized − Phí − COF
Mark-to-Model = PBT − CW unrealized (thị trường) + CW unrealized (mô hình)
ROI = P&L trong kỳ / Vốn bình quân; Vốn = | giá vốn phòng hộ + phí − tiền thu ròng từ bán CW |

Khi lọc theo mã cơ sở hoặc trader, COF của Lô được chia theo tỷ trọng giá vốn phòng hộ của nhóm đang lọc. Cộng các nhóm lại luôn bằng tổng: đã kiểm tra trên dữ liệu mẫu, tổng theo Lô, theo trader, theo mã cơ sở và theo hai nửa kỳ đều khớp với P&L toàn sổ.

8. Cơ sở dữ liệu đầu ra — data/pnl.db

BảngCột chínhNội dung
fp_symbol_dailydate, leg, sym, sub, batch, qty, qtty, px, spot, strike, ratio, iv, iv_src, bs_px, avg_px, realized, unreal, unreal_pricing, fee, net_cash, net_tx, fundMỗi dòng = 1 mã × 1 phiên. realized, fee, net_cash là luỹ kế; unreal, unreal_pricing, fund, qty là trạng thái cuối ngày; net_tx là giao dịch ròng trong ngày
fp_cof_calendarcal_date, COF <Lô> (VND)COF từng ngày lịch của mỗi Lô
fp_vn30date, closeChỉ số tham chiếu
fp_settlementscw, maturity_date, s5, strike, ratio, payoff_per_cw, qty, realizedCW đã tất toán
fp_cw_adjustmentsnhư file đầu vàoLịch điều chỉnh quyền
fp_metakey, valueĐịnh nghĩa Lô, giờ chạy engine, mô tả COF (dạng JSON)

File này mở được bằng DB Browser for SQLite, Python (pandas.read_sql) hoặc Excel qua Power Query, nên có thể dùng làm nguồn cho các báo cáo khác.

9. Dashboard và API

TabNội dungBộ lọc
Tổng quan6 thẻ KPI (PBT, Mark-to-Model, Delta exposure, Gamma 1%, Theta, Sổ phòng hộ) · P&L luỹ kế hai cách đo · Sổ phòng hộ so với VN30 · Báo cáo P&L theo kỳ với cột YTD từng năm và Toàn kỳNgày / Tuần / Tháng
Phân tích P&LWaterfall đóng góp · P&L luỹ kế · ROI · Phân rã theo mã cơ sở và từng CWKỳ, Lô, mã cơ sở, trader, trạng thái, cách đo
Vị thếTỷ lệ phân phối theo thời gian · Delta exposure theo mã · Bảng vị thế: moneyness, ngày còn lại, IV (gắn nhãn nếu không phải IV nội bộ), OI, Greeks, giao dịch ròngNgày, Lô, mã cơ sở, trader
IVSmile và term structure (IV nội bộ so với IV thị trường) · IV lịch sử · bảng IVMã cơ sở
Tất toánCW đã đáo hạn: giá S5, thanh toán mỗi CW, realized tại đáo hạn—

Giao diện lấy dữ liệu qua 6 API JSON (đều là GET, tham số trên URL), có thể gọi từ Excel hoặc script khác:

APITham sốTrả về
/api/meta—Danh sách Lô, mã cơ sở, trader, ngày dữ liệu
/api/overviewrange = D/W/MKPI, xu hướng, bảng P&L theo kỳ
/api/pnlstart, end, batch, underlying, owner, status, method (model/market), rangeWaterfall, xu hướng, ROI, phân rã
/api/positionsdate, batch, underlying, ownerVị thế, Greeks, tỷ lệ phân phối
/api/ivunderlyingĐiểm IV hiện tại và lịch sử
/api/settlements—CW đã tất toán

10. Kiểm tra và lỗi thường gặp

Hiện tượngCách xử lý
Thiếu data/input/...csvĐặt đủ 3 file bắt buộc
CW có giao dịch nhưng thiếu trong cw_metadata.csvBổ sung điều khoản cho mã được báo
mua lại ... > KL đang lưu hành hoặc bán ... > KL nắm giữGiao dịch thiếu hoặc sai chiều; kiểm tra trades.csv của mã và ngày được báo
chưa gán LôThêm mã CW hoặc tiểu khoản vào batches trong config.json
Dashboard báo “Chưa có dữ liệu”Chưa chạy engine, hoặc db_path trỏ sai
Cổng 8050 đang bậnĐổi port trong config.json và URL trong run_dashboard.bat
Nhãn TT / MĐ cạnh IVCW chưa có IV nội bộ: Greeks đang dùng IV thị trường (TT) hoặc IV mặc định (MĐ)
Không tìm thấy PythonCài lại Python và tick Add to PATH, hoặc đặt bản Python nhúng vào thư mục python_runtime\ cạnh bộ code

11. Giới hạn và hướng mở rộng

  • Cửa sổ S5: trong 5 phiên trước đáo hạn, nghĩa vụ CW đã bị chốt dần theo giá bình quân, nhưng Mark-to-Model vẫn định giá bằng Black-Scholes trên giá hiện tại. P&L có thể dao động trong vài phiên này rồi trở về đúng ở ngày tất toán. Có thể cải tiến bằng cách định giá theo phần S5 đã biết cộng phần còn lại.
  • Một lãi suất phi rủi ro cho mọi CW; mỗi CW một IV (không nội suy bề mặt IV).
  • Đầu vào là CSV xuất tay. Bước tiếp theo hợp lý là đọc thẳng từ cơ sở dữ liệu giao dịch, hoặc dùng Power Query gộp file xuất hằng ngày thành trades.csv.
  • Server không có đăng nhập. Không đổi host thành 0.0.0.0 nếu không muốn máy khác trong mạng xem được số liệu.
  • Có thể lên lịch chạy engine tự động bằng Task Scheduler (Windows) hoặc cron (macOS/Linux) sau giờ đóng cửa.

12. Bản quyền và miễn trừ

  • Bộ code và bài viết do tác giả tự viết. Dữ liệu mẫu (mã ABC, DEF, GHI, JKL, các CW và tiểu khoản) hoàn toàn giả lập, không phản ánh vị thế hay kết quả của bất kỳ tổ chức nào.
  • Mô hình Black-Scholes là kiến thức công khai (Black, F. & Scholes, M., 1973, “The Pricing of Options and Corporate Liabilities”, Journal of Political Economy).
  • Thư viện dùng kèm: Chart.js 4.4.4 (MIT, file giấy phép đi kèm trong static/vendor/), pandas (BSD), SQLite (public domain), Python (PSF License).
  • VN30, HOSE là tên gọi thuộc chủ sở hữu tương ứng, chỉ dùng để mô tả.
  • Bài viết là chia sẻ kỹ thuật, không phải khuyến nghị đầu tư. Kiểm tra kỹ số liệu trước khi dùng cho báo cáo chính thức.

Tags:

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments