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

How to Calculate return

เงิน 1 ล้านบาท ปีแรกกำไร 100% ปีที่สองขาดทุน 50% สรุปแล้วเราได้ผลตอบแทนเท่าไร?

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

เรียบเรียงใหม่ประกอบหัวข้อ Analysing returns ของคอร์ส Introduction to Portfolio Construction and Analysis with Python โดย EDHEC Business School โดยอ่าน Transcript และตรวจ Lab 101–104 เพิ่มคำอธิบายพื้นฐาน ตัวอย่าง และแบบฝึกหัดของบทนี้เอง บทนี้สอนผลตอบแทน ความผันผวน Sharpe ratio และ Drawdown ส่วนการแจกแจงและความเสียหายปลายหางอ่านต่อใน เมื่อผลตอบแทนไม่เป็น Normal และ Log return อยู่ในส่วนอ่านต่อท้ายหน้านี้

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

อ่านทีละช่วง ไม่ต้องจำทุกสูตรในครั้งเดียว

ช่วงเรียน คำถามที่ควรตอบได้เมื่อจบ
เปอร์เซ็นต์และผลตอบแทน กำไร 20 บาทต่างจากผลตอบแทน 20% อย่างไร?
เงินทบต้นและค่าเฉลี่ย ทำไมบวกเปอร์เซ็นต์รายเดือนจึงไม่เท่ากับกำไรสะสม?
เริ่ม Python และ pandas แต่ละแถวคืออะไร และโค้ดกำลังหารราคาไหนด้วยราคาไหน?
ความผันผวนและความเสี่ยง ผลตอบแทนเท่ากันแล้ว ทำไมประสบการณ์ของผู้ลงทุนยังต่างกัน?
อ่านต่อ: Log return เราเปลี่ยนการคูณข้ามเวลาเป็นการบวกได้อย่างไร?

รอบแรกอ่านและคำนวณด้วยมือก่อน ข้ามกรอบโค้ดได้ เมื่อเข้าใจความหมายแล้วค่อยกลับมารันทีละช่อง ช่วงท้ายของบทมีคำถามพร้อมเฉลย และมีตัวทดลองเงินทบต้นให้ปรับตัวเลขโดยไม่ต้องเขียนโค้ด

ก่อนใช้สูตร: บาท เปอร์เซ็นต์ และช่วงเวลา

ซื้อสินทรัพย์ 80 บาท ขาย 100 บาท ได้กำไร 20 บาท แต่ถ้าซื้ออีกสินทรัพย์ที่ 400 บาทแล้วขาย 420 บาท กำไร 20 บาทเท่ากันไม่ได้หมายถึงผลการลงทุนเท่ากัน เพราะเงินที่ใช้เริ่มต้นต่างกัน ตัวแรกให้ผลตอบแทน 25% ส่วนตัวที่สองให้ 5%

Return หรือผลตอบแทน ในบทนี้หมายถึงกำไรหรือขาดทุนเมื่อเทียบกับเงินที่ใช้เริ่มต้น คำว่าเปอร์เซ็นต์หมายถึง “ต่อหนึ่งร้อย” ดังนั้นกำไร 5% คือเงินต้นทุก 100 บาทได้กำไร 5 บาท และเงินต้น 1,000 บาทได้กำไร 50 บาท

เขียนเป็นเปอร์เซ็นต์ เขียนเป็นทศนิยมเพื่อคำนวณ เงินต้น 100 บาทกลายเป็น ตัวคูณเงิน 1 + R
+5% 0.05 105 บาท 1.05
0% 0.00 100 บาท 1.00
−5% −0.05 95 บาท 0.95

จากเปอร์เซ็นต์ไปเป็นทศนิยมให้หาร 100 จากทศนิยมไปแสดงเป็นเปอร์เซ็นต์ให้คูณ 100 แต่ 1.05 ไม่ใช่ผลตอบแทน 105% ในคอลัมน์สุดท้าย: มันนับเงินต้น 1 เท่ารวมกับกำไร 0.05 เท่าแล้ว

ต้องบอกด้วยว่าถือ หนึ่งช่วง นานเท่าไร เช่น หนึ่งวัน หนึ่งเดือน หรือหนึ่งปี ผลตอบแทน 5% ในหนึ่งเดือนไม่ใช่ผลตอบแทน 5% ในหนึ่งปี เวลาเห็นตัวห้อย tt ให้อ่านว่า “ช่วงปัจจุบัน” และ t−1t-1 คือ “ช่วงก่อนหน้า” ไม่จำเป็นต้องเริ่มจากความหมายที่ซับซ้อนกว่านี้

Simple return: ได้หรือเสียเท่าไรเมื่อเทียบกับเงินต้น

ให้ Pt−1P_{t-1} เป็นราคาต้นช่วง และ PtP_t เป็นราคาปลายช่วง หน่วยเป็นบาทต่อหุ้นเหมือนกัน เมื่อไม่มีปันผล Simple return คือ

Rt=Pt−Pt−1Pt−1=PtPt−1−1.R_t=\frac{P_t-P_{t-1}}{P_{t-1}}=\frac{P_t}{P_{t-1}}-1.

ตัว tt นับช่วงเวลาที่เลือก เช่น วัน เดือน หรือปี การเปรียบเทียบ 5% ต่อเดือนกับ 5% ต่อปีต้องแปลงให้เป็นช่วงเวลาเดียวกันก่อน

ราคา 80 → 100 ให้ R=0.25R=0.25 หรือ 25% ตัวเลข 0.25 เป็นทศนิยมที่ใช้คำนวณ ส่วน 25% เป็นรูปแสดงผล ห้ามใส่ 25 ลงในสูตรที่ต้องการ 0.25

อีกค่าที่ใช้บ่อยคือ Gross return หรือ Gt=1+RtG_t=1+R_t ซึ่งเป็นตัวคูณเงินลงทุน ตัวอย่างนี้ได้ G=1.25G=1.25 หมายถึงเงินต้น 1 บาทกลายเป็น 1.25 บาท หรือได้กำไร 25%

ถ้ารับปันผล DtD_t ต่อหุ้นเมื่อสิ้นช่วง ผลตอบแทนรวมก่อนภาษีและค่าใช้จ่ายคือ

Rttotal=Pt+Dt−Pt−1Pt−1.R_t^{\mathrm{total}}=\frac{P_t+D_t-P_{t-1}}{P_{t-1}}.

เช่น ซื้อ 80 บาท สิ้นช่วงเหลือ 79 บาท แต่รับปันผล 3 บาท ผลตอบแทนจากราคาเท่ากับ −1.25% ขณะที่ผลตอบแทนรวมเท่ากับ 2.5% หากใช้ราคาหรือดัชนีที่ปรับปันผลไว้แล้ว ต้องตรวจวิธีปรับก่อนเพื่อไม่บวกปันผลซ้ำ การเชื่อมผลตอบแทนรวมหลายช่วงด้วยการคูณสะท้อนการนำเงินที่ได้รับกลับไปลงทุนตามวิธีสร้างชุดข้อมูลนั้น

หลายช่วงเวลา: ทำไมบวกเปอร์เซ็นต์ตรง ๆ ไม่ได้

ลองเริ่มจากเงินลงทุน 1,000,000 บาท ปีแรกกำไร 100% และปีที่สองขาดทุน 50% ตลอดสองปีไม่มีการฝากถอนเงินเพิ่ม และสมมติว่าไม่มีปันผล ภาษี หรือค่าใช้จ่าย

การบวก 100%−50%=50%100\%-50\%=50\% จะให้คำตอบว่าได้กำไรครึ่งล้าน ลองเทียบกับจำนวนเงินจริงทีละปี

ช่วงเวลา เงินต้นช่วง Simple return กำไรหรือขาดทุนเป็นบาท เงินปลายช่วง
ปีที่ 1 1,000,000 +100% +1,000,000 2,000,000
ปีที่ 2 2,000,000 −50% −1,000,000 1,000,000

ปีแรกกำไร 100% ของเงิน 1 ล้านบาท จึงได้กำไร 1 ล้านบาท ส่วนปีที่สองขาดทุน 50% ของเงิน 2 ล้านบาท ก็เสีย 1 ล้านบาทเท่ากัน สุดท้ายเงินกลับมาเท่าทุน

1,000,000 → +100%  2,000,000 → −50%  1,000,000.1{,}000{,}000\ \xrightarrow{\,+100\%\,}\ 2{,}000{,}000 \ \xrightarrow{\,-50\%\,}\ 1{,}000{,}000.
เงินเริ่มต้น 1 ล้านบาท เพิ่มเป็น 2 ล้านบาทเมื่อได้กำไร 100% แล้วกลับมา 1 ล้านบาทเมื่อขาดทุน 50% ผลตอบแทนสะสม 0%
แท่งแสดงมูลค่าเงินลงทุนโดยเริ่มแกนที่ศูนย์ ปีแรกได้กำไร 1 ล้านบาท ปีที่สองเสีย 1 ล้านบาท · ตัวอย่างสมมติ · ดาวน์โหลดภาพ SVG

ผลตอบแทนสะสมทั้งสองปีคือ

R0:2=1,000,0001,000,000−1=0.R_{0:2}=\frac{1{,}000{,}000}{1{,}000{,}000}-1=0.

ใช้ตัวคูณเงินลงทุนเชื่อมแต่ละปี

กำไร 100% หมายถึงเงินกลายเป็น 2 เท่า ส่วนขาดทุน 50% หมายถึงเงินเหลือ 0.5 เท่า ตัวคูณของทั้งสองปีจึงเป็น

(1+R1)(1+R2)=2×0.5=1.(1+R_1)(1+R_2)=2\times0.5=1.

เงินปลายทางเท่ากับ 1 เท่าของเงินตั้งต้น เมื่อหักเงินต้นหนึ่งเท่าออก ผลตอบแทนสะสมจึงเป็น 1−1=01-1=0 สำหรับ TT ช่วงเวลาที่ต่อกัน หลักเดียวกันเขียนได้ว่า

R0:T=∏t=1T(1+Rt)−1.R_{0:T}=\prod_{t=1}^{T}(1+R_t)-1.

เครื่องหมาย ∏\prod หมายถึงนำทุกพจน์มาคูณกัน สูตรนี้คูณ gross returns ของทุกช่วงเข้าด้วยกัน แล้วลบหนึ่งเพื่อหาผลตอบแทนสะสม

ค่าเฉลี่ย: ผลตอบแทนเฉลี่ยกับการเติบโตของเงินเป็นคนละคำถาม

ค่าเฉลี่ยเลขคณิต คือบวกผลตอบแทนทุกช่วงแล้วหารด้วยจำนวนช่วง TT เครื่องหมาย ∑\sum อ่านว่า “นำมาบวกกัน” สำหรับ simple returns เขียนได้ว่า

Rˉ=1T∑t=1TRt.\bar R=\frac{1}{T}\sum_{t=1}^{T}R_t.

ในตัวอย่างปีแรก +100% ปีที่สอง −50% ค่าเฉลี่ยเลขคณิตเท่ากับ (100%−50%)/2=25%(100\%-50\%)/2=25\% ต่อปี แต่เงินปลายทางยังเท่ากับเงินต้น 1 ล้านบาท การเฉลี่ยเปอร์เซ็นต์จึงไม่ได้บอกว่าเงินโตทบต้นปีละเท่าไร

ค่าเฉลี่ยเรขาคณิต ตอบว่า ถ้าโตด้วยอัตราคงที่ทุกปี ต้องใช้อัตราเท่าไรจึงได้เงินปลายทางเท่าเดิม

g=[∏t=1T(1+Rt)]1/T−1.g=\left[\prod_{t=1}^{T}(1+R_t)\right]^{1/T}-1 .

กรณีนี้ g=2×0.5−1=0%g=\sqrt{2\times0.5}-1=0\% ต่อปี

ถ้าวัดเป็นปี ใช้ CAGR (compound annual growth rate) โดยให้ YY เป็นจำนวนปีที่ถือ และ VV เป็นมูลค่าเงินลงทุนที่ไม่มีฝากถอนภายนอก

CAGR=(VTV0)1/Y−1.\mathrm{CAGR}=\left(\frac{V_T}{V_0}\right)^{1/Y}-1.

เงิน 1,000 → 1,210 บาทในสองปีมี CAGR 10% ต่อปี ไม่ใช่ 21% ÷ 2 = 10.5% และ CAGR ไม่ได้บอกว่าแต่ละปีได้ 10% จริง การทำข้อมูลระยะสั้นให้เป็นอัตราต่อปีเป็นเพียงการแปลงหน่วยด้วยการทบต้น ไม่ใช่การพยากรณ์ว่าจะทำซ้ำได้ทั้งปี

ผลตอบแทนเฉลี่ยเท่ากัน แต่เงินปลายทางต่างกันได้

สมมติพอร์ต C ได้ +2% ทั้งสองเดือน ส่วน D ได้ +12% แล้ว −8% ทั้งคู่มีค่าเฉลี่ยเลขคณิต 2% ต่อเดือนเหมือนกัน แต่ถ้าเริ่ม 100 บาท C จบที่ 100(1.02)(1.02)=104.04100(1.02)(1.02)=104.04 บาท ส่วน D จบที่ 100(1.12)(0.92)=103.04100(1.12)(0.92)=103.04 บาท

ผลตอบแทนสะสมจึงเป็น 4.04% กับ 3.04% ตามลำดับ การรู้เพียงค่าเฉลี่ยทำให้มองไม่เห็นทั้งเงินปลายทางและความแกว่งระหว่างเดือน เราจะกลับมาวัดความแกว่งนี้ในหัวข้อความผันผวน

ผลตอบแทนที่ต้องได้เพื่อกลับมาเท่าทุน

เมื่อราคาลดลงแล้วกลับขึ้นมาเท่าเดิม simple return ของขาลงกับขาขึ้นมีขนาดต่างกัน Motty Shai อธิบายความต่างนี้ไว้ใน เอกสารต้นทาง

ใช้ตัวอย่างสมมติ 80 → 60 → 80 บาท ขาลงคือ −25% แต่ขากลับต้องได้ 80/60−1=33.3333%80/60-1=33.3333\%

ถ้าขาดทุนเป็นสัดส่วน LL ของเงินเริ่มต้น โดย 0≤L<10\leq L<1 ผลตอบแทนที่ต้องได้จากเงินที่เหลือเพื่อกลับไปเท่าทุนคือ

Rrecovery=11−L−1=L1−L.R_{\mathrm{recovery}}=\frac{1}{1-L}-1=\frac{L}{1-L}.
ขาดทุนจากเงินเริ่มต้น ต้องได้กำไรจากเงินที่เหลือ
10% 11.11%
25% 33.33%
50% 100%
75% 300%

ฐานเงินที่ลดลงทำให้ต้องได้กำไรในอัตราที่สูงขึ้นเพื่อกลับมาเท่าทุน Shai เรียกผลนี้ว่า inverse compound interest ซึ่งคำนวณได้จากสูตรทบต้นข้างต้น

สำหรับการซื้อสินทรัพย์ด้วยเงินที่มี โดยไม่กู้ยืมและไม่มีภาระเพิ่มเติม ผลตอบแทนจากราคาต่ำสุดคือ −100% เมื่อราคาลงเป็นศูนย์ เงินก้อนเดิมจึงไม่สามารถฟื้นกลับได้ด้วยการคูณอัตราผลตอบแทนเพียงอย่างเดียว

รวมผลตอบแทนหลายสินทรัพย์ในช่วงเดียวกัน

ให้ wi,t−1w_{i,t-1} เป็นน้ำหนักสินทรัพย์ ii ตามมูลค่าพอร์ตต้นช่วง โดยน้ำหนักรวมเท่ากับหนึ่ง ถ้าไม่มีฝากถอนหรือซื้อขายระหว่างช่วง และรวมกระแสเงินสดกับค่าใช้จ่ายในผลตอบแทนอย่างสอดคล้องกัน

Rp,t=∑i=1Nwi,t−1Ri,t.R_{p,t}=\sum_{i=1}^{N}w_{i,t-1}R_{i,t}.

สมมติพอร์ต 1,000 บาท ลงทุน A 600 บาท และ B 400 บาท ในเดือนเดียวกัน A ได้ +20% ส่วน B ได้ −10%

สินทรัพย์ มูลค่าต้นเดือน ผลตอบแทน มูลค่าปลายเดือน
A 600 +20% 720
B 400 −10% 360
รวม 1,000 +8% 1,080

ผลตอบแทนพอร์ตเท่ากับ 0.6(0.20)+0.4(−0.10)=0.080.6(0.20)+0.4(-0.10)=0.08

เมื่อขึ้นเดือนใหม่ น้ำหนัก A กลายเป็น 720/1080=2/3720/1080=2/3 และ B เป็น 1/31/3 ถ้าไม่ได้ปรับพอร์ต การคงน้ำหนัก 60/40 ในทุกเดือนเท่ากับสมมติว่าปรับพอร์ตกลับสู่น้ำหนักเป้าหมายทุกต้นเดือน ต้องนับต้นทุนการปรับด้วยเมื่อนำไปใช้งานจริง

การรวมข้ามเวลาใช้การคูณ gross returns ของพอร์ตเดียวกัน ส่วนการรวมสินทรัพย์ในช่วงเดียวกันใช้ simple returns ถ่วงด้วยน้ำหนักต้นช่วง

เริ่มคำนวณด้วย Python: หนึ่งบรรทัดควรตอบได้ว่ากำลังหาอะไร

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

เปิด Notebook และรันจากบนลงล่าง

Python เป็นภาษาที่เราใช้เขียนคำสั่ง ส่วน Jupyter Notebook เป็นสมุดทำงานที่วางคำอธิบาย โค้ด และผลลัพธ์ไว้ด้วยกัน ไฟล์ลงท้าย .ipynb เปิดใน Jupyter ได้ ภายในแบ่งเป็นช่องหรือ cell: ช่องแบบ Markdown ใช้เขียนเนื้อหา ส่วนช่องแบบ Code ใช้รัน Python คลิกช่อง Code แล้วกด Shift + Enter เพื่อรันและไปช่องถัดไป คู่มือ Jupyter Notebook

เริ่มด้วย Notebook ใหม่ที่ใช้ Python แล้วคัดลอกโค้ดทีละกรอบในบทนี้ลงใน cell คนละช่อง รันเรียงจากบนลงล่าง เพราะช่องหลังอาจใช้ค่าที่ช่องก่อนสร้างไว้ ตัวประมวลผลที่เรียกว่า kernel จะจำค่าเหล่านั้นระหว่างทำงาน ถ้าแก้ราคาในช่องต้นทาง ต้องรันช่องนั้นและช่องที่คำนวณต่อจากมันใหม่ด้วย ก่อนเชื่อผลลัพธ์ครั้งสุดท้าย ให้ restart kernel แล้วรันทุกช่องตามลำดับ เพื่อยืนยันว่าไม่ได้อาศัยค่าจากการทดลองครั้งก่อน

ราคาสองตัวก็เริ่มได้แล้ว

ตัวแปรคือชื่อที่เราใช้เรียกค่าซ้ำ เช่น price_start หมายถึงราคาต้นเดือน เครื่องหมาย = ให้คำนวณด้านขวาแล้วเก็บไว้ในชื่อด้านซ้าย ส่วน / คือหาร และ print(...) ให้แสดงค่าที่อยู่ในวงเล็บ ลองเริ่มจากสินทรัพย์ A ที่ราคาเพิ่มจาก 100 เป็น 110 บาท

price_start = 100
price_end = 110
price_gain = price_end - price_start
simple_return = price_gain / price_start
print(price_gain)
print(simple_return)

ได้กำไรจากราคา 10 บาท และผลตอบแทน 0.1 หรือ 10% ถ้าใช้ price_end / price_start - 1 จะได้คำตอบเท่ากันทางคณิตศาสตร์ แต่บางครั้งหน้าจออาจแสดง 0.10000000000000009 เพราะคอมพิวเตอร์เก็บทศนิยมบางค่าได้เพียงค่าประมาณ ไม่ใช่ผลตอบแทนอีกชนิดหนึ่ง

ให้เก็บผลตอบแทนเป็นทศนิยมสำหรับคำนวณต่อ และจัดรูปแบบเฉพาะตอนแสดงผล เช่น print(f"{simple_return:.2%}") แสดง 10.00% โดย f"..." ใช้แทรกค่าลงในข้อความ ส่วน :.2% ให้แสดงเป็นเปอร์เซ็นต์ที่มีทศนิยมสองตำแหน่ง ไม่ต้องคูณ 100 ก่อนใช้รูปแบบนี้ อย่าเขียนทับค่าที่ใช้คำนวณด้วยข้อความ "10.00%" พื้นฐานตัวเลขและตัวแปรใน Python

จากตัวเลขหนึ่งค่าเป็น Series และตาราง DataFrame

เมื่อมีหลายเดือน เราต้องเก็บทั้งราคาและชื่อเดือน pandas เป็นชุดเครื่องมือเสริมของ Python สำหรับทำงานกับข้อมูลตาราง คำสั่ง import pandas as pd เปิดใช้ชุดเครื่องมือนี้และให้เรียกชื่อสั้นว่า pd หากขึ้น ModuleNotFoundError แปลว่า Python ที่ Notebook ใช้ยังไม่มี pandas ให้รัน %pip install pandas numpy ในช่อง Code หนึ่งครั้ง แล้ว restart kernel ก่อนรันตัวอย่างใหม่ คำสั่งนี้ติดตั้งเครื่องมือใน environment ของ kernel ไม่ใช่โค้ดคำนวณผลตอบแทน

Series เก็บข้อมูลหนึ่งชุดพร้อมป้ายกำกับ ส่วน DataFrame เป็นตารางหลายคอลัมน์ แต่ละแถวมีป้ายกำกับที่เรียกว่า index เพื่อให้รู้ว่าค่านั้นเป็นของช่วงใด ตัวอย่างนี้กำหนดว่าข้อความ "2026-01" หมายถึงราคาสิ้นเดือนมกราคม 2026 ตัวเลขในวงเล็บเหลี่ยม [...] คือรายการหรือ list และข้อความอยู่ในเครื่องหมายอัญประกาศ โครงสร้างข้อมูลของ pandas

import pandas as pd

months = ["2026-01", "2026-02", "2026-03", "2026-04"]
asset_a = pd.Series([100, 110, 99, 108.9], index=months)
print(asset_a)

Series นี้มีราคาสี่จุด แต่มีช่วงผลตอบแทนเพียงสามช่วง เพราะต้องใช้ราคาสองจุดติดกันเพื่อหาผลตอบแทนหนึ่งช่วง ลองเพิ่มสินทรัพย์ B แล้วเก็บคู่กันในตาราง เครื่องหมาย {...} สร้างข้อมูลแบบจับคู่ชื่อกับค่า เช่น "A": [...] ให้รายการราคาเป็นคอลัมน์ชื่อ A

prices = pd.DataFrame({
    "A": [100, 110, 99, 108.9],
    "B": [100, 102, 104.04, 106.1208]
}, index=months)
print(prices)
ราคาสิ้นเดือน (บาท) A B
2026-01 100.00 100.0000
2026-02 110.00 102.0000
2026-03 99.00 104.0400
2026-04 108.90 106.1208

prices["A"] เลือกคอลัมน์ A ออกมาเป็น Series ส่วนตัว prices ยังเป็น DataFrame ที่มีสองคอลัมน์ ในงานนี้แถวแทนเวลาและคอลัมน์แทนสินทรัพย์ จึงต้องคำนวณผลตอบแทนลงไปตามแถวของแต่ละคอลัมน์

ให้คอมพิวเตอร์หารราคาปัจจุบันด้วยราคาก่อนหน้า

ชื่อคำสั่งที่ตามหลังจุด เช่น .pct_change(...) เรียกว่า method หมายถึงการสั่งให้ข้อมูลก้อนนั้นทำงานบางอย่าง pct_change คำนวณการเปลี่ยนแปลงเทียบแถวก่อนหน้า ใช้ fill_method=None เพื่อไม่เติมราคาที่หายไปเอง และแม้ชื่อมีคำว่า percent ผลที่ได้ยังเป็นสัดส่วนทศนิยม เช่น 0.1 ไม่ใช่เลข 10 เอกสาร pct_change

returns = prices.pct_change(fill_method=None)
print(returns)
เดือนที่สิ้นสุดช่วง A B
2026-01 NaN NaN
2026-02 0.10 0.02
2026-03 −0.10 0.02
2026-04 0.10 0.02

เดือนกุมภาพันธ์ของ A คือ 110/100−1=0.10110/100-1=0.10 ส่วนเดือนมีนาคมคือ 99/110−1=−0.1099/110-1=-0.10 ตัวลบหมายถึงขาดทุน เดือนเมษายนกลับมาได้ 10% เมื่อเทียบกับราคา 99 บาท ไม่ได้หมายความว่าทุกรอบกำไร 10 บาท

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

ในตัวอย่างที่ตรวจแล้วว่าราคาครบ เราตัดเฉพาะแถวแรกออกด้วย .iloc[1:] โดย iloc เลือกตามตำแหน่ง เริ่มนับจาก 0 และ 1: หมายถึงตั้งแต่ตำแหน่ง 1 จนถึงท้ายชุดข้อมูล จึงเหลือผลตอบแทนสามเดือน

monthly_returns = returns.iloc[1:]
print(monthly_returns)
print((monthly_returns * 100).round(2))

บรรทัดสุดท้ายแสดง A เป็น 10.0, -10.0, 10.0 และ B เป็น 2.0, 2.0, 2.0 หน่วยเปอร์เซ็นต์ แต่ monthly_returns ยังเก็บ 0.10 และ 0.02 ตามเดิม .round(2) ปัดเพื่อให้อ่านง่ายเท่านั้น

การเขียน monthly_returns * 100 หมายถึงคูณทุกค่าด้วย 100 ในครั้งเดียว การใช้สูตรกับข้อมูลทั้งชุดเช่นนี้เรียกว่า vectorization ช่วยให้ไม่ต้องเขียนคำสั่งคำนวณแยกทีละแถว ขณะที่ Python list ธรรมดาไม่ได้รองรับการหารสอง list แบบจับคู่สมาชิกเหมือนตาราง pandas

ทำไมตัดตารางสองชุดมาหารกันแล้วจึงผิดได้

pandas จับคู่ข้อมูลด้วย ป้าย index ไม่ใช่ตำแหน่งที่มองเห็นอย่างเดียว สมมติเราเลือก A ตั้งแต่แถวที่สองเป็นตัวตั้ง แล้วเลือก A ยกเว้นแถวสุดท้ายเป็นตัวหาร ทั้งสองชุดยังพกป้ายเดือนเดิมมาด้วย

later = prices["A"].iloc[1:]
earlier = prices["A"].iloc[:-1]
wrong_returns = later / earlier - 1
print(wrong_returns)

ส่วน :-1 หมายถึงเลือกตั้งแต่ต้นจนถึงก่อนสมาชิกสุดท้าย ผลจะเป็น NaN ในมกราคมและเมษายน และเป็นศูนย์ในกุมภาพันธ์กับมีนาคม เพราะชื่อเดือนที่ตรงกันถูกจับมาหารกัน: กุมภาพันธ์กลายเป็น 110/110−1=0110/110-1=0 แทนที่จะเป็น 110/100−1110/100-1 ตัวแปร wrong_returns จึงตั้งชื่อไว้เตือนว่าเป็นตัวอย่างของคำตอบที่ผิด

ถ้าต้องการเห็นกลไกเดียวกับ pct_change ให้ใช้ prices / prices.shift(1) - 1 โดย .shift(1) เลื่อนค่าลงหนึ่งแถวและคงป้ายเดือนเดิมไว้ เมื่อหารกัน แถวเดือนกุมภาพันธ์จึงจับ 110 คู่กับ 100 ได้ถูกต้อง ในบทนี้เราใช้ pct_change(fill_method=None) เป็นคำสั่งหลักหลังเข้าใจว่ากำลังหารอะไร เอกสาร shift

ทบต้นด้วย .prod() แล้วตรวจด้วยราคาต้น–ปลาย

ผลตอบแทนของ A คือ +10%, −10%, +10% หากบวกกันจะได้ 10% แต่เงิน 100 บาทเปลี่ยนเป็น 110 → 99 → 108.90 บาท จึงมีกำไรสะสมเพียง 8.90% เราต้องเปลี่ยนแต่ละผลตอบแทนเป็นตัวคูณ 1+R1+R แล้วคูณเข้าด้วยกัน

.prod() ย่อจาก product และคูณค่าลงไปในแต่ละคอลัมน์ ตัวเลือก skipna=False ให้ค่าที่ขาดหายส่งต่อเป็น NaN แทนการข้ามไปเงียบ ๆ จึงไม่ทำให้ช่วงที่ไม่ทราบผลตอบแทนหายไปจากการทบต้น ในตัวอย่างนี้เราเอาแถวแรกที่คำนวณไม่ได้ออกอย่างมีเหตุผลแล้ว เอกสาร prod

growth_factors = 1 + monthly_returns
compound_return = growth_factors.prod(skipna=False) - 1
endpoint_return = prices.iloc[-1] / prices.iloc[0] - 1
print((compound_return * 100).round(4))
print((endpoint_return * 100).round(4))

สองวิธีให้ A 8.9000% และ B 6.1208% ตรงกัน iloc[-1] เลือกแถวสุดท้าย และ iloc[0] เลือกแถวแรก การตรวจด้วยราคาต้น–ปลายเหมาะกับข้อมูลชุดนี้เพราะไม่มีปันผลหรือกระแสเงินสดอื่นที่ต้องนับเพิ่ม

เงินต้น 10,000 บาทที่ถือ A จะกลายเป็น 10,890 บาท และถ้าถือ B จะกลายเป็น 10,612.08 บาท สังเกตว่า A จบด้วยกำไรมากกว่า แต่ระหว่างทางเคยลดจาก 110 เหลือ 99 บาท ขณะที่ B เพิ่มเดือนละ 2% สม่ำเสมอ ผลตอบแทนปลายทางจึงเป็นเพียงด้านหนึ่งของการเปรียบเทียบการลงทุน

แปลงผลตอบแทนสามเดือนเป็นอัตราต่อปี

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

Rann=G12/n−1=[∏t=1n(1+Rt)]12/n−1.R_{\text{ann}}=G^{12/n}-1 =\left[\prod_{t=1}^{n}(1+R_t)\right]^{12/n}-1.

len(monthly_returns) นับจำนวนแถว ซึ่งที่นี่ได้ 3 ส่วน ** เป็นเครื่องหมายยกกำลังใน Python ผลตอบแทนสามเดือนจึงต้องใช้กำลัง 12/3=412/3=4 ไม่ใช่คูณผลตอบแทนตรง ๆ ด้วยสี่

n_months = len(monthly_returns)
total_growth = (1 + monthly_returns).prod(skipna=False)
annualized_return = total_growth ** (12 / n_months) - 1
print(n_months)
print((annualized_return * 100).round(2))

ได้ 3 เดือน และอัตราต่อปีของ A 40.64% กับ B 26.82% ตัวเลขนี้เป็นการแปลงอัตราจากช่วงเวลาสั้น มิได้แปลว่าเราสังเกตผลตอบแทนครบหนึ่งปี หรือทำนายว่าปีหน้าจะได้เท่านี้ ผลตอบแทนจริงที่ข้อมูลแสดงยังคงเป็น 8.9% และ 6.1208% ในสามเดือน

ถ้าผลตอบแทนหนึ่งเดือนเท่ากับ 1% การแปลงเป็นอัตราทบต้นต่อปีคือ (1 + 0.01) ** 12 - 1 ได้ประมาณ 12.68% ไม่ใช่ 12% สำหรับข้อมูลรายวันต้องระบุจำนวนวันต่อปีที่ใช้ เช่น 252 วันซื้อขายเป็นข้อตกลงหนึ่ง ไม่ควรใช้เลข 252 กับทุกตลาดหรือใช้กับข้อมูลรายเดือน สำหรับข้อมูลวันที่ไม่สม่ำเสมอ ควรตรวจระยะเวลาจริงก่อนเลือกสูตร annualization

กลับไปอ่าน Lab ผลตอบแทนและความเสี่ยง

lab_101.ipynb ไล่จากราคาไปสู่ผลตอบแทน การจับคู่แถว การอ่าน CSV การทบต้น และการแปลงอัตราเป็นรายปี ส่วน lab_102.ipynb ใช้ชุดผลตอบแทนต่อเพื่อวัดความผันผวนและผลตอบแทนเมื่อเทียบกับความเสี่ยง ลองกลับไปอ่านสองไฟล์โดยถามทุกครั้งว่า ตอนนี้ข้อมูลเป็นราคา ผลตอบแทน หรือเปอร์เซ็นต์ที่ใช้แสดงผล

CSV เป็นไฟล์ตารางข้อความ แต่ชื่อไฟล์และหน้าตาตารางไม่ได้ยืนยันหน่วยข้อมูล ในไฟล์ตัวอย่างของ Lab 101 ข้อมูลเป็นราคา จึงต้องใช้ pct_change ก่อน ส่วนไฟล์พอร์ตตามขนาดกิจการที่ Lab 102 อ่านเข้ามาเป็นผลตอบแทนรายเดือนหน่วยเปอร์เซ็นต์อยู่แล้ว เช่น 4.5 หมายถึง 4.5% จึงหาร 100 ให้เป็น 0.045 และไม่ต้องทำ pct_change ซ้ำ มิฉะนั้นจะกลายเป็นการวัดการเปลี่ยนแปลงของตัวเลขผลตอบแทนอีกชั้นหนึ่ง

ก่อนใช้ไฟล์จริง ตรวจคอลัมน์ วันเริ่ม–วันสิ้นสุด ความถี่ หน่วย และค่าที่ผู้ให้ข้อมูลใช้แทนข้อมูลหายด้วย เช่น -99.99 อาจเป็นรหัสข้อมูลหายตามคำอธิบายชุดข้อมูลนั้น ไม่ควรสรุปว่าเป็นผลตอบแทนขาดทุนหรือใช้กติกานี้กับทุกแหล่งโดยไม่ตรวจคู่มือ

ลองเปลี่ยนข้อมูลหนึ่งจุดแล้วอธิบายผล

ถ้าราคาสุดท้ายของ A เปลี่ยนจาก 108.90 เป็น 99 บาท ผลตอบแทนเดือนสุดท้าย ผลตอบแทนสะสม และอัตราต่อปีจะเป็นเท่าไร? ลองตอบจากราคาด้วยมือก่อน แล้วเปลี่ยนค่าในช่องสร้าง prices และรันช่องที่ตามมาทั้งหมด

ดูคำตอบและวิธีตรวจ

เดือนสุดท้ายราคา 99 → 99 จึงได้ 0% เงินเปลี่ยนจาก 100 → 110 → 99 → 99 บาท ผลตอบแทนสะสมคือ 99/100−1=−1%99/100-1=-1\% หรือคูณ 1.10×0.90×1−1=−1%1.10\times0.90\times1-1=-1\% ส่วนอัตราต่อปีคือ 0.9912/3−1=−3.940399%0.99^{12/3}-1=-3.940399\% ประมาณ −3.94% การใช้ค่าเฉลี่ยเลขคณิตของผลตอบแทน (10%−10%+0%)/3=0%(10\%-10\%+0\%)/3=0\% จะไม่บอกการเติบโตทบต้นของเงินในเส้นทางนี้

ถ้าราคาสิ้นเดือนมีนาคมของ A หายไป เรายังบอกผลตอบแทนสะสมมกราคมถึงเมษายนได้หรือไม่?

ดูคำตอบ: รู้ปลายทาง แต่ยังไม่รู้ทุกเดือน

ถ้าราคามกราคมและเมษายนยังเชื่อถือได้ และไม่มีปันผลหรือกระแสเงินสดอื่น เราคำนวณผลตอบแทนรวมสามเดือนจากราคาต้น–ปลายได้ 8.9% แต่ระบุผลตอบแทนเดือนมีนาคมและเมษายนแยกกันไม่ได้ การเติมราคาที่หายด้วย 110 จะสร้างเดือนหนึ่งที่ผลตอบแทนเป็นศูนย์โดยไม่มีหลักฐาน และอาจเปลี่ยนค่าความผันผวนที่นำไปคำนวณต่อ

ผลตอบแทนเฉลี่ยเท่ากัน ระหว่างทางอาจต่างกัน

หลังจากคำนวณผลตอบแทนได้แล้ว คำถามถัดไปคือ “ระหว่างที่เงินเติบโต เราต้องเจอกับการขึ้นลงมากแค่ไหน” ตัวเลขกำไรปลายทางเพียงตัวเดียวตอบเรื่องนี้ไม่ได้

ตัวอย่างต่อจากนี้เป็นข้อมูลสมมติเพื่อฝึกคำนวณ ไม่ใช่ผลตอบแทนของสินทรัพย์จริง นี่เป็นข้อมูลชุดใหม่แยกจากตัวอย่างราคาในส่วน Python สมมติว่าเราสังเกตผลตอบแทนรายเดือนของ A และ B เป็นเวลา 4 เดือน และไม่มีเงินฝากหรือถอนระหว่างทาง

เดือน A B
1 0% −2%
2 +1% 0%
3 +1% +2%
4 +2% +4%
ค่าเฉลี่ยเลขคณิต 1% ต่อเดือน 1% ต่อเดือน

ค่าเฉลี่ยเลขคณิตคือการบวกผลตอบแทนแต่ละเดือนแล้วหารด้วยจำนวนเดือน เช่น B ได้ (−2+0+2+4)/4=1%(−2+0+2+4)/4=1\% แต่ B กระจายห่างจากค่าเฉลี่ยมากกว่า A จึงมีความผันผวนมากกว่า แม้ค่าเฉลี่ยเท่ากัน

คำว่า ความผันผวน (volatility) ในบทนี้หมายถึงส่วนเบี่ยงเบนมาตรฐานของผลตอบแทน ซึ่งนับทั้งการขึ้นแรงและลงแรง ส่วนคำว่า “ความเสี่ยง” กว้างกว่านั้น เช่น ขายสินทรัพย์ไม่ได้เมื่อต้องใช้เงิน หรือเกิดการขาดทุนรุนแรงที่ข้อมูลที่ผ่านมาไม่เคยมี เราจะค่อย ๆ เพิ่มเครื่องมือสำหรับคำถามที่ต่างกัน

เริ่ม code ชุดใหม่ได้โดยรัน cell นี้ก่อน Series คือตารางหนึ่งคอลัมน์ที่มีป้ายกำกับแต่ละแถว ตัวเลขใน Python เก็บเป็นทศนิยม เช่น -0.02 แทน −2% และ .mean() ขอให้ pandas คำนวณค่าเฉลี่ย

import pandas as pd

risk_returns = pd.Series([-0.02, 0.00, 0.02, 0.04], name="B")
risk_mean = risk_returns.mean()
print(f"Mean per month: {risk_mean:.2%}")

ผลคือ Mean per month: 1.00% ส่วน :.2% มีหน้าที่แสดงตัวเลขเป็นเปอร์เซ็นต์สองตำแหน่ง ไม่ได้เปลี่ยนค่าที่เก็บในตัวแปร ชุดข้อมูลเล็กนี้ช่วยให้ตรวจด้วยมือได้ แต่ยังไม่เพียงพอสำหรับประมาณพฤติกรรมของสินทรัพย์จริง

คำนวณความผันผวนทีละขั้น

เราจะวัดว่าแต่ละเดือนอยู่ห่างจากค่าเฉลี่ย 1% เท่าไร หากบวกความห่างตรง ๆ ค่าบวกและค่าลบจะหักล้างกัน จึงยกกำลังสองก่อน

เดือน ผลตอบแทน RtR_t ห่างจากค่าเฉลี่ย Rt−RˉR_t-\bar R ความห่างยกกำลังสอง
1 −0.02 −0.03 0.0009
2 0.00 −0.01 0.0001
3 0.02 +0.01 0.0001
4 0.04 +0.03 0.0009
รวม 0.0020

ในงานนี้เรามอง 4 เดือนที่เห็นเป็น ตัวอย่าง (sample) ของผลตอบแทนที่อาจเกิดขึ้น จึงคำนวณ sample variance โดยหารด้วย n−1=3n-1=3 แล้วถอดรากที่สองเพื่อกลับมาอยู่ในหน่วยเดียวกับผลตอบแทน

s=∑t=1n(Rt−Rˉ)2n−1=0.00203≈0.02582s=\sqrt{\frac{\sum_{t=1}^{n}(R_t-\bar R)^2}{n-1}} =\sqrt{\frac{0.0020}{3}} \approx0.02582

nn คือจำนวนข้อมูล, Rˉ\bar R คือค่าเฉลี่ย และเครื่องหมาย ∑\sum แปลว่า “บวกค่าของทุกงวดเข้าด้วยกัน” ผลคือ ส่วนเบี่ยงเบนมาตรฐานประมาณ 2.582% ต่อเดือน ไม่ได้หมายความว่าแต่ละเดือนจะขึ้นหรือลงเท่านี้เสมอ

risk_deviations = risk_returns - risk_mean
risk_squared_deviations = risk_deviations ** 2
risk_variance = risk_squared_deviations.sum() / (len(risk_returns) - 1)
risk_monthly_vol = risk_variance ** 0.5

print(f"Manual calculation: {risk_monthly_vol:.3%}")
print(f"pandas calculation: {risk_returns.std(ddof=1):.3%}")

ทั้งสองบรรทัดได้ 2.582% โดย len(...) นับจำนวนสมาชิก, ** 2 คือยกกำลังสอง และ ** 0.5 คือถอดรากที่สอง ddof=1 บอกให้ใช้ตัวหาร n−1n-1 หากต้องการสรุปการกระจายของข้อมูลชุดนี้แบบ population ให้ใช้ ddof=0 ซึ่งหารด้วย nn ตัวเลขจึงต่างกันได้โดยไม่ใช่การคำนวณผิด

เปลี่ยนความผันผวนรายเดือนเป็นรายปี

ภายใต้สมมติฐานว่าความแปรปรวนต่อเดือนคงที่และผลตอบแทนต่างเดือนไม่มีความสัมพันธ์กัน เราประมาณ annualized volatility ด้วย

sannual=smonthly12s_{\text{annual}}=s_{\text{monthly}}\sqrt{12}
risk_annual_vol = risk_monthly_vol * (12 ** 0.5)
print(f"Annualized volatility: {risk_annual_vol:.3%}")

ได้ประมาณ 8.944% ต่อปี สังเกตว่าใช้ 12\sqrt{12} ไม่ใช่ 12 เพราะสิ่งที่บวกข้ามงวดได้ภายใต้สมมติฐานนี้คือ variance แล้วจึงถอดรากอีกครั้ง

สูตรนี้เป็นการปรับสเกลที่ใช้กันทั่วไปสำหรับผลตอบแทนระยะสั้น ไม่ใช่สูตร exact ของส่วนเบี่ยงเบนมาตรฐานของผลตอบแทนแบบทบต้นทั้งปี ถ้าผลตอบแทนต่อเนื่องกันมีความสัมพันธ์ หรือความผันผวนเปลี่ยนไปตามเวลา ต้องพิจารณาวิธีประมาณที่เหมาะสมเพิ่มเติม อย่านำสูตร annualize return แบบทบต้นมาใช้แทนสูตร annualize volatility

Sharpe ratio: ผลตอบแทนส่วนเพิ่มต่อความผันผวน

สมมติว่าในแบบฝึกหัดนี้มีอัตราอ้างอิงไร้ความเสี่ยงเท่ากับ 0.1% ต่อเดือน เราต้องหักอัตรานี้ออกจากผลตอบแทนแต่ละเดือนก่อน ผลลัพธ์เรียกว่า ผลตอบแทนส่วนเกิน (excess return) เช่น เดือนที่ได้ 2% มีผลตอบแทนส่วนเกิน 2%−0.1%=1.9%2\%-0.1\%=1.9\%

Sharpe ratio แบบที่ใช้ในส่วนนี้นำ ค่าเฉลี่ยเลขคณิตของ excess return หารด้วย ส่วนเบี่ยงเบนมาตรฐานของ excess return ถ้าข้อมูลเป็นรายเดือน สามารถปรับสเกลเป็นรายปีภายใต้สมมติฐานการบวกความแปรปรวนเช่นเดียวกับด้านบน

SR^annual=R−Rf‾s(R−Rf)12\widehat{SR}_{\text{annual}} =\frac{\overline{R-R_f}}{s(R-R_f)}\sqrt{12}
risk_free_monthly = 0.001
risk_excess = risk_returns - risk_free_monthly
risk_sharpe = (
    risk_excess.mean() / risk_excess.std(ddof=1)
) * (12 ** 0.5)
print(f"Annualized Sharpe: {risk_sharpe:.3f}")

ได้ 1.207 ไม่มีหน่วยเปอร์เซ็นต์ เพราะตัวเศษและตัวส่วนมีหน่วยเดียวกันและตัดกัน ตัวเลขนี้สรุปเฉพาะผลตอบแทนส่วนเพิ่มเทียบกับความผันผวนในตัวอย่าง ไม่ได้ยืนยันว่ากลยุทธ์จะทำได้ดีในอนาคต และยังไม่บอกความรุนแรงของเหตุการณ์ปลายหาง

ถ้า RfR_f คงที่ทุกเดือน การลบค่าคงที่ไม่เปลี่ยนส่วนเบี่ยงเบนมาตรฐาน แต่ถ้าใช้อัตราอ้างอิงที่เปลี่ยนรายเดือน ให้จับคู่เดือนและคำนวณ std ของ excess return จริง ๆ ถ้า std เป็นศูนย์ จะหารไม่ได้ จึงไม่ควรแสดง Sharpe เป็นตัวเลขธรรมดา

ระวังหน่วยเวลา: ห้ามเอา risk-free rate รายปีไปลบ return รายเดือนตรง ๆ หากอัตราที่ให้มาเป็นอัตราต่อปีแบบ effective ให้แปลงเป็นรายเดือนด้วย (1+Rf,annual)1/12−1(1+R_{f,\text{annual}})^{1/12}-1 ก่อน

เวลาอ่าน Lab ของคอร์ส: ตัวอย่างใน lab_102 นำผลตอบแทนทบต้นต่อปีหัก risk-free rate รายปี แล้วหาร annualized volatility วิธีนั้นใช้ตัวเศษแบบ geometric ซึ่งต่างจากค่าเฉลี่ย excess return แบบ arithmetic ด้านบน จึงอาจได้ตัวเลขไม่เท่ากัน ก่อนเปรียบเทียบ Sharpe จากคนละแหล่ง ต้องดูนิยามที่ใช้ด้วย

Drawdown: เงินลดลงจากจุดสูงสุดเท่าไร

ความผันผวนไม่ได้บอกว่าเงินเคยลดลงจากยอดสูงสุดมากแค่ไหน เราจึงสร้าง เส้นมูลค่าเงินลงทุน (wealth index) แล้วเปรียบเทียบกับยอดสูงสุดที่เคยเกิดขึ้นจนถึงเวลานั้น

สมมติเริ่มด้วย 1,000 บาท และมีผลตอบแทน 5 งวดเป็น −10%, +5%, −20%, +10%, +25% โดยลงทุนต่อทั้งจำนวน ไม่มีเงินเข้าออก ไม่มีค่าธรรมเนียมหรือภาษี

เวลา มูลค่าเงิน ยอดสูงสุดถึงเวลานั้น Drawdown
ก่อนเริ่ม 1,000.00 1,000.00 0%
งวด 1 900.00 1,000.00 −10.00%
งวด 2 945.00 1,000.00 −5.50%
งวด 3 756.00 1,000.00 −24.40%
งวด 4 831.60 1,000.00 −16.84%
งวด 5 1,039.50 1,039.50 0%
Wt=W0∏i=1t(1+Ri),Ht=max⁡(W0,W1,…,Wt),Dt=WtHt−1W_t=W_0\prod_{i=1}^{t}(1+R_i),\qquad H_t=\max(W_0,W_1,\ldots,W_t),\qquad D_t=\frac{W_t}{H_t}-1

WtW_t คือมูลค่าเงิน, HtH_t คือยอดสูงสุดสะสม และ DtD_t คือ drawdown ซึ่งเขียนเป็นศูนย์หรือค่าลบในบทนี้ จุดต่ำที่สุดคือ −24.4% ถ้ารายงาน maximum drawdown เป็น ขนาดการขาดทุน จะเขียนบวก 24.4% ต้องบอก convention ให้ชัด

dd_returns = pd.Series([-0.10, 0.05, -0.20, 0.10, 0.25])
dd_initial = 1000.0

dd_after_each_period = dd_initial * (1 + dd_returns).cumprod()
dd_wealth = pd.concat(
    [pd.Series([dd_initial]), dd_after_each_period],
    ignore_index=True,
)
dd_peaks = dd_wealth.cummax()
dd_drawdown = dd_wealth / dd_peaks - 1

dd_table = pd.DataFrame({
    "Wealth": dd_wealth,
    "Peak": dd_peaks,
    "Drawdown": dd_drawdown,
})
print(dd_table.round(4))
print(f"Worst drawdown: {dd_drawdown.min():.2%}")

.cumprod() คูณสะสมจากงวดแรกไปทีละงวด ส่วน .cummax() เก็บค่าสูงสุดตั้งแต่เริ่มจนถึงแต่ละแถว pd.concat ต่อข้อมูลเข้าด้วยกัน เราจงใจใส่ทุนเริ่มต้นไว้เป็นแถวแรก เพราะถ้าเริ่มเส้นยอดสูงสุดที่ 900 บาท จะมองไม่เห็นการขาดทุน 10% ในงวดแรก

Drawdown สนใจ ลำดับเวลา ด้วย การสลับลำดับ returns อาจไม่เปลี่ยนเงินปลายทางเมื่อไม่มีเงินเข้าออก แต่เปลี่ยนช่วงขาดทุนจากยอดสูงสุดได้ อีกทั้งการวัดจากข้อมูลสิ้นเดือนอาจไม่เห็นการตกแรงแล้วฟื้นตัวภายในเดือน

เก็บวิธีคำนวณไว้เป็นฟังก์ชัน

ก่อนสร้าง toolkit ใหญ่ เริ่มจากงานเล็กที่เข้าใจแล้ว เช่น การหาผลตอบแทนรวม

def compound_return(return_series):
    growth_factors = 1 + return_series
    total_growth = growth_factors.prod()
    return total_growth - 1

print(f"Compounded return: {compound_return(dd_returns):.2%}")

ได้ 3.95% คำว่า def สร้างฟังก์ชัน, return_series เป็นชื่อข้อมูลขาเข้า และ return ส่งคำตอบกลับไปที่จุดเรียกใช้ บรรทัดด้านในต้องเยื้องให้เท่ากัน ฟังก์ชันตัวอย่างนี้ตั้งใจรับ pandas Series ที่ไม่มีข้อมูลหาย และยังไม่ได้ทำระบบตรวจ input สำหรับงานจริง

เมื่อมีหลายฟังก์ชัน สามารถเก็บไว้ในไฟล์ finance_tools.py ซึ่งเรียกว่า module แล้วใช้ from finance_tools import compound_return ใน Notebook ที่มองเห็นไฟล์นั้นได้ ถ้าแก้ไฟล์หลัง import ไปแล้ว Python อาจยังใช้เวอร์ชันที่โหลดไว้ วิธีเริ่มต้นที่เข้าใจง่ายคือ restart kernel แล้วรันใหม่ตามลำดับ

ผลตอบแทนอาจไม่ได้มีรูปทรงแบบ Normal

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

บท เมื่อผลตอบแทนไม่เป็น Normal เริ่มจาก Histogram แล้วคำนวณ Skewness, Kurtosis และทดสอบ Normality ทีละขั้น ใช้บทนั้นเมื่อพร้อมศึกษาว่าสิ่งใดที่ mean และ volatility ยังบอกไม่ได้

วัดเฉพาะด้านที่ไม่ต้องการ: ต้องบอกเกณฑ์ก่อน

หากสนใจเฉพาะผลตอบแทนที่ต่ำกว่าเกณฑ์ ต้องระบุทั้งเกณฑ์ที่ใช้และจำนวนงวดที่นำมาหาร ชื่อ Downside deviation หรือ Semideviation เพียงอย่างเดียวอาจยังบอกสูตรไม่ครบ

อ่านการเปรียบเทียบสูตรพร้อมตัวอย่างและ Python ใน การวัดผลตอบแทนต่ำกว่าเกณฑ์ ซึ่งแยกสูตรของเว็บไซต์ คำแก้ไขของคอร์ส และโค้ดใน Lab ไว้ด้วยกัน

VaR และ CVaR: ขอบเขตหาง กับขนาดการขาดทุนในหาง

VaR ระบุเส้นแบ่งผลขาดทุนตามระดับความเชื่อมั่นและช่วงเวลาที่กำหนด ส่วน CVaR หรือ Expected Shortfall สนใจผลขาดทุนเฉลี่ยในส่วนที่แย่ที่สุดนั้น ค่า VaR จึงยังไม่บอกว่าผลขาดทุนที่เลยเส้นแบ่งไปจะรุนแรงเพียงใด

เรียนการเรียงผลขาดทุนและเลือก Quantile ใน Historical VaR ต่อด้วย การเฉลี่ยปลายหางให้ได้น้ำหนักครบ แล้วจึงเปรียบเทียบกับ Gaussian และ Cornish–Fisher VaR ตัวอย่าง โค้ด และข้อตกลงในการรายงานตัวเลขอยู่ในบทเดียวกัน

ลองตอบก่อนเปิดเฉลย

  1. A กับ B มีค่าเฉลี่ย 1% เท่ากัน แปลว่า volatility เท่ากันหรือไม่?
  2. จาก B ทำไมจึงหารผลรวมความห่างยกกำลังสองด้วย 3 ไม่ใช่ 4 ใน sample variance?
  3. ถ้าเริ่ม 1,000 บาทแล้วจบงวดแรกที่ 900 บาท drawdown เป็นเท่าไร และทำไมต้องเก็บแถวก่อนเริ่ม?
  4. Sharpe ratio 1.207 ในตัวอย่างมีหน่วยเปอร์เซ็นต์หรือไม่ และตัวเศษวัดอะไร?
  5. ถ้าสลับลำดับผลตอบแทนโดยไม่มีเงินเข้าออก เงินปลายทางกับ Drawdown จำเป็นต้องเปลี่ยนเหมือนกันหรือไม่?
  6. เหตุใด volatility ต่อปีจึงใช้รากที่สองของ 12 แทนการคูณ 12 และต้องมีสมมติฐานอะไร?
เปิดแนวคำตอบ

แนวคำตอบ: (1) ไม่เท่า B กระจายมากกว่า (2) กำลังใช้ sample estimator ที่มีตัวหาร n−1n-1 (3) −10%; เงินทุนก่อนเริ่มเป็นยอดสูงสุดที่ต้องนับด้วย (4) ไม่มีหน่วยเปอร์เซ็นต์ ตัวเศษคือค่าเฉลี่ยผลตอบแทนส่วนเกินเหนืออัตราอ้างอิง (5) ผลคูณของตัวคูณเงินลงทุนยังเท่าเดิม จึงได้เงินปลายทางเท่าเดิม แต่ Drawdown เปลี่ยนได้ตามลำดับ (6) ภายใต้ variance ต่อเดือนคงที่และไม่มี covariance ข้ามเดือน variance ของผลรวมเพิ่มเป็น 12 เท่า ส่วน SD จึงเพิ่มเป็นรากที่สองของ 12 เท่า เป็นการปรับสเกล ไม่ใช่สูตร exact ของผลตอบแทนทบต้นทั้งปี

หากต้องการทดสอบความเข้าใจเพิ่ม ให้เปลี่ยนผลตอบแทนงวดแรกใน dd_returns เป็น −20% แล้วรันส่วน drawdown ใหม่ ตรวจว่าแถวแรกหลังลงทุนยังเปรียบเทียบกับทุน 1,000 บาท และอธิบายความต่างระหว่างกำไรปลายทาง ความผันผวน และ maximum drawdown ด้วยคำของตัวเอง

เนื้อหาความผันผวน Sharpe ratio และ Drawdown เรียบเรียงจาก Measures of Risk and Reward, Measuring Max Drawdown และ Lab 102–104 ของ EDHEC/Coursera โดยใช้ตัวอย่างสมมติของบทนี้เอง ส่วน Deviations from Normality, Downside Risk Measures, Estimating VaR และ Lab 105–106 เป็นแหล่งของบท เมื่อผลตอบแทนไม่เป็น Normal ซึ่งรวมคำแก้ไขและข้อตกลงเรื่องปลายหางไว้

อ่านต่อ: Log return เปลี่ยนการคูณให้เป็นการบวก

หัวข้อนี้เป็นส่วนต่อยอด อ่านภายหลังได้เมื่อเข้าใจการทบต้นแล้ว ลอการิทึม เป็นการถามย้อนกลับจากตัวคูณไปหาเลขชี้กำลัง ส่วน e≈2.71828e\approx2.71828 เป็นค่าคงที่ทางคณิตศาสตร์ที่ใช้เป็นฐานของลอการิทึมธรรมชาติ

Log return เริ่มจากตัวคูณเงินลงทุนเช่นเดียวกัน แล้วนำมาหาลอการิทึมธรรมชาติ ln⁡\ln ซึ่งมีฐาน ee ไม่ใช่ log ฐาน 10 ให้ VtV_t เป็นมูลค่าเงินลงทุนเมื่อสิ้นช่วง tt

rt=ln⁡(VtVt−1)=ln⁡(1+Rt).r_t=\ln\left(\frac{V_t}{V_{t-1}}\right)=\ln(1+R_t).

ในตัวอย่างนี้ปีแรกเงินเพิ่มเป็น 2 เท่า จึงมี log return เท่ากับ ln⁡2\ln 2 ปีที่สองเงินเหลือครึ่งหนึ่ง จึงมี log return เท่ากับ ln⁡0.5\ln 0.5

ช่วงเวลา ตัวคูณเงินลงทุน Log return (ทศนิยม)
ปีที่ 1 2 ln⁡2≈+0.693147\ln 2\approx+0.693147
ปีที่ 2 0.5 ln⁡0.5≈−0.693147\ln 0.5\approx-0.693147
รวมสองปี 2×0.5=12\times0.5=1 ln⁡1=0\ln 1=0
Log return ปีแรกบวก 0.693147 ปีที่สองลบ 0.693147 แท่งสูงเท่ากันคนละด้านของศูนย์ ผลรวม 0
แท่งบวกกับแท่งลบมีขนาดเท่ากันจึงหักล้างกัน แกนนี้เป็นหน่วย log แบบทศนิยม ไม่ใช่เปอร์เซ็นต์กำไร · ตัวอย่างสมมติ · ดาวน์โหลดภาพ SVG

ถ้ายังไม่คุ้นกับ log ให้เริ่มจากความสัมพันธ์ er=1+Re^r=1+R: log return คือจำนวน rr ที่เมื่อใช้เป็นเลขชี้กำลังของ ee แล้วจะได้ตัวคูณเงินลงทุนกลับมา ปีแรก e0.693147≈2e^{0.693147}\approx2 ส่วนปีที่สอง e−0.693147≈0.5e^{-0.693147}\approx0.5

ทำไม Log return จึงบวกข้ามเวลาได้

ลอการิทึมมีคุณสมบัติว่า ln⁡a+ln⁡b=ln⁡(ab)\ln a+\ln b=\ln(ab) เมื่อ aa และ bb เป็นบวก ดังนั้น

r1+r2=ln⁡2+ln⁡0.5=ln⁡(2×0.5)=ln⁡1=0.r_1+r_2=\ln 2+\ln 0.5 =\ln(2\times0.5)=\ln 1=0.

ผลรวม log returns ของช่วงเวลาที่ต่อกันเท่ากับ log return จากจุดเริ่มต้นถึงจุดสิ้นสุด คุณสมบัตินี้เรียกว่า การบวกข้ามเวลา (time additivity)

ดูเหตุผลอีกแบบหนึ่ง โดยเขียนมูลค่าแต่ละปีเป็น V0,V1,V2V_0,V_1,V_2

r1+r2=ln⁡(V1V0)+ln⁡(V2V1)=ln⁡(V1V0×V2V1)=ln⁡(V2V0).\begin{aligned} r_1+r_2 &=\ln\left(\frac{V_1}{V_0}\right)+\ln\left(\frac{V_2}{V_1}\right)\\ &=\ln\left(\frac{V_1}{V_0}\times\frac{V_2}{V_1}\right)\\ &=\ln\left(\frac{V_2}{V_0}\right). \end{aligned}

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

สำหรับหลายช่วงเวลา

r0:T=∑t=1Trt=ln⁡(VTV0).r_{0:T}=\sum_{t=1}^{T}r_t=\ln\left(\frac{V_T}{V_0}\right).

สูตรนี้ใช้กับมูลค่าบวกของเงินลงทุนก้อนเดียวกัน โดยไม่มีฝากถอนภายนอก หรือใช้อนุกรมที่ปรับกระแสเงินสดไว้แล้ว ช่วงที่นำมารวมต้องต่อกันครบและไม่ซ้อนกัน เช่น รวม log returns รายเดือน 12 เดือนเป็นหนึ่งปีได้ แต่ห้ามนำผลรายปีนั้นมาบวกซ้ำกับ 12 เดือนเดิม

จากผลรวม Log return กลับมาเป็นเงินและเปอร์เซ็นต์

ผลรวมยังอยู่ในหน่วย log แปลงเป็นผลตอบแทนสะสมและเงินปลายทางได้ด้วย

R0:T=er0:T−1,VT=V0er0:T.R_{0:T}=e^{r_{0:T}}-1, \qquad V_T=V_0e^{r_{0:T}}.

ตัวอย่างสองปีมี r0:2=0r_{0:2}=0 จึงได้ผลตอบแทนสะสม e0−1=0%e^0-1=0\% และเงินปลายทาง 1,000,000×e0=1,000,0001{,}000{,}000\times e^0=1{,}000{,}000 บาท ตรงกับจำนวนเงินที่เราติดตามในตาราง

อย่าอ่าน log return 0.6931470.693147 ของปีแรกว่าได้กำไร 69.3147% เงินปีแรกเพิ่มขึ้น 100% เพราะ eln⁡2−1=1e^{\ln 2}-1=1 หรือ 100% หากแสดง log return คูณ 100 ต้องระบุหน่วยว่า log return × 100

คุณสมบัตินี้ใช้ได้แม้เงินไม่กลับมาเท่าเดิม

สมมติปีที่สามได้กำไรอีก 20% เงินจึงเพิ่มจาก 1 ล้านบาทเป็น 1.2 ล้านบาท ผลรวม log returns ทั้งสามปีเป็น

ln⁡2+ln⁡0.5+ln⁡1.2=ln⁡(2×0.5×1.2)=ln⁡1.2≈0.182322.\ln 2+\ln 0.5+\ln 1.2 =\ln(2\times0.5\times1.2)=\ln 1.2\approx0.182322.

แปลงกลับได้ eln⁡1.2−1=20%e^{\ln 1.2}-1=20\% เท่ากับผลตอบแทนจากเงินต้น 1 ล้านบาทไปถึง 1.2 ล้านบาทพอดี

Simple return กับ log return วัดการเปลี่ยนแปลงของเงินก้อนเดียวกัน เมื่อ gross return เป็นบวกจึงแปลงกลับไปมาได้โดยตรง การประมาณเกิดขึ้นเมื่อใช้ rt≈Rtr_t\approx R_t สำหรับผลตอบแทนขนาดเล็ก

ชื่อ continuously compounded return มาจากการเขียนตัวคูณการเติบโตเป็น ere^r เราคำนวณ log return จากมูลค่าที่สังเกตปีละครั้งได้ โดยไม่ต้องสมมติว่าราคาเคลื่อนที่ต่อเนื่อง

ทดลองเปลี่ยนตัวเลข: เงินกับ Log return ไปด้วยกันอย่างไร

เริ่มจาก +100% แล้ว −50% จากนั้นเลือก +100% แล้ว −25% ดูว่าแท่ง Log return ยังหักล้างกันหมดหรือไม่ กด สลับลำดับปี เพื่อเปรียบเทียบเงินระหว่างทางกับเงินปลายทาง แล้วลองเลื่อนค่าเอง

เปิด JavaScript เพื่อปรับผลตอบแทนในตัวทดลอง ภาพและตัวอย่างคำนวณด้านบนอ่านได้โดยไม่ใช้ JavaScript

Log return ของขาลงและขาขึ้นกลับมาเท่าทุน

ย้อนกลับไปที่ตัวอย่างราคา 80 → 60 → 80 บาท: simple return ขาลงคือ −25% และขาขึ้นคือ +33.3333% ส่วน log returns เท่ากับ −0.287682 และ +0.287682 จึงมีขนาดเท่ากันแต่คนละเครื่องหมายและรวมกันเป็นศูนย์ ประเด็นนี้อธิบายไว้ใน เอกสารของ Motty Shai

การใช้ log return เปลี่ยนวิธีแสดงผล ส่วนจำนวนเงินที่ขาดทุนยังเท่าเดิม

สำหรับการถือสินทรัพย์ long โดยไม่กู้ยืมและไม่มีภาระเพิ่มเติม simple return ต่ำสุดคือ −100% เมื่อมูลค่าลงเป็นศูนย์ แต่ log return ไม่มีค่าจำกัด ณ จุดนั้น เพราะ ln⁡0\ln 0 ไม่ได้ถูกนิยามเป็นจำนวนจริง เมื่อมูลค่าเข้าใกล้ศูนย์ log return จะมุ่งไปที่ −∞-\infty กรณีพอร์ตลงทุนเกินเงินทุนที่มี หรือมูลค่าสุทธิไม่เป็นบวกต้องพิจารณาแยก ไม่ควรลบข้อมูลนั้นเงียบ ๆ เพื่อให้คำนวณ log ได้

Log return ของพอร์ตหลายสินทรัพย์

ย้อนกลับไปที่ตัวอย่างพอร์ต A/B น้ำหนัก 60/40 ซึ่ง A ได้ +20% และ B ได้ −10% ในเดือนเดียวกัน ผลตอบแทน simple return ของพอร์ตเท่ากับ 8%: log return ของพอร์ตต้องคำนวณจาก

rp,t=ln⁡(1+Rp,t)=ln⁡(∑iwi,t−1eri,t).r_{p,t}=\ln(1+R_{p,t}) =\ln\left(\sum_i w_{i,t-1}e^{r_{i,t}}\right).

ได้ ln⁡(1.08)=0.076961\ln(1.08)=0.076961 แต่การถ่วงน้ำหนัก log return ของสินทรัพย์ตรง ๆ ได้ 0.6ln⁡(1.2)+0.4ln⁡(0.9)=0.0672490.6\ln(1.2)+0.4\ln(0.9)=0.067249 ซึ่งไม่เท่ากัน

เมื่อไรสองค่าจึงใกล้กัน

การกระจาย Taylor รอบศูนย์ให้

ln⁡(1+R)=R−R22+R33−⋯ .\ln(1+R)=R-\frac{R^2}{2}+\frac{R^3}{3}-\cdots.

ใกล้ศูนย์ พจน์กำลังสองและสูงกว่ายังเล็ก การใช้ r≈Rr\approx R จึงอาจสะดวก แต่ความคลาดเคลื่อนขึ้นกับขนาดผลตอบแทนและความแม่นยำที่งานต้องการ

Simple return Log return × 100 ส่วนต่าง simple − log (หลังคูณ 100)
+1% +0.9950 0.0050
+5% +4.8790 0.1210
+20% +18.2322 1.7678
−20% −22.3144 2.3144

โดยทั่วไป ln⁡(1+R)≤R\ln(1+R)\leq R สำหรับ R>−1R>-1 และเท่ากันเฉพาะที่ศูนย์ จึงไม่ควรแทนสองค่าเข้าหากันโดยไม่ดูขนาดการเปลี่ยนแปลง โดยเฉพาะช่วงตลาดเคลื่อนไหวแรง

เปลี่ยนเป็น log ไม่ได้ทำให้ข้อมูลเป็น Normal

คุณสมบัติการบวกข้ามเวลาเป็นความสัมพันธ์ทางคณิตศาสตร์ ใช้ได้แม้ log returns ไม่เป็น Normal หรือมีความสัมพันธ์กัน การแจกแจงและความเป็นอิสระจึงเป็นสมมติฐานที่ต้องตรวจแยกกัน

ถ้าตั้งสมมติฐานว่า log returns หลายช่วงมีการแจกแจงปกติร่วมกัน ผลรวมจึงเป็น Normal และเมื่อราคาเริ่มต้นเป็นค่าคงที่บวก ราคาปลายทางที่เขียนเป็น PT=P0e∑trtP_T=P_0e^{\sum_t r_t} จะเป็น Lognormal กรณีผลตอบแทนแต่ละช่วงเป็น Normal และเป็นอิสระกันเป็นเงื่อนไขหนึ่งที่เพียงพอ แต่ความเป็นบวกของราคาอย่างเดียวไม่เพียงพอ

บทความของ Simon Leung ชวนพิจารณาข้อจำกัดของสมมติฐานการแจกแจง ในการใช้งานจริงต้องตรวจข้อมูลและวิธีทดสอบเพิ่มเติม การประมาณพารามิเตอร์จากตัวอย่างแล้วใช้ KS test แบบมาตรฐานกับตัวอย่างเดิมต้องปรับวิธีหาค่า p-value ให้รองรับการประมาณนั้น จึงไม่ควรยกค่า p-value ในบทความมาเป็นข้อยืนยันแบบจำลองของเรา บทความต้นทาง

ลองเช็กความเข้าใจด้วยตัวเอง

ถ้าสลับลำดับเป็นปีแรกขาดทุน 50% แล้วปีที่สองกำไร 100% เงินปลายทางจะเปลี่ยนหรือไม่? ใช้เงินต้น 1 ล้านบาทเหมือนเดิม ไม่มีฝากถอนและค่าใช้จ่าย ลองคิดทั้งจำนวนเงิน ตัวคูณ และ log return ก่อนเปิดคำตอบ

ดูคำตอบ: สลับปีขาดทุนกับปีกำไร

เงินเปลี่ยนจาก 1,000,000 → 500,000 → 1,000,000 บาท ตัวคูณรวมคือ 0.5×2=10.5\times2=1 ส่วน log return รวมคือ ln⁡0.5+ln⁡2=0\ln 0.5+\ln 2=0 ผลตอบแทนสะสมจึงเท่ากับ 0% เหมือนเดิม

เมื่อไม่มีฝากถอนและตรึงผลตอบแทนแต่ละปีไว้ ลำดับของตัวคูณไม่เปลี่ยนผลคูณ แต่เส้นทางเงินต่างกัน: กรณีนี้เคยเหลือครึ่งล้าน ขณะที่กรณีเดิมเคยเพิ่มเป็นสองล้าน ผลตอบแทนต้น–ปลายจึงไม่ได้บอกความเสี่ยงระหว่างทางทั้งหมด

แล้วถ้าปีแรกกำไร 100% และปีที่สองขาดทุนเพียง 25% ล่ะ? ผลรวม log returns จะยังเป็นศูนย์หรือไม่?

ดูคำตอบ: ปีที่สองขาดทุน 25%

เงินเปลี่ยนจาก 1,000,000 → 2,000,000 → 1,500,000 บาท จึงได้ผลตอบแทนสะสม 50% ตัวคูณรวมเท่ากับ 2×0.75=1.52\times0.75=1.5

ln⁡2+ln⁡0.75=ln⁡1.5≈0.405465,eln⁡1.5−1=50%.\ln 2+\ln 0.75=\ln 1.5\approx0.405465, \qquad e^{\ln 1.5}-1=50\%.

ผลรวม log returns ไม่จำเป็นต้องเป็นศูนย์ แต่จะเท่ากับ log return ที่คำนวณตรงจากต้นถึงปลายเสมอภายใต้เงื่อนไขเดียวกัน ส่วนการบวก simple returns เป็น 100%−25%=75%100\%-25\%=75\% ยังให้คำตอบผิด เพราะแต่ละปีใช้ฐานเงินต่างกัน

เลือกใช้ให้ตรงคำถาม

คำถาม วิธีคำนวณ
ช่วงนี้เงินเพิ่มหรือลดกี่เปอร์เซ็นต์? Simple total return ที่นับกระแสเงินสดครบ
ถือหลายช่วงแล้วเงินโตเท่าไร? คูณ gross returns แล้วลบหนึ่ง หรือรวม log returns แล้วแปลงกลับ
เงินโตเฉลี่ยแบบทบต้นเท่าไร? Geometric mean หรือ CAGR พร้อมระบุช่วงเวลา
พอร์ตหลายสินทรัพย์ได้เท่าไรในช่วงเดียวกัน? ถ่วงน้ำหนัก simple returns ด้วยน้ำหนักต้นช่วง
จะใช้ log returns สร้างแบบจำลองได้ไหม? ตรวจมูลค่าบวก คุณภาพข้อมูล และสมมติฐานของแบบจำลองก่อน

กรณีมีฝากถอน เงินเข้าพอร์ตไม่ใช่กำไรจากการลงทุน อัตราเปลี่ยนแปลงของยอดบัญชีจึงใช้ตรง ๆ ไม่ได้ ต้องแยกกระแสเงินสดภายนอกก่อน เช่น แบ่งช่วงตามจุดฝากถอนเพื่อวัด time-weighted return หรือใช้ money-weighted return เมื่อคำถามเกี่ยวกับผลตอบแทนของเงินที่ผู้ลงทุนใส่จริง

แหล่งอ้างอิงและข้อสังเกตในการอ่าน

  1. Motty Shai. Simple return vs. Logarithmic return — additional aspects. 1 กุมภาพันธ์ 2025 ปรับปรุง 2 มีนาคม 2025 ตามไฟล์ PDF ที่ได้รับ — SSRN 5161959. ใช้ประเด็นผลตอบแทนขาไป–ขากลับและการเปลี่ยนผลคูณเป็นผลบวก ไม่รับข้อสรุปว่าการใช้ log เพียงอย่างเดียวทำให้ Backtest ดีขึ้นหรือแก้ความเสี่ยงได้โดยอัตโนมัติ
  2. Simon Leung. A brief overview on simple returns and log returns in financial data. 5 ธันวาคม 2024 — Medium. ใช้เป็นกรอบเปรียบเทียบสองนิยามและข้อควรระวังเรื่องการแจกแจง ตัวอย่างราคา 100 → 110 → 90 → 120 ในต้นทางต้องได้ผลตอบแทนรวม 20% พอดีเมื่อไม่ปัดเศษระหว่างทาง ไม่ใช่ 20.04%
  3. AlgoAddict Diary. Simple Return VS Log Return ความแตกต่างที่ไม่ควรมองข้าม!. 10 มกราคม 2019 — Medium. ใช้เป็นจุดเริ่มต้นเรื่องการบวกผลตอบแทนข้ามเวลา สูตรแปลงกลับต้องเป็น er−1e^r-1: eln⁡(1.5)=1.5e^{\ln(1.5)}=1.5 คือ gross return จึงต้องลบหนึ่งก่อนรายงานกำไร 50% ไม่จำเป็นต้องใช้ log เสมอไปหากทบต้น simple returns ถูกต้อง

แหล่งเรียนเรื่องผลตอบแทนและความเสี่ยง

เนื้อหาที่เพิ่มในวันที่ 2 ตุลาคม 2026 ใช้ Fundamentals of Returns และ Lab Session-Basics of returns โดย Vijay Vaidyanathan ร่วมกับบทความเสี่ยงของ Vijay Vaidyanathan และ Lionel Martellini ในคอร์ส EDHEC/Coursera แหล่งเฉพาะเรื่องเชื่อมไว้ใกล้หัวข้อที่เกี่ยวข้อง ส่วนประกาศแก้ไข Skewness และ Semi Deviation อยู่ในแหล่งอ้างอิงของ บทการแจกแจงและความเสียหายปลายหาง

ตรวจโค้ดอ้างอิงจาก lab_101.ipynb ถึง lab_104.ipynb สำหรับพื้นฐานในหน้านี้ และใช้ lab_105.ipynb ถึง lab_106.ipynb ประกอบบทถัดไป แล้วสร้างตัวอย่างสอนใหม่จากหัวข้อเหล่านั้น ไฟล์ต้นทาง วิดีโอ Transcript และข้อมูลตลาดของคอร์สไม่ได้รวมในเว็บ ตัวอย่าง Python ในหน้านี้ตรวจผลด้วย pandas 2.3.3; ไม่จำเป็นต้องมี CSV ของคอร์สเพื่อรันตัวอย่างในหน้านี้

ใช้บท ความเสี่ยง: แนวคิดและการอ่านผลวัด เมื่อต้องการเชื่อมตัววัดกับคำถามลงทุน ดูประวัติแนวคิด และทดลองว่าตัววัดแต่ละชนิดเห็นหรือมองข้ามอะไร

อ่านบทถัดไป: เมื่อผลตอบแทนไม่เป็น Normal เรียนรูปร่างผลตอบแทน การทดสอบ Normality การสร้าง Python module และการคำนวณ VaR/CVaR จากข้อมูลปลายหาง

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

ปิด
คำค้นหา

© 2023 by QuantCorner, Thailand.

bottom of page