top of page
Portfolio Management Toolkit Notesสารบัญ
Portfolio Management Toolkit Notesพิมพ์หน้านี้

Beyond Diversification · บทย่อย 2

CPPI จากศูนย์

ถ้ามีเงิน 1,000 บาท และอยากรักษาเงินไว้ 800 บาท เราควรนำเงินไปเสี่ยงเท่าไร และจะปรับอย่างไรเมื่อพอร์ตขาดทุน?

บท ข้อจำกัดของ Diversification อธิบายว่าการเพิ่มสินทรัพย์ยังอาจเหลือความเสี่ยงจากเหตุการณ์ร่วม บทนี้เปลี่ยนคำถามมาเป็นการจัดสรรเงินระหว่างส่วนเสี่ยงกับส่วนที่ใช้รองรับเกณฑ์ขั้นต่ำ เราจะกำหนดกติกา คำนวณหนึ่งงวดด้วยมือ แล้วใช้ Python ทำซ้ำตามเวลา

CPPI — Constant Proportion Portfolio Insurance เป็นกติกาที่ผูกเงินในสินทรัพย์เสี่ยงเข้ากับส่วนต่างระหว่างมูลค่าพอร์ตกับ floor ชื่อ “insurance” ไม่ได้แปลว่ามีบริษัทประกันรับชำระส่วนที่ขาดให้ และ floor ในการจำลองแบบปรับพอร์ตเป็นงวดอาจถูกทะลุได้ ตัวอย่างท้ายบทจะแสดงเหตุการณ์นั้นด้วยตัวเลข

บทนี้เป็นจุดเริ่มเรียน CPPI: Floor, Cushion, การปรับวงเงิน และ Gap risk รวมอยู่ที่นี่ เมื่อคำนวณตามได้แล้ว อ่าน Portfolio Insurance: เปรียบเทียบวิธีป้องกันพอร์ต เพื่อเทียบกับการซื้อ Option การยก Floor แบบ TIPP และตัวคูณที่ปรับได้ หรือไป Monte Carlo เพื่อทดสอบ CPPI หลายเส้นทาง ข้อมูลทุกชุดในหน้านี้เป็นตัวอย่างสมมติ ไม่ใช่ผลการลงทุนจริงหรือคำแนะนำให้เลือกค่า multiplier ใด

เส้นทางเรียน คำถาม
Floor และ Cushion เงินส่วนใดเป็นเกณฑ์ขั้นต่ำ และส่วนใดใช้รับความเสี่ยง?
คำนวณทีละเดือน หลังขึ้นหรือลง ต้องเปลี่ยนวงเงินอย่างไร?
เขียนฟังก์ชัน เก็บสถานะของพอร์ตให้เวลาถูกต้องอย่างไร?
Gap Risk ตลาดต้องลงแรงแค่ไหนจึงทะลุ floor ได้?
ทดสอบ Floor แบบ TIPP ฟังก์ชันยก Floor ตามจุดสูงสุดที่รู้แล้วจริงหรือไม่?

ดาวน์โหลด Notebook ของบทนี้ เพื่อรันตัวอย่างตามลำดับและเทียบผลลัพธ์ที่บันทึกไว้

ตั้งชื่อให้เงินแต่ละส่วนก่อนเขียนสูตร

ใช้เงินตั้งต้น 1,000 บาทและ floor คงที่ 800 บาท ดังนั้นเงินส่วนเกินจาก floor เท่ากับ 200 บาท เราเรียกส่วนนี้ว่า cushion หากกำหนด multiplier หรือ ตัวคูณ เท่ากับ 3 กติกาจะให้ลงทุนในสินทรัพย์เสี่ยงสามเท่าของ cushion หรือ 600 บาท แล้วเก็บอีก 400 บาทในส่วนปลอดภัยของแบบจำลอง

สัญลักษณ์ ความหมาย ณ เวลาที่กำลังจัดพอร์ต ค่าเริ่มต้น หน่วย
VtV_t มูลค่าพอร์ตทั้งหมด 1,000 บาท
FtF_t floor หรือเกณฑ์ขั้นต่ำขณะนั้น 800 บาท
Ct=Vt−FtC_t=V_t-F_t cushion 200 บาท
mm ตัวคูณ cushion 3 ไม่มีหน่วย
EtE_t จำนวนเงินในสินทรัพย์เสี่ยง 600 บาท
Vt−EtV_t-E_t จำนวนเงินในส่วนปลอดภัย 400 บาท

ในรูปแบบที่ยังไม่จำกัดการกู้ยืม สูตรเงินเสี่ยงคือ Et=mCtE_t=mC_t แต่ในบทนี้จะ ไม่กู้เงินและไม่ขายชอร์ต จึงจำกัด EtE_t ไว้ระหว่าง 0 กับมูลค่าพอร์ตทั้งหมด

Et=min⁡(max⁡(m(Vt−Ft),0),Vt),wt=EtVtE_t=\min\bigl(\max(m(V_t-F_t),0),V_t\bigr), \qquad w_t=\frac{E_t}{V_t}

wtw_t คือน้ำหนักในสินทรัพย์เสี่ยง ตัวอย่างเริ่มต้นได้ 60% คำว่า constant proportion ในชื่อ CPPI หมายถึงการใช้ตัวคูณกับ cushion ตามกติกา ไม่ได้หมายความว่าน้ำหนักสินทรัพย์เสี่ยงจะคงที่ 60% ตลอดเวลา และเมื่อชนขอบ 0% หรือ 100% จำนวนเงินจริงจะไม่เท่ากับ mCtmC_t เสมอ

สมมติว่าสินทรัพย์เสี่ยงให้ผลตอบแทน Rt+1R_{t+1} และส่วนปลอดภัยให้ Rt+1sR^s_{t+1} ระหว่างจุดปรับพอร์ตสองจุด มูลค่าพอร์ตปลายงวดคือ

Vt+1=Et(1+Rt+1)+(Vt−Et)(1+Rt+1s)V_{t+1}=E_t(1+R_{t+1})+(V_t-E_t)(1+R^s_{t+1})

น้ำหนักตัดสินใจที่เวลา tt ส่วนผลตอบแทนเกิดตลอดงวดที่ตามมา ตอนกำหนด EtE_t เรายังไม่รู้ Rt+1R_{t+1} การคำนวณ CPPI ต้องรักษาลำดับนี้ไว้ แนวคิดพื้นฐานและรูปแบบ floor หลายชนิดอธิบายอยู่ใน CPPI Part 1 และ CPPI Part 2 ของคอร์ส

“ส่วนปลอดภัย” ต้องสัมพันธ์กับสิ่งที่ต้องการรักษา

ตัวอย่างแรกให้เงินสดมีผลตอบแทน 0% ทุกเดือน จึงไม่เสี่ยงต่อ floor ที่กำหนดเป็นเงินบาทคงที่ แต่ในการใช้งานอื่น สินทรัพย์ที่เหมาะกับเป้าหมายอาจต่างกัน เช่น เงินที่จะต้องจ่าย ณ วันหนึ่งในอนาคตต้องพิจารณามูลค่าปัจจุบันและอายุของสินทรัพย์รองรับด้วย กองทุนตราสารหนี้ที่ราคาขึ้นลงไม่ได้กลายเป็นเงินสดไร้ความเสี่ยงเพียงเพราะเรียกว่า safe asset อ่านต่อได้ที่ Floor และ Cushion

สองเดือนแรก: คิดด้วยมือให้ทันกติกา

เดือนแรกสินทรัพย์เสี่ยงให้ผลตอบแทน +8% เงิน 600 บาทจึงกลายเป็น 648 บาท ขณะที่เงินสด 400 บาทคงเดิม พอร์ตปลายเดือนเป็น 1,048 บาท จากนั้นจึงเตรียมการลงทุนเดือนที่สอง

C1=1,048−800=248,E1=3(248)=744C_1=1{,}048-800=248, \qquad E_1=3(248)=744

พอร์ตจึงเพิ่มเงินเสี่ยงจากมูลค่าที่ถืออยู่ 648 บาทเป็น 744 บาท ณ จุดปรับพอร์ต โดยใช้เงินสดเพิ่ม 96 บาท เดือนที่สองสมมติสินทรัพย์เสี่ยงให้ −12% จึงได้

V2=744(1−0.12)+(1,048−744)=958.72V_2=744(1-0.12)+(1{,}048-744)=958.72

เดือนที่สามจึงเริ่มด้วย cushion 958.72−800=158.72958.72-800=158.72 บาท และวงเงินเสี่ยง 3(158.72)=476.163(158.72)=476.16 บาท กติกานี้เพิ่มวงเงินเสี่ยงหลังพอร์ตโตและลดวงเงินหลังพอร์ตลง ภายใต้ floor คงที่และตัวคูณคงที่

เราจะต่อเป็นหกเดือน โดยกำหนดผลตอบแทนสินทรัพย์เสี่ยงเท่ากับ +8%, −12%, +6%, −25%, +10%, +4% ตามลำดับ เงินสดยังให้ 0% และไม่มีค่าธรรมเนียม ภาษี หรือข้อจำกัดการซื้อขายเศษหน่วย

เดือน มูลค่าพอร์ตต้นเดือน เงินเสี่ยงต้นเดือน ผลตอบแทนสินทรัพย์เสี่ยง มูลค่าพอร์ตปลายเดือน
1 1,000.0000 600.0000 +8% 1,048.0000
2 1,048.0000 744.0000 −12% 958.7200
3 958.7200 476.1600 +6% 987.2896
4 987.2896 561.8688 −25% 846.8224
5 846.8224 140.4672 +10% 860.8691
6 860.8691 182.6074 +4% 868.1734

หลังเดือนที่สี่ พอร์ตเหลือ cushion เพียง 46.8224 บาท จึงให้เงินเสี่ยงในเดือนที่ห้าประมาณ 140.4672 บาท เมื่อสินทรัพย์เสี่ยงฟื้น +10% พอร์ตจึงไม่ได้ฟื้น +10% ทั้งก้อน แต่เพิ่มประมาณ 14.0467 บาท เพราะส่วนใหญ่ย้ายไปอยู่ในเงินสดแล้ว

เขียนฟังก์ชันโดยแยก “ก่อนงวด” กับ “หลังงวด”

เปิด Notebook ใหม่แล้วรันทุกช่องในหน้านี้ตามลำดับ โค้ดใช้ NumPy สำหรับตัวเลขและ pandas สำหรับตาราง ไม่ต้องเปิด Notebook ของบทอื่นก่อน

import numpy as np
import pandas as pd

cppi_risky_returns = np.array([0.08, -0.12, 0.06, -0.25, 0.10, 0.04])
cppi_start = 1000.0
cppi_floor_fraction = 0.80
cppi_multiplier = 3.0

0.80 ในที่นี้หมายถึง floor เริ่มต้น 80% ของเงินตั้งต้น ไม่ได้แปลว่าลงทุนปลอดภัย 80% ส่วน 0.08 เป็นผลตอบแทน +8% ของหนึ่งเดือน เราจะเก็บตัวเลขด้วยหน่วยทศนิยมตลอดการคำนวณ แล้วค่อยแปลงเพื่อแสดงผล

เริ่มจากฟังก์ชันที่รับผลตอบแทนทีละงวด เราให้ผู้เรียกบอกจำนวนงวดต่อปีด้วย เพื่อแปลงอัตราเงินสดต่อปีให้เป็นอัตราต่องวดอย่างสอดคล้อง หากเป็น อัตราผลตอบแทนต่อปีแบบทบต้น gsg_s สูตรคือ

Rs=(1+gs)1/k−1R^s=(1+g_s)^{1/k}-1

kk คือจำนวนงวดต่อปี เช่น 12 สำหรับรายเดือน หรือ 4 สำหรับรายไตรมาส อัตราเงินสด 3% ต่อปีจึงเป็นประมาณ 0.2466% ต่อเดือน การหาร 12 ตรง ๆ เป็นอีก convention หนึ่งหรือเป็นค่าประมาณ ต้องไม่สลับใช้โดยไม่ระบุ

def cppi_path(risky_returns, start=1000.0, floor_fraction=0.8,
              multiplier=3.0, cash_annual=0.0, periods_per_year=12,
              drawdown=None):
    risky = np.asarray(risky_returns, dtype=float)
    if risky.ndim != 1 or not np.isfinite(risky).all() or (risky < -1).any():
        raise ValueError("Use a finite 1D array of simple returns, all >= -1.")
    if start <= 0 or not 0 <= floor_fraction <= 1 or multiplier < 0:
        raise ValueError("Check start, floor_fraction and multiplier.")
    if cash_annual <= -1 or periods_per_year <= 0:
        raise ValueError("Check cash_annual and periods_per_year.")
    if drawdown is not None and not 0 <= drawdown <= 1:
        raise ValueError("drawdown must be between 0 and 1.")

    cash_return = (1 + cash_annual)**(1 / periods_per_year) - 1
    wealth = float(start)
    peak = float(start)
    floor = start * floor_fraction if drawdown is None else start * (1 - drawdown)
    records = [{"step": 0, "wealth": wealth, "floor": floor,
                "floor_before": np.nan, "risky_before": np.nan,
                "weight_before": np.nan}]

    for step, risky_return in enumerate(risky, start=1):
        if drawdown is not None:
            floor = (1 - drawdown) * peak
        floor_before = floor
        cushion = wealth - floor_before
        risky_amount = np.clip(multiplier * cushion, 0, wealth)
        risky_weight = risky_amount / wealth if wealth > 0 else 0.0
        safe_amount = wealth - risky_amount
        wealth = risky_amount * (1 + risky_return) + safe_amount * (1 + cash_return)
        peak = max(peak, wealth)
        if drawdown is not None:
            floor = (1 - drawdown) * peak
        records.append({"step": step, "wealth": wealth, "floor": floor,
                        "floor_before": floor_before,
                        "risky_before": risky_amount,
                        "weight_before": risky_weight})

    return pd.DataFrame(records).set_index("step")

cppi_fixed = cppi_path(cppi_risky_returns)
print(cppi_fixed.round(4))

ไม่ต้องจำทั้งฟังก์ชันในรอบเดียว อ่านตามลำดับการเงินได้ดังนี้

  1. np.asarray(..., dtype=float) แปลง list หรือ array ที่รับมาเป็นตัวเลขทศนิยมหนึ่งชุด ndim != 1 ตรวจว่าเผลอส่งตารางหลายคอลัมน์มาหรือไม่ ส่วน np.isfinite กันค่าขาดหรือค่าที่ไม่สิ้นสุด
  2. records เก็บผลแต่ละจุดเวลา โดยเริ่มจากแถว 0 ซึ่งเป็นเงินตั้งต้น แถวนี้ไม่มีผลตอบแทนงวดก่อน จึงใช้ NaN ในคอลัมน์วงเงินก่อนงวด
  3. for ... in enumerate(..., start=1) อ่านผลตอบแทนทีละค่า พร้อมเลขงวดเริ่มที่ 1 ต้องคำนวณตามเวลา เพราะวงเงินรอบใหม่ขึ้นอยู่กับมูลค่าที่ได้จากรอบก่อน
  4. np.clip(..., 0, wealth) จำกัดเงินเสี่ยงไม่ให้ต่ำกว่า 0 หรือสูงกว่ามูลค่าพอร์ต จากนั้นคำนวณเงินปลายงวด แล้วจึงเก็บผล
  5. return ส่งตารางออกจากฟังก์ชัน คอลัมน์ wealth และ floor คือสถานะหลังจบงวด ส่วนชื่อที่ลงท้าย _before คือค่าที่ใช้ก่อนรับผลตอบแทนงวดนั้น

พารามิเตอร์ drawdown=None หมายถึงยังไม่เปิดกติกายก floor ตามจุดสูงสุด เราจะใช้ส่วนนี้ในหัวข้อถัดไป หากไม่ส่งค่าบางตัวตอนเรียก ฟังก์ชันจะใช้ค่าหลังเครื่องหมาย = ในบรรทัดประกาศ เช่น multiplier=3.0

เก็บเงินตั้งต้นไว้ เพื่อไม่ทำผลตอบแทนงวดแรกหาย

เพราะตารางมีแถวเริ่มต้น 1,000 บาท .pct_change() จึงคำนวณเดือนแรกเป็น 1,048/1,000−1=4.8%1{,}048/1{,}000-1=4.8\% ได้ หากเก็บเฉพาะปลายเดือนแล้วใช้คำสั่งเดียวกัน จะไม่เหลือฐานสำหรับคำนวณผลตอบแทนเดือนแรก

cppi_portfolio_returns = cppi_fixed["wealth"].pct_change().dropna()
cppi_buy_hold = cppi_start * np.r_[1.0, np.cumprod(1 + cppi_risky_returns)]
cppi_comparison = pd.DataFrame({
    "CPPI": cppi_fixed["wealth"],
    "Risky buy and hold": cppi_buy_hold,
    "Fixed floor": cppi_start * cppi_floor_fraction
})
print("Portfolio returns (%):")
print((cppi_portfolio_returns * 100).round(4))
print(cppi_comparison.round(4))

np.cumprod(1 + returns) คูณ gross return สะสม np.r_[1.0, ...] เติมค่า 1 ด้านหน้าเพื่อแทนจุดเริ่มต้น จากนั้นคูณเงินตั้งต้นทั้งชุด ทั้งสองกลยุทธ์จึงเริ่มจาก 1,000 บาทและมีเจ็ดจุดเวลาเหมือนกัน

เมื่อจบตัวอย่าง CPPI มีประมาณ 868.17 บาท ส่วนการถือสินทรัพย์เสี่ยงเต็มพอร์ตตลอดมีประมาณ 864.37 บาท ผลจากหกเดือนชุดนี้ยังใช้จัดอันดับว่ากลยุทธ์ใดดีกว่าเสมอไม่ได้ โดยเฉพาะ CPPI ที่เปลี่ยนวงเงินตามลำดับเหตุการณ์ บท Monte Carlo จะเปรียบเทียบหลายเส้นทางภายใต้สมมติฐานที่ระบุไว้

มูลค่า CPPI เทียบกับการถือสินทรัพย์เสี่ยงและ floor ตลอดหกเดือนสมมติ
ใช้ผลตอบแทนหกเดือนเดียวกับตารางและโค้ด เส้นเชื่อมจุดสิ้นเดือน ไม่ได้แสดงราคาทุกขณะระหว่างเดือน เงินสดให้ผลตอบแทนศูนย์และไม่มีต้นทุน การที่ CPPI อยู่เหนือ floor บนเส้นทางนี้ยังไม่ครอบคลุมกรณีขาดทุนแรงในหัวข้อถัดไป

Gap Risk: ราคาข้าม floor ก่อนถึงเวลาปรับพอร์ต

กลับไปเริ่มใหม่ที่ 1,000 บาท floor 800 บาท ตัวคูณ 3 เงินเสี่ยงจึงเป็น 600 บาท ถ้าระหว่างจุดปรับพอร์ตสินทรัพย์เสี่ยงลง 40% เงินเสี่ยงจะเหลือ 360 บาท บวกเงินสด 400 บาท พอร์ตเหลือ 760 บาท ต่ำกว่า floor 40 บาท

cppi_gap = cppi_path([-0.40])
print(cppi_gap[["wealth", "floor", "risky_before"]])
print("Shortfall:", max(800.0 - cppi_gap.loc[1, "wealth"], 0.0))

รอบถัดไปกติกาจะเห็น cushion ติดลบและให้วงเงินเสี่ยงเป็นศูนย์ แต่นั่นไม่ได้ย้อนกลับไปคืนเงินที่เสียไปแล้ว Gap risk คือความเสี่ยงที่ความเปลี่ยนแปลงระหว่างจุดปรับพอร์ตทำให้การลดวงเงินเกิดไม่ทัน ไม่จำเป็นต้องเป็นการกระโดดของราคาในแบบจำลองเสมอไป ราคาที่เปลี่ยนอย่างต่อเนื่องก็ลงมากได้ระหว่างจุดสังเกตที่ห่างกัน

ในกรณี floor คงที่ เงินสดให้ 0% ไม่มีต้นทุน และยังไม่ชนขอบวงเงิน 100% เราแทน E=mCE=mC ลงในสูตรได้

Ct+1=Ct+EtRt+1=Ct(1+mRt+1)C_{t+1}=C_t+E_tR_{t+1} =C_t(1+mR_{t+1})

เมื่อ Ct>0C_t>0 พอร์ตจะต่ำกว่า floor หาก Rt+1<−1/mR_{t+1}<-1/m สำหรับ m=3m=3 คือขาดทุนเกิน 33.33% ในงวดนั้น ถ้าลงพอดีหนึ่งในสาม พอร์ตจะสัมผัส floor สูตรนี้ขึ้นกับเงื่อนไขที่เพิ่งระบุ เมื่อวงเงินจริงถูกจำกัด อัตราดอกเบี้ยเปลี่ยน หรือ floor เคลื่อนตามภาระผูกพัน ต้องกลับไปคำนวณจากเงินเสี่ยงและส่วนปลอดภัยจริง

ตัวอย่าง m=1m=1 ลงทุนเสี่ยงเท่ากับ cushion 200 บาทและกันเงินสด 800 บาทไว้ ภายใต้ floor คงที่ เงินสดไม่ลดมูลค่า ไม่มีต้นทุน และสินทรัพย์เสี่ยงขาดทุนได้ไม่เกิน 100% แม้สินทรัพย์เสี่ยงเหลือศูนย์ พอร์ตก็ยังมี 800 บาท นี่เป็นผลจากสมมติฐานเฉพาะของตัวอย่าง ไม่ใช่คำรับรองสำหรับสินทรัพย์ปลอดภัยหรือ floor ทุกแบบ

การลดตัวคูณทำให้เริ่มต้นเสี่ยงน้อยลง ส่วนการปรับพอร์ตถี่ขึ้นช่วยลดช่วงเวลาที่วงเงินค้างอยู่ แต่การซื้อขายถี่ขึ้นมีต้นทุนและยังอาจมีช่องว่างราคา หลักประกันในแบบจำลองอุดมคติที่ซื้อขายต่อเนื่องจึงต้องแยกจากผลของโค้ดที่ปรับเป็นงวด ดู Gap Risk และบทคอร์ส Designing and calibrating CPPI strategies

ใช้โค้ดเดิมกับ Floor แบบ TIPP

บท TIPP อธิบายการยก Floor ตามจุดสูงสุด พร้อมเปรียบเทียบกับ CPPI ที่คง Floor ไว้ ในที่นี้เราจะตรวจว่าส่วน drawdown ของฟังก์ชันทำตามกฎนั้นหรือไม่ โดยใช้ Floor เท่ากับ 80% ของมูลค่าพอร์ตสูงสุดที่สังเกตแล้ว หรือ Ft=0.8PtF_t=0.8P_t เมื่อ PtP_t คือจุดสูงสุดของพอร์ตนี้เอง

เรียก drawdown=0.20 กับผลตอบแทนหกเดือนชุดเดิม แล้วใช้ .cummax() คำนวณจุดสูงสุดจากตารางผลลัพธ์แยกอีกครั้ง หลังเดือนแรกพอร์ตมี 1,048 บาท จึงต้องได้ Floor 838.40 บาท คำสั่ง assert ท้ายช่องตรวจ Floor ทุกแถวกับสูตรนี้

cppi_trailing = cppi_path(cppi_risky_returns, drawdown=0.20)
cppi_trailing["peak"] = cppi_trailing["wealth"].cummax()
cppi_trailing["drawdown"] = cppi_trailing["wealth"] / cppi_trailing["peak"] - 1
print(cppi_trailing[["wealth", "floor", "risky_before", "drawdown"]].round(4))
assert np.allclose(cppi_trailing["floor"], 0.8 * cppi_trailing["peak"])

พอร์ตนี้จบที่ประมาณ 896.02 บาท และ drawdown ลึกสุดในเจ็ดจุดเวลาประมาณ −16.22% การตั้งเป้าหมาย 20% ไม่ได้ป้องกันการทะลุระหว่างจุดปรับพอร์ต ตัวอย่าง −40% จากหัวข้อก่อนยังทำให้ floor เริ่มต้น 800 บาทถูกทะลุได้

ในฟังก์ชัน เราอัปเดต peak หลังคำนวณมูลค่าปลายงวด และใช้มันกำหนด Floor สำหรับรอบถัดไป จึงไม่ใช้จุดสูงสุดจากอนาคต

เมื่อใช้ drawdown ฟังก์ชันนี้จะให้ floor เริ่มต้นจาก (1-drawdown)*start และใช้กติกา peak แทน floor_fraction ตลอดเส้นทาง จึงไม่ควรส่งสองเป้าหมายที่ขัดกันแล้วเข้าใจว่าฟังก์ชันกำลังบังคับใช้ทั้งคู่

ถ้ากำหนดเพดานความมั่งคั่งด้วย

คอร์สยังต่อยอดไปถึงการลดความเสี่ยงเมื่อพอร์ตเข้าใกล้เพดานที่ตั้งไว้ สมมติ floor 800 บาท และเพดาน U=1,400U=1{,}400 บาท รูปแบบตัวอย่างที่ใช้ตัวคูณเท่ากันทั้งสองด้านอาจกำหนดวงเงินเป็น

E(V)=min⁡(m(V−F),m(U−V))E(V)=\min\bigl(m(V-F),m(U-V)\bigr)

เมื่อ F≤V≤UF\leq V\leq U แล้วจำกัดวงเงินจริงให้อยู่ในช่วง 0 ถึง VV อีกครั้ง ด้วย m=3m=3 ที่ V=900V=900 จะเสี่ยง 300 บาท ที่ V=1,100V=1{,}100 เสี่ยง 900 บาท และที่ V=1,300V=1{,}300 กลับมาเสี่ยง 300 บาท จุดที่สองสูตรเท่ากันคือ (F+U)/2=1,100(F+U)/2=1{,}100 บาท

ส่วนนี้เป็นการแสดงกติกาเพิ่มเติม ไม่ได้รวมอยู่ใน cppi_path ด้านบน และเมื่อปรับพอร์ตเป็นงวดก็อาจทะลุขอบทั้งสองด้านได้เช่นกัน อย่าอ่าน “cap” ว่าเป็นการรับรองผลตอบแทนสูงสุดของการลงทุนจริง

แบบฝึกหัด: คำนวณก่อนรัน

คำถามต่อไปนี้เป็นโจทย์ฝึกที่สร้างขึ้นใหม่ ใช้เงินสดผลตอบแทน 0% และไม่มีต้นทุน เว้นแต่ระบุเป็นอย่างอื่น

1. พอร์ต 1,200 บาท floor 900 บาท และ m=2m=2 จะถือเงินเสี่ยงกี่บาท? หากสินทรัพย์เสี่ยงให้ −10% พอร์ตจะเหลือเท่าไร และวงเงินเสี่ยงงวดถัดไปเป็นเท่าไร?

เปิดคำตอบข้อ 1

cushion 300 บาท จึงเสี่ยง 600 บาทและถือเงินสด 600 บาท หลังขาดทุนได้ 600(0.9)+600=1,140600(0.9)+600=1{,}140 บาท cushion ใหม่ 240 บาท จึงเสี่ยง 480 บาทในงวดถัดไป

2. พอร์ต 1,000 บาท floor 800 บาท ตัวคูณ 8 สูตรดิบให้เสี่ยง 1,600 บาท แต่ฟังก์ชันในบทจะใช้เท่าไร?

เปิดคำตอบข้อ 2

ใช้ 1,000 บาท เพราะจำกัดวงเงินเสี่ยงไม่ให้เกินมูลค่าพอร์ต จึงถือสินทรัพย์เสี่ยง 100% และไม่กู้เพิ่ม ในกรณีนี้จะใช้เกณฑ์ −1/m-1/m จากสูตรที่ยังไม่ชนขอบโดยตรงไม่ได้ การลงเกิน 20% จากวงเงินนี้ทำให้ต่ำกว่า 800 บาทแล้ว

3. ใช้ drawdown เป้าหมาย 15% พอร์ตเริ่ม 1,000 บาท โตไป 1,200 บาท แล้วลดมา 1,100 บาท floor หลังแต่ละจุดเป็นเท่าไร?

เปิดคำตอบข้อ 3

เริ่มที่ 850 บาท จากนั้นยกเป็น 1,020 บาท เมื่อพอร์ตลดมา 1,100 บาท peak ยังเป็น 1,200 บาท floor จึงยังเป็น 1,020 บาท ไม่ลดกลับไปเป็น 935 บาท

4. ถ้าจำลองหนึ่งพันเส้นทางแล้วไม่มีเส้นใดทะลุ floor พอจะสรุปได้หรือไม่ว่ากลยุทธ์ไม่มี gap risk?

เปิดคำตอบข้อ 4

ยังสรุปไม่ได้ เหตุการณ์อาจมีโอกาสต่ำจนไม่ปรากฏในตัวอย่าง และแบบจำลองอาจไม่สร้างเหตุการณ์บางประเภทเลย ต้องดูทั้งจำนวนเส้นทาง ความถี่ปรับพอร์ต การแจกแจง สมมติฐานสินทรัพย์ปลอดภัย และต้นทุน บท Monte Carlo จะวัดผลจากหลายเส้นทางโดยแยกความไม่แน่นอนเหล่านี้

เรียนต่อและตรวจเทียบกับ Notebook ของคอร์ส

หัวข้อในหน้านี้ใช้ CPPI Part 1, Part 2, Lab CPPI Part 1 และ Part 2 เป็นเส้นทางประกอบ และตรวจแนวคิดกับ lab_119 ของคอร์ส ตัวอย่างและฟังก์ชันในหน้านี้เขียนขึ้นใหม่

เมื่อตรวจเทียบโค้ด ให้ดู convention ก่อนเปรียบเทียบผล โค้ดในหน้านี้มีแถวเงินตั้งต้น คำนวณเงินสดจากอัตราต่อปีแบบทบต้น และเก็บทั้ง floor ก่อนงวดกับหลังงวด เพื่อให้ตรวจลำดับเวลาได้ ผลจึงอาจต่างจากการตัดแถวแรกหรือใช้อัตราต่อปีหาร 12

ขั้นถัดไปคือ Monte Carlo: สร้างหลายเส้นทางและวัด Shortfall ซึ่งจะใช้กติกา CPPI เดิมกับผลตอบแทนที่จำลองขึ้น โดยรายงานว่าผลขึ้นกับแบบจำลองอย่างไร

ค้นหาในหนังสือ

ปิด
คำค้นหา

© 2023 by QuantCorner, Thailand.

bottom of page