</>
#
WEALTH CODE CURRICULUM

บทที่ 6: Code an Investment Rule
สร้างกฎและทดสอบกลยุทธ์

"อย่าลงทุนด้วยความรู้สึก ให้ลงทุนด้วยกฎเกณฑ์และสถิติ"

เรียนรู้เทคนิค Backtesting จำลองการทำงานของ Moving Average
และสร้างตัวสร้างสัญญาณซื้อขาย (Signals) ด้วย Python

{}

เส้นทางการเรียนรู้ (120 นาที)

10m

CASE

ซื้อด้วยอารมณ์ VS ซื้อด้วยกฎเกณฑ์

20m

CONCEPT

เส้นค่าเฉลี่ย (SMA) & Backtesting

25m

CODE ALONG

คำสั่ง Rolling & Numpy Where

45m

BUILD

สร้าง Strategy Backtest Engine

15m

DEFEND

กลยุทธ์นี้เวิร์คจริง หรือแค่ฟลุค?

05m

EXIT

กฎเหล็กของนักพัฒนา Quant

?
CASE STUDY

เมื่อความรู้สึก.. ทำพอร์ตพัง

คุณเคยตัดสินใจผิดพลาดเรื่องเงิน เพราะความตกใจกลัว (Panic) หรือความโลภ (FOMO) ไหม?

นาย A (นักลงทุนสายมโน)

"ข่าวบอกเศรษฐกิจแย่ ฉันกลัว รีบขายดีกว่า!"
"หุ้นตัวนี้เพื่อนบอกกำลังมา ซื้อตามเลย!"

ผลลัพธ์: ซื้อแพง ขายถูก ตลอดเวลา

นางสาว B (นักลงทุนสาย Quant)

"ฉันไม่สนข่าว ฉันเขียนกฎไว้ว่า ถ้าราคาหลุดเส้นค่าเฉลี่ย ให้ขายเป็นเงินสดทันที"

ผลลัพธ์: รอดตายจากวิกฤตเศรษฐกิจตกหนัก
%
CONCEPT

หาเทรนด์ด้วย Moving Average (SMA)

ราคาหุ้นรายวันมันแกว่งไปมา (Noise) ทำให้ดูยากว่ากำลังขึ้นหรือลง
การทำ Simple Moving Average คือการเอาข้อมูลย้อนหลังมา "หาค่าเฉลี่ย" เพื่อให้เส้นสมูทขึ้น

$$ SMA_3 = \frac{Price_1 + Price_2 + Price_3}{3} $$
เทรนด์ขาขึ้น:
ถ้าราคาหุ้น > เส้น SMA (สัญญาณ ซื้อ)
เทรนด์ขาลง:
ถ้าราคาหุ้น < เส้น SMA (สัญญาณ ขายหนี)
&
CONCEPT

การจำลองอดีต (Backtesting)

ก่อนจะเอากฎ (Rule) ที่เราคิด ไปใช้กับเงินจริง เราต้องใช้โปรแกรมคอมพิวเตอร์ "ย้อนเวลา" เอาข้อมูลในอดีตมาใส่ แล้วดูว่าถ้ายึดตามกฎนี้...

  • เราจะได้กำไรเท่าไหร่?
  • เราจะโดนลากติดลบ (Drawdown) กี่เปอร์เซ็นต์?
  • เราชนะการ "ซื้อแล้วถือทิ้งไว้เฉยๆ (Buy & Hold)" ไหม?

"ทดสอบกลยุทธ์ในกระดาษ
ดีกว่าพลาดในตลาดจริง"

</>
CODE ALONG

คำสั่ง rolling และ np.where

สร้างไฟล์ lesson6_intro.py เราจะลองใช้ Pandas คำนวณค่าเฉลี่ยย้อนหลัง

lesson6_intro.py
import pandas as pd
import numpy as np

# จำลองราคาหุ้น 5 วัน
df = pd.DataFrame({"Price": [10, 12, 15, 14, 9]})

# 1. คำนวณ SMA ย้อนหลัง 3 วัน (rolling = เลื่อนหน้าต่าง)
df["SMA_3"] = df["Price"].rolling(window=3).mean()

# 2. สร้าง Signal: ถ้าราคา > SMA ให้ถือหุ้น (1) ถ้าไม่ใช่ถือเงินสด (0)
# np.where(เงื่อนไข, ค่าถ้าเป็นจริง, ค่าถ้าเป็นเท็จ)
df["Signal"] = np.where(df["Price"] > df["SMA_3"], 1, 0)

print(df)
*
BUILD PROJECT (Step 1/5)

เตรียมข้อมูลสำหรับ Backtest

สร้างไฟล์ strategy_dash.py และจำลองข้อมูลตลาด 20 ช่วงเวลา

strategy_dash.py
import streamlit as st
import pandas as pd
import numpy as np

st.set_page_config(layout="wide")
st.title("🤖 Strategy Backtest Engine")

# --- 1. จำลองข้อมูลตลาด (มีช่วงขึ้น, ตกหนัก, และฟื้นตัว) ---
mock_prices = [
    100, 105, 110, 115, 120, 118, 122, 125, 130, 128, # ตลาดขาขึ้น
    110, 95, 85, 80, 82, 85, 90, 95, 100, 105 # ตลาดถล่ม แล้วฟื้น
]
df = pd.DataFrame({"Time": list(range(1, 21)), "Price": mock_prices}).set_index("Time")
*
BUILD PROJECT (Step 2/5)

กำหนดเงื่อนไข (Trading Rule)

พิมพ์ต่อ: สร้าง Slider รับค่าหน้าต่าง SMA และสร้างสัญญาณซื้อขาย (Signal)

strategy_dash.py (cont.)
# --- 2. ตั้งค่ากลยุทธ์ Moving Average ---
st.subheader("⚙️ ปรับแต่งกฎการลงทุน")
sma_window = st.slider("เลือกช่วงเวลาคำนวณ SMA", 2, 10, 3)

# คำนวณเส้นค่าเฉลี่ย
df["SMA"] = df["Price"].rolling(window=sma_window).mean()

# สร้างสัญญาณ (Signal): 1 = ถือหุ้น, 0 = ถือเงินสด (หลบภัย)
df["Signal"] = np.where(df["Price"] > df["SMA"], 1, 0)

# shift(1) สำคัญมาก! ใช้สัญญาณของเมื่อวาน ตัดสินใจวันนี้ ป้องกันการรู้อนาคต
df["Position"] = df["Signal"].shift(1).fillna(0)
{}
BUILD PROJECT (Step 3/5)

คำนวณผลตอบแทนเปรียบเทียบ

พิมพ์ต่อ: เอาผลตอบแทนตลาด ไปคูณกับ Position ที่เราถืออยู่ (1 หรือ 0)

strategy_dash.py (cont.)
# --- 3. คำนวณผลตอบแทน (Return & Growth) ---
# Buy & Hold Benchmark (ซื้อแล้วถือยาว)
df["Market_Return"] = df["Price"].pct_change().fillna(0)
df["Buy_Hold_Growth"] = (1 + df["Market_Return"]).cumprod() * 100

# Strategy Return (ผลตอบแทนตามกลยุทธ์ ถ้า Position=0 คือหลบภัย Return=0)
df["Strategy_Return"] = df["Market_Return"] * df["Position"]
df["Strategy_Growth"] = (1 + df["Strategy_Return"]).cumprod() * 100

Syntax Tip: cumprod() คือการคูณทบต้น (Cumulative Product)

[]
BUILD PROJECT (Step 4/5)

สร้าง UI แสดงกราฟ

พิมพ์ต่อ: พลอตกราฟราคา (เพื่อดูจุดตัด) และกราฟความมั่งคั่งเพื่อตัดสินแพ้ชนะ

strategy_dash.py (cont.)
# --- 4. แสดงผล Dashboard ---
col1, col2 = st.columns(2)
with col1:
    st.subheader("📉 สัญญาณซื้อขาย (Price vs SMA)")
    st.line_chart(df[["Price", "SMA"]])
with col2:
    st.subheader("🏆 ใครชนะ? (Growth Comparison)")
    st.line_chart(df[["Buy_Hold_Growth", "Strategy_Growth"]])

# สรุปผลสุดท้าย
final_diff = df['Strategy_Growth'].iloc[-1] - df['Buy_Hold_Growth'].iloc[-1]
st.metric("ความมั่งคั่งของกลยุทธ์ (ฐาน 100)", f"{df['Strategy_Growth'].iloc[-1]:.2f}",
          delta=f"{final_diff:.2f} เทียบกับตลาด")
*
BUILD PROJECT (Result)

ผลลัพธ์: Strategy Backtest Engine

เมื่อรันโค้ดเสร็จ นี่คือหน้าตา Web App ของคุณ (ปรับค่าวิเคราะห์หาจุดที่ชนะตลาดได้)

localhost:8501

Strategy Backtest Engine

สัญญาณซื้อขาย (Price vs SMA)
ใครชนะ? (Growth Comparison)
128.75
23.75 เทียบกับตลาด
@
DEFEND

กลยุทธ์นี้รอดจริง หรือแค่ฟลุค?

เห็นว่ากลยุทธ์เราชนะ เพราะหลบหนีวิกฤตได้ทัน แต่ในโลกจริงมีปัญหาที่ต้องระวัง:

ต้นทุนแฝง (Transaction Costs)

ทุกครั้งที่ระบบสั่งซื้อๆ ขายๆ คุณต้องเสีย "ค่าธรรมเนียม" ยิ่งปรับ SMA สั้น สัญญาณยิ่งถี่ กำไรอาจหมดไปกับค่าธรรมเนียม

ตลาดแกว่งแคบๆ (Whipsaw)

ถ้าตลาดไม่ได้เป็นเทรนด์ชัดเจน แต่แกว่งแคบๆ (Sideway) สัญญาณจะหลอกให้เรา ซื้อแพง-ขายถูก ซ้ำๆ จนขาดทุนสะสมได้

%
EXIT

กฎเหล็กของนักพัฒนา Quant

Past Performance != Future Results

"ผลการดำเนินงานในอดีต ไม่ได้การันตีอนาคต"

โมเดลที่ดูสวยหรูตอนทำ Backtest มักจะพังเมื่อเจอตลาดจริง
เพราะเรามักปรับตัวเลขให้เข้าข้างตัวเอง (Overfitting)

"ในบทต่อไป เราจะทำการ 'ทดสอบความเครียด' (Stress Test)
เพื่อดูว่าโมเดลที่เราสร้าง แข็งแกร่งพอจะสู้โลกความจริงไหม!"