ฟันกำไรหุ้นด้วยกลยุทธ์ Pair Trade - Pandas Package| Python Trading

Vithan Minaphinant
investic

--

Data Science กับการลงทุน 101 ตอนที่ 2.3

มาเริ่มกันเลยดีกว่า จากตัวอย่างที่อธิบายไปในตอนที่แล้วด้วย Excel วันนี้เราจะมาใช้ Pandas กันนะครับ ไม่ต้องห่วงสำหรับคนพึ่งหัด python จะไปแบบช้ามาก และสามารถ copy paste ได้เลย และจะใช้แค่ Pandas เท่านั้นนน !!

และคนที่ไม่มีพื้น Data Science ไม่ต้องกังวลครับ อันนี้เป็นเบสิกมากๆ เรียกว่า เป็นทักษะ ในการดูความสัมพันธ์ที่พื้นฐานสุดๆ คือ ดูว่า หุ้นสองตัว ห่างกันได้ แค่ไหน? ถ้าห่างเยอะ มันมีโอกาสกลับมาบรรจบกัน เหมือนในอดีตรึเปล่า?

เพื่อความสะดวกในการ copy code ไปแปะเพื่อให้เห็นผลลัพธ์ เราจะดึงข้อมูลราคาจาก ในด้วย yahoo API กันครับ

Step 1: install packages ที่จำเป็น

เริ่มแรกกันเลย ลง package pandas-datareader กันก่อน วิธีลงก็เปิด command promt ขึ้นมาแล้ว copy ไปวาง หรือพิมพ์ตาม

pip install pandas-datareader

หรือถ้าไม่ชอบผ่าน pip ลงผ่าน Anaconda promt ก็ได้

conda install pandas-datareader

มันจะบอกให้ตอบ [y/n] ตอบ y

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

Package นี้สามารถดึงข้อมูลได้จากหลายๆ source เช่น yahoo, google,quandl และอื่นๆ ตามลิ้งนี้

อ่านความวิเศษวิโสของ packages นี้ได้ที่ https://pandas-datareader.readthedocs.io/en/latest/whatsnew.html#v0-6-0-january-24-2018

Step 2: ดึงข้อมูล

เราจะดึงข้อมูลจาก yahoo กันผ่าน pandas-datareader โดยใช้ .get_data_yahoo ทั้ง PTTGC และ TOP ถ้ามัน error ก็กดรันซ้ำอีกรอบนะครับ

ลองข้อมูล 3 เดือนกันดีกว่า ตั้งแต่ 1 ต.ค. 60

import pandas_datareader as pdr
import datetime
#get PTTGC data
pttgc = pdr.get_data_yahoo(‘PTTGC.BK’, start = datetime.datetime(2017, 10, 1), end = datetime.datetime ( 2018, 1, 1))
#get TOP data
top = pdr.get_data_yahoo(‘TOP.BK’, start = datetime.datetime(2017, 10, 1), end = datetime.datetime ( 2018, 1, 1))

ข้อมูลจะถูกจัดเก็บเข้าเป็น Dataframe ของ pandas ให้เอง พร้อมตั้งวันที่ให้เป็น Index ครับ

Dataframe คืออะไร? ให้จินตนาการถึง Excel ที่เก็บข้อมูลครับ ได้มาก็จะเป็นแบบนี้

เห็นมั้ย เหมือน ตารางใน excel น่ะแหละ แต่มันเหนือกว่านั้นมาก (จะค่อยๆอธิบาย)

Step 3: ได้ข้อมูลมาแล้ว เช็คความถูกต้องเสมอ

เราจะได้ตารางข้อมูล มา ลองเช็คง่ายๆ ว่ามาครบมั้ย ดู .head() , .tail()

pttgc.head()
pttgc.tail()
top.head()
top.tail()

pttgc.head() ผลลัพธ์ ออกมา อ้าวทำไมเริ่ม วันที่ 2 เราให้ดึงวันที่ 1 … เขาตัดวันหยุดให้นะครับซึ่งเป็นเรื่องที่ดีมากสำหรับเรา เพราะไม่ต้องมาตัดเอง

pttgc.tail() เช็คว่าได้ถึงวันที่ีเท่าไหร่ … จะพบว่าได้ถึงวันที่ 29 เพราะ 30 เป็นวันหยุด

**ตรงนี้ต้องระวังดีๆ เพราะถ้ากดไปถึงวันที่ 1 มกราคม มันจะไปดึงวันที่ 3 มกราคม ซึ่งเป็นวันทำการถัดไป มาแล้วข้อมูลวันที่ 3 มกราแสดงมั่วครับ เวลาคาบเกี่ยววันหยุดมักจะงงเสมอ***

Step 4: ลองสร้างกราฟกันเลย

เริ่มจากเบสิกสุดๆก่อนเลยครับ ปกติจะใช้ matplotlib แต่เมื่อไม่นานมานี้ pandas dataframe สามารถสร้างกราฟได้เลยแบบสวยๆ พร้อม column label

ลองใส่คำสั่งนี้เลยครับ ไม่ต้อง import อะไรเพิ่มเติม

pttgc.plot(y='Adj Close')
จะได้แบบนี้นะจ๊ะ

เรียกได้ว่าเป็นการวาดกราฟที่หยาบโพลน ไม่มี legend แกนใดๆ แต่อย่างน้อยก็ได้ตามที่ต้องการ รวดเร็วทันใจ

แต่ถ้าใครกดแล้วไม่ออก ก็ต้อง import matplotlib เข้ามาก่อนนะครับ แล้วไปทำแบบปกติ

Step 5: รวมข้อมูล หุ้น2 ตัวมาอยู่ด้วยกันก่อน

  1. เราจะ สร้าง dataframe ใหม่ โดย refer จากอันเก่า โดยดึง Adj Close (ราคาปิดที่มีการแปลงข้อมูลให้ถูกต้องแล้ว เพราะหุ้นแตกพาร์ เพิ่มทุนได้)
import pandas as pddf = pd.DataFrame({'PTTGC' :pttgc['Adj Close'],'TOP': top['Adj Close']})

*ด้วยเหตุผลประหลาด หากใครติด error ซึ่งผมเองก็เจอ ทำให้มันไป ต่อกันแบบ งงๆ ใน row สุดท้ายก็เอาออกไป*

df = df.drop(df.index[-1])

2. สร้าง column ที่จับหุ้นสองตัวนี้มาลบกัน เพื่อดูส่วนต่าง โดยเอา TOP — PTTGC

df['diff'] = df['TOP'] - df['PTTGC']

3. คราวนี้ลองมาดู ว่ามันเป็นยังไง พล็อตกราฟกันแบบทื่อๆไปเลยครับ ตกแต่งทีหลัง

df.plot()
ผลลัพธ์ที่ได้

ถ้าใช้ Jupyter Notebook ก็เติม %matplotlib inline เข้าไปก่อนนะครับ

%matplotlib inline
df.plot()

Step 6: มาวิเคราะห์เชิงลึกมากขึ้น

ก่อนอื่น เรามาสำรวจกันก่อนครับ ว่า ปกติส่วนต่างราคานั้นอยู่ในช่วงเท่าไหร่

df.plot.hist(y='diff',bin=10)
Distribution ของส่วนต่างราคา TOP — PTTGC ในช่วงเวลาดังกล่าว

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

แต่ใน time frame ที่ยาวขึ้น มันควรจะเป็น เข้าใกล้ Normal Distribution มากขึ้นเรื่อยๆ ตัวอย่างที่ยกมาสั้นแค่สามเดือน มันสั้นเกินไป จะเน้นสอน Concept ครับ

เริ่มคำนวนกันเลยดีกว่าครับ ใช้หลักการแบบ เบสิกๆ ที่เรียกว่า mean reversal (ก็คือเชื่อว่า มันจะกลับมาที่ค่าเฉลี่ย แม้รูปข้างบนมันจะไม่ได้เป็น Normal Distribution แต่ถ้าเราซื้อแถวๆตอนห่างกัน 14 บาท ก็โอกาสขาดทุนต่ำ รอไปขายตอนมันขึ้นสูง)

แล้วเข้าซื้อเมื่อไหร่ ก็เข้าเมื่อมันเบี้ยวไปมากๆ ที่ 1 ส่วนเบี่ยงเบนมาตรฐาน (std) เป็นอย่างน้อย …

ทำไมต้อง 1 std? (สำหรับคนที่ ไม่รู้ ใครรู้แล้วข้ามเลยครับ)

ในทางสถิติ เปรียบผลตอบแทนของหุ้นเป็น Normal Distribution (ซึ่งจริงๆก็ไม่ใช่ ภูเขาจะสูงและผอมกว่า ในขณะที่ ฐานจะบานออกเป็น Fat Tail)

Normal Distribution เป็น Bell Curve นะ

เป็นที่รู้กันว่า โดยเวลาปกติ หุ้นจะแกว่งในช่วง -1 ถึง 1 std (68%) และเวลาวิกฤตจะลงไปถึง -2 std จาก mean ซึ่งก็หมายถึงว่ามีโอกาสเกิดขึ้น 5% นั่นเอง (แต่ถ้าเอาแบบเหนือๆ ก็ไปใช้ VaR 99% 1 tail บน อดีต จริงๆ)

เมื่อใดก็ตามที่ราคาหลุดมาไกลเกิน 1 std ก็แปลว่าผิดปกติ ก็เป็นจุดแรกที่นักลงทุนจะเริ่มเข้าซื้อกัน ผิดทางก็คัท ส่วนใครจะใช้ 2 std เลยก็ได้ แต่ก็ต้องรอนานหน่อยนะครับ

กลับมาเคาะเลขกันต่อ

ส่วนต่างเฉลี่ย (mean) และส่วนเบี่ยงเบนมาตรฐาน (std) ใช้คำสั่งตามนี้ได้เลยครับ จะได้ mean ประมาณ 18 std ประมาณ 3 ก็คือมีโอกาส 68% ที่ ส่วนต่างราคา จะวิ่งในช่วง 15–21 บาท ถ้าเป็นไปตาม Normal Distribution … ซึ่งมันไม่เป็น นะจ๊ะ แต่ไม่ต้องห่วง โลกเราไม่ได้ต้องการความ perfect ไม่มีอะไรเป็นไปตาม Normal Distribution หรอกครับ เราใช้มันพอเป็น Concept ก็พอ ไม่อย่างนั้นต้องใช้ t-distribution ไว้สอนกันทีหลัง เชิงลึก

df[‘diff’].mean()
df['diff'].std()

คราวนี้ มาแสดงผลเป็นกราฟกันดีกว่า จะได้เห็นภาพชัด ก่อนอื่นเราต้อง นำค่า mean และ SD และที่ได้ใส่เข้าไปใน Dataframe ที่เก็บข้อมูลหุ้นของเรากันก่อน โดยเราจะบวกเลขไปเลยว่า -1SD และ +1SD จากค่า mean คือเท่าไหร่

df['mean'] = df['diff'].mean()
df['-1SD'] = df['diff'].mean() - df['diff'].std()
df['+1SD'] = df['diff'].mean() + df['diff'].std()
#visualized
df.plot(y=['diff','-1SD','mean','+1SD'])
TOP — PTTGC

จะเห็นได้ว่า โดยเฉลี่ยหุ้น TOP จะมากกว่า PTTGC 18 บาท (เส้นเขียว)

ในบางช่วง TOP ห่าง PTTGC สูงถึง 28 บาท ซึ่งเกิดขึ้นแค่ครั้งเดียว ในขณะที่ห่างสูง 22 บาท นั้นเกิดขึ้นน้อยกว่า (แถวๆเส้นแดง)

กรณีนี้ ให้ ขาย TOP ซื้อ PTTGC เพื่อรอขายตอนที่กลับมาห่างน้อยกว่า 22 บาท (เช่นเส้นเขียว ที่ 18 บาท)

ในบางช่วง TOP ห่าง PTTGC เพียง 14 บาท จากรูปจะเห็นว่าโอกาสต่ำกว่านี้มีน้อยครั้ง

กรณีนี้ ให้ ซื้อ TOP ขาย PTTGC เพื่อรอขายตอนที่กลับมาห่างมากกว่า 14 บาท (เช่นเส้นเขียว ที่ 18 บาท)

วิธีนี้เป็นที่นิยมเพราะ ไม่ต้องกลัวตลาดหุ้นตก เป็นการเล่น spread ระหว่างหุ้น 2 ตัวครับ

จับทั้งหมดมารวมกัน จะพบว่าใช้ code เพียง 13 บรรทัดเท่านั้น ก็สามารถได้คำตอบแล้ว (เป็นแก้ bug ไป 1 และ import ไป 3 จริงๆแล้วแค่ 9 บรรทัดเท่านั้นเอง)

import pandas_datareader as pdr
import datetime
import pandas as pd
#get PTTGC data
pttgc = pdr.get_data_yahoo(‘PTTGC.BK’, start = datetime.datetime(2017, 10, 1), end = datetime.datetime ( 2018, 1, 1))
#get TOP data
top = pdr.get_data_yahoo(‘TOP.BK’, start = datetime.datetime(2017, 10, 1), end = datetime.datetime ( 2018, 1, 1))
#get PTTGC TOP together in one table
df = pd.DataFrame({'PTTGC' :pttgc['Adj Close'],'TOP': top['Adj Close']})
#delete error in last row
df = df.drop(df.index[-1])
#find difference between 2 stocks and characteristic
df['diff'] = df['TOP'] - df['PTTGC']
df['mean'] = df['diff'].mean()
df['-1SD'] = df['diff'].mean() - df['diff'].std()
df['+1SD'] = df['diff'].mean() + df['diff'].std()
#visualized
df.plot(y=['diff','-1SD','mean','+1SD'])

ลองเล่นกันดูนะครับ ขอให้ทุกท่านโชคดีกับการลงทุน เอาชัวร์ๆก็รอ 2SD ครับ และใช้ data ให้ยาวขึ้น …. รอติดตาม

--

--