Two Trading States Processing Design Pattern

Context

state ที่หนึ่งคือการ predict properties สำหรับ new trade ด้วยการประมวลผลแบบ vectorization และ state ที่สองคือการ predict action สำหรับ active trade ด้วยการประมวลผลแบบ loop

Background

เราไม่สามารถเตรียมข้อมูลสถานะบัญชี, พอร์ตโฟลิโอ, position, trade, order ล่วงหน้าสำหรับทุกแท่งราคาได้หากยังไม่ได้ระบุ trade signal และหากยังไม่ได้เกิดการเทรด ให้นึกถึงเรามีแค่ข้อมูลราคา open, high, low, close จำนวน 10,000 แท่งราคา แต่ยังไม่ได้ระบุ trade signal เลย ดังนั้นเราเตรียมข้อมูลล่วงหน้าได้แค่ยอดเงินลงทุนในบัญชี (equity) เท่านั้น

เงื่อนไขในตลาดทั่วไป ข้อมูล trade จะเกิดขึ้นได้เมื่อมีข้อมูล order (เกิดการส่ง order แล้ว order match เรียบร้อย) ซึ่งข้อมูล order จะเกิดได้ก็เมื่อมีข้อมูล trade signal (เช่น buy หรือ long) ข้อมูล trade signal เกิดขึ้นได้เมื่อมีข้อมูลราคาแ แล้วเมื่อ trade เกิดขึ้น ก็จะเกิดข้อมูล position หรือ trade position และส่งผลต่อยอดเงินในบัญชีเปลี่ยนแปลงไป เพราะต้องหักค่าใช้จ่ายเช่น ค่าซื้อหุ้น, ค่า transaction cost เช่น คอมมิชชั่น นอกจากนี้เมื่อ trade จบลง (เช่น ปิดสถานะ หรือขายหุ้น) ก็จะเกิดสถิติเทรด (trade statistics) ที่นำ historical trades มาคำนวณสถิติ

ดังนั้นในการเทรดจริง ข้อมูลสถานะบัญชี, พอร์ตโฟลิโอ, position, trade, order จึงเปลี่ยนไปเรื่อยๆ เรียงตามลำดับเวลา (time series) ทำให้เราไม่สามารถจำลอง (simulate) ข้อมูลเหล่านี้ล่วงหน้าได้ หากยังไม่เกิด trade properties เช่น trade signal, SL (stop loss), TP (take profit), position size

เราจึงแบ่ง trading state ออกได้เป็น 2 ช่วง ได้แก่ state ช่วงก่อนเข้าเทรด (position ว่าง) กับ state ช่วงกำลังเทรด (ถือ position อยู่ ยังไม่ได้ปิดหรือขาย)

ข้อสังเกตเพื่อให้จำง่ายๆ คือ state แรกไม่จำเป็นต้องใช้ข้อมูลสถานะบัญชี, พอร์ตโฟลิโอ, position, trade, order เสมอไป แต่ใน state ที่สองมักจำเป็นต้องใช้ ดังนั้นใน state แรกจึงใช้แค่ข้อมูลราคา/technical data/fundamental data ก็ได้

Problem

ขณะบอทรัน ต้องการ feed ข้อมูลสถานะบัญชี, พอร์ตโฟลิโอ, position, trade, order ล่าสุดประกอบการ predict ด้วยหรือไม่?

  • การออกแบบการ predict ใดๆ ที่ต้องใช้ข้อมูลสถานะบัญชี, พอร์ตโฟลิโอ, position, trade, order ประกอบ สามารถออกแบบให้ประมวลผลแบบ vectorization ได้ แต่ทำได้ยาก ผมเคยทดลองทำ และประสบความสำเร็จ พบว่าสามารถทำได้ รันเร็วมาก แต่ก็มาเจออุปสรรคว่า แต่ละโบรกเกอร์/ตลาด/แพลตฟอร์ม/API มีเงื่อนไขแตกต่างกันบ้างไม่มากก็น้อย การทำระบบให้รองรับกับทั้งหมดเหล่านี้จึงยากมาก จะทำให้โค้ดซับซ้อนเกินไป และยากต่อการ maintain
  • การรัน backtesting แบบ loop ประมวลผลช้ามาก เพราะใช้ซีพียูแค่ 1 คอร์ หากต้อง backtest ย้อนหลังไกลมาก เช่น 1.5 ล้านแท่งราคา จะต้องรัน 1.5 ล้านรอบ
  • การประมวผลแบบ vectorization เหมาะกับการ predict ที่ใช้แค่ข้อมูลราคา, technical data, fundamental data ประมวลผลรวดเร็วก็จริง แต่ไม่เหมาะกับ trade strategy ที่มีความซับซ้อนมากได้ เช่น การ predict ที่ต้องใช้ข้อมูล unrealized profit, average cost, สถิติการเทรด ประกอบ

Solution

แบ่งการทำงานของ trade model ออกเป็น 2 ช่วง (state) ได้แก่ ช่วงแรกที่ประมวลผลทุกแท่งราคาแบบ vectorization เพื่อความรวดเร็ว และ ช่วงที่สองที่ประมวลผลแบบ loop เพื่อการวิเคราะห์หรือ predict บางอย่างที่ต้องการความสมจริงในแง่การเทรดจริงที่ต้องทำเรียงตามลำดับเวลา

state แรกจึงสามารถใช้การประมวลผลแบบ vectorization ได้ ทำให้รันได้เร็วและใช้เวลาน้อย แต่ state ที่สองข้อมูลสถานะต่างๆ มีค่าเปลี่ยนไปเรื่อยๆ ตามเวลาที่เปลี่ยนไป state ที่สองจึงควรประมวลผลแบบ loop ที่ประมวลผลเรียงลำดับเวลาไปเรื่อยๆ ซึ่งใช้เวลามากกว่า

การ predict ที่ไม่ต้องใช้ข้อมูลสถานะฯ

การ predict trade properties เช่น trade signal, SL, TP, position size,… ไม่ควรใช้ข้อมูลสถานะบัญชี, พอร์ตโฟลิโอ, position, trade, order ประกอบ แบบนี้จะทำให้ตอน backtest สามารถใช้ historical price data มา predict ทุกแท่งราคารวดเดียวในแบบ vectorization ได้เลย

ยกตัวอย่างเช่น เรามีข้อมูลทองคำ ไทม์เฟรม 5 นาที ย้อนหลัง 20 ปี จะมีปริมาณราว 1.5 ล้านแท่งราคาเลยทีเดียว การประมวลผลแบบ vectorization จะโหลดข้อมูลทั้งหมดเข้าหน่วยความจำในเครื่อง หรือหน่วยความจำใน GPU (graphics processing unit หรือการ์ดจอ) แล้วประมวลผลทั้ง 1.5 ล้านแท่งราคารวดเดียว!!! หากเป็นการประมวลผลแบบ loop จะวนลูป predict ทีละแท่งทั้งหมด 1.5 ล้านรอบ!!! ซึ่งการประมวลผลแบบ vectorization สามารถแบ่งข้อมูลเป็นหลายส่วน แล้วประมวลผลด้วยซีพียู หรือ GPU หลายคอร์พร้อมกันได้ด้วย

การประมวลผลแบบ vectorization ยังรองรับการประมวลผลแบบ parallelization หรือแบบคู่ขนานได้ เช่นในการประมวลผลด้วยซีพียู หรือ GPU หลายคอร์พร้อมกัน จึงทำให้ใช้เวลาน้อยกว่า ทำให้ predict ข้อมูลทุกแท่งราคาเสร็จเร็วกว่าการประมวลผลแบบ loop

ยกตัวอย่างเปรียบเทียบ หากใครเคยรัน backtest ใน MetaTrader กับ Amibroker จะเห็นความแตกต่างชัดเจน เพราะ MetaTrader รัน backtest แบบ loop จึงรันช้ามากกก แต่ Amibroker รันแบบ vectorization หรือใน Amibroker เรียกว่าการประมวลผลแบบ array processing ซึ่งเร็วมาก รัน backtest หลักล้านแท่งราคาเสร็จในไม่กี่นาทีหรือไม่กี่วินาทีเท่านั้น

MetaTrader มีจุดเด่นที่ เน้นการเทรดจริง (ไม่เก่งและไม่นิยมใช้ในงานวิเคราะห์) ดังนั้นการรัน backtest จึงประมวลผลแบบ loop เพราะรันทีละแท่งราคาจึงมีความสมจริง ส่วน Amibroker มีจุดเด่นที่ เน้นการวิเคราะห์ (ไม่เก่งและไม่นิยมใช้ในงานเทรดจริง) ดังนั้นการรัน backtest จึงประมวลผลแบบ vectorization (จริงๆ สามารถเขียนโค้ดให้ Amibroker รัน backtest แบบ loop ให้มีความสมจริงได้ แต่โค้ดจะมีความซับซ้อนมาก)

ดังนั้นทั้ง MetaTrader กับ Amibroker จึงเก่งเพียงด้านใดด้านหนึ่ง ไม่ได้เก่งทั้ง backtest และ เทรดจริง สองด้าน ทางออกคือ การใช้ภาษาโปรแกรมอื่นพัฒนา trade model และบอท ซึ่งภาษาที่เขียนง่าย มีประสิทธิภาพดี และได้รับความนิยมที่สุดก็คือ Python นั่นเอง

สำหรับการ predict trade properties เช่น trade signal, SL, TP, position size,… แบบ vectorization ในตอน execution ไม่มีผลมากนัก เพราะโดยปกติตอนรัน backtest มักประมวลผลกับแท่งราคาจำนวนมากกว่าตอนรัน execution เช่น เราอาจรัน backtest ย้อนหลัง 1.5 ล้านแท่งราคา แต่ตอนรันจริงใช้แค่ 3,000 แท่งราคาเท่านั้นก็ได้

ดังนั้นการประมวลผล trade signal สำหรับการเข้าเทรด ที่ใช้การประมวลผลแบบ vectorization จึงมีผลต่อความเร็วตอนรัน backtest มากนั่นเอง จึงต้องยอมทำใจสักเล็กน้อย ที่จะไม่ใช้ข้อมูลสถานะของบัญชี, พอร์ตโฟลิโอ, position, trade, order ตอน predict trade signal, SL, TP, position size

การ predict ที่ต้องใช้ข้อมูลสถานะฯ

ไม่ใช่ทุก trade strategy ที่จำเป็นต้องใช้ข้อมูลบัญชี, พอร์ตโฟลิโอ, position, trade, order ประกอบการวิเคราะห์และการ predict ดังนั้นบาง trade model จึงไม่ต้องประมวลผลแบบ loop ในส่วนนี้เลยก็ได้ ทำให้ประมวลผล trade model รวดเร็วยิ่งขึ้น

การวิเคราะห์ประกอบการบริหารเทรด (trade management) มักเลี่ยงไม่ได้ที่ต้องใช้ข้อมูลบัญชี, พอร์ตโฟลิโอ, position, trade, order ประกอบ และข้อมูลเหล่านี้เปลี่ยนแปลงไปตามลำดับเวลา จึงเลี่ยงไม่ได้ที่ควรประมวลผลแบบ loop ซึ่งเหมาะสมต่อการจำลองความสมจริงแแบบตอนเทรดจริงได้ดี

ในการรัน backtest เป็นเรื่องปกติที่เรามักรันซ้ำไปมาหลายครั้งเพื่อดูผลที่แตกต่างไป เราจึงมักลองเปลี่ยนค่า setting และ เปลี่ยนช่วงวันทดสอบ แม้ทุกครั้งที่รัน backtest เราไม่ได้แก้ไขโค้ดใดๆ เลย เพียงแค่แก้ค่า setting และ เปลี่ยนช่วงวันทดสอบเท่านั้น แต่หากทุกครั้งต้อง predict trade entry properties (trade signal, SL, TP, position size) ใหม่เสมอ ก็จะทำให้ใช้เวลานานขึ้น

ดังนั้นหาก predict trade entry properties เพียงครั้งเดียวแล้วเก็บผล predict เอาไว้ เช่น save เป็นไฟล์เก็บไว้ หรือเก็บเป็นอ็อบเจ็คต์ไว้ในหน่วยความจำ ทำให้แต่ละครั้งที่รัน backtest ใหม่ ไม่ต้องเสียเวลา predict ในส่วนนี้ซ้ำอีก จึงช่วยให้รัน backtest เสร็จเร็วขึ้น

สรุป

การแบ่งการประมวลผล trade state ออกเป็น 2 ช่วง ช่วยให้การรัน backtest เร็วขึ้น และโค้ดมีความเป็น modularity มากขึ้น เรียบง่ายขึ้น แบ่งลอจิกเป็นสัดส่วนเป็นระเบียบดีขึ้น ช่วยให้ maintain โค้ดง่ายขึ้น

เทมเพลตโค้ด trade model ในระบบ/คอร์ส กลุ่ม Deep Quant ได้แบ่งฟังก์ชั่นต่างๆ ภายในออกเป็น 2 ส่วนรองรับเรียบร้อย

ตัวอย่างการ predict ใน trading state แรก:

  • predict open buy
  • predict open sell
  • predict close buy
  • predict close sell
  • predict filter open buy
  • predict filter open sell
  • predict filter close buy
  • predict filter close sell
  • predict stop loss
  • predict take profit
  • predict position size

ตัวอย่างการ predict ใน trading state ที่สอง:

  • predict scale in (เปิดเพิ่ม หรือขยายสถานะ)
  • predict scale out (ทยอยปิด หรือลดสถานะ)
  • predict modify stop loss (trailing loss stop)
  • predict modify take profit (trailing profit stop)

Leave a Reply

Discover more from

Subscribe now to keep reading and get access to the full archive.

Continue reading