เขียน Strategy บน Freqtrade กัน

รู้จักกับ Strategy เขียนโค้ดด้วยภาษา Python พร้อมเพิ่มประสิทธิภาพให้กับบอทกัน
Feature Image

สวัสดีครับ วันนี้เราจะมาเขียน Strategy บน Freqtrade กัน จะเป็นอย่างไรนั้นมาดูกัน

สำหรับใครที่ยังไม่รู้จักว่า Freqtrade คืออะไร สามารถดูได้ตามด้านล่างนี้เลย

เอาล่ะ เรามาเริ่มกันเลย

ก่อนอื่นเลย เรามารู้จักกับคำว่า Strategy คืออะไร

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

  • Indicators
  • Entry strategy rules
  • Exit strategy rules
  • Minimal ROI
  • Stoploss strongly

Indicators

Indicator ถ้าแปลเป็นไทยก็คือ ตัวบ่งชี้ แต่ถ้าบอกว่าตัวบ่งชี้ก็จะงง ๆ ถ้าในความหมายการเทรด Indicator ก็คือ เครื่องมือที่ใช้สำหรับวิเคราะห์ข้อมูลหรือกราฟต่าง ๆ ไปในทิศทางที่เราต้องการ โดยที่ Indicator นั้นมีให้เลือกหลายตัวมาก ๆ ที่ผู้เขียนใช้บ่อยก็จะมี RSI, Bollinger bands, MACD แนะนำว่าให้ศึกษาทีละตัวให้เข้าใจ แล้วค่อยเอาหลาย ๆ ตัวมาประกอบกัน ซึ่งบทความนี้ขอไม่เจาะจงเรื่องของ Indicators แต่ละตัว เดี๋ยวจะพูดถึงในคลิปแทน

Entry strategy rules

Entry strategy rules เป็นกฎการเข้าเทรดของกลยุทธ์เรา เวลาเราส่งคำสั่งซื้อ เราสามารถตั้งกฎหรือเงื่อนไขได้จากตรงนี้ เช่น สมมติเราใช้ RSI Indicator เราตั้งว่า ที่ RSI ต่ำกว่า 30 ให้ทำการเปิดออเดอร์หรือซื้อเหรียญ

Exit strategy rules

Exit strategy rules เป็นกฎการออกจากการเทรดของกลยุทธ์เรา เวลาเราส่งคำสั่งขาย เราสามารถตั้งกฎหรือเงื่อนไขการขายได้ เช่น สมมติเราใช้ RSI Indicator เราตั้งว่า ที่ RSI สูงกว่า 70 ให้ทำการปิดออเดอร์หรือขายเหรียญ

Minimal ROI

Minimal ROI เป็นการกำหนด ROI ขั้นต่ำ โดยเรากำหนดได้ว่า กี่นาที และกำไรเท่าไรถึงให้ทำการขายหรือออกจากการเทรด

Stoploss

Stoploss เป็นการป้องกันความเสี่ยงเวลาเราขาดทุนมาก ๆ โดยเราสามารถตั้งได้ว่า ถ้าขาดทุนกี่เปอร์เซ็นต์ให้ทำการขายเพื่อให้เราขาดทุนน้อยลง

เอาล่ะ เรารู้จักรายละเอียดของกลยุทธ์การเทรดไปคร่าว ๆ แล้ว เรามาดูในส่วนของ Source Code กันว่าเป็นอย่างไร โดยผู้เขียนจะใช้ตามตัวอย่าง sample_strategy.py จะเป็นแบบไหนนั้น เรามาดูกัน โดยเอาเฉพาะส่วนที่สำคัญ ๆ นะ

can_short: bool = False

can_short เป็นการตั้งค่าเกี่ยวกับการเทรดแบบ Future ก็คือการเทรดแบบสัญญา การเทรดตลาด Future จะมีการเทรดได้ทั้งขาขึ้นและขาลง เราจะเรียกกันว่า Long กับ Short โดย Long คือ การทำกำไรจากราคาเหรียญที่สูงขึ้น ส่วน Short คือ การทำกำไรจากราคาเหรียญที่ต่ำลง ซึ่ง can_short ไม่สามารถใช้กับตลาดปกติหรือ Spot ได้ โดย Default จะถูกตั้งเป็น False อยู่แล้ว ถ้าต้องการใช้งานก็ตั้งค่าเป็น True

minimal_roi = {
        "60": 0.01,
        "30": 0.02,
        "0": 0.04
    }

minimal_roi เป็นการกำหนด ROI ขั้นต่ำที่เราจะได้ ซึ่งค่า ROI ความหมายของมันก็คือผลตอบแทนหรือกำไรที่เราจะได้ โดยค่า roi ในที่นี้จะคิดเป็น % โดยจากด้านบนเราจะเห็นว่าในส่วนของด้านหน้า เช่น “60”, “30”, “0” จะเป็นส่วนของเวลา มีหน่วยเป็นนาที ส่วนด้านหลังที่เป็น 0.01, 0.02, 0.04 จะเป็นในส่วนของผลกำไรที่ได้ โดยที่ 0.01 = 1% นั่นเอง ถ้าใช้ตัวอย่างตามโค้ดด้านบน จะสรุปได้ว่า

  • เมื่อมีการเปิดออเดอร์แล้วปรากฎว่าเหรียญนั้นทำกำไรได้มากกว่าหรือเท่ากับ 4% ก็จะทำการปิดออเดอร์หรือขายเหรียญนั้น แต่ถ้าไม่ถึงก็จะไปดูเงื่อนไขต่อไป
  • ตั้งแต่ 30 นาทีเป็นต้นไป ถ้ากำไรได้มากกว่าหรือเท่ากับ 2% จะทำการปิดออเดอร์หรือขายเหรียญ แต่ถ้ายังไม่ถึงก็ไปดูเงื่อนไขต่อไป
  • ตั้งแต่ 60 นาทีเป็นต้นไป ถ้ากำไรได้มากกว่าหรือเท่ากับ 1% จะทำการปิดออเดอร์หรือขายเหรียญ

ถ้าเกิดว่าบอทที่เราเปิดนั้น มีการซื้อเหรียญไว้แล้ว แต่ก็ยังไม่เข้าเงื่อนไขข้างบน ก็แสดงว่าเหรียญที่เราถือนั้นยังขาดทุนอยู่ แปลว่ามีโอกาสที่จะเข้าเงื่อนไข Stoploss ที่จะพูดถึงในต่อ ๆ ไป

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

stoploss = -0.10

stoploss เป็นการป้องกันความเสี่ยงเวลาเหรียญที่เราซื้อขาดทุน เป็นตัวช่วยสำหรับลดการขาดทุนของเรานั่นเอง เราควรตั้งไว้ ซึ่งจากตัวอย่างเป็นการตั้ง stoploss = -0.10 ก็คือ ถ้าขาดทุนมากกว่าหรือเท่ากับ 10% ให้ทำการ stoploss ทันที

trailing_stop = False

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

timeframe = '5m'

timeframe เป็นข้อมูลของแท่งเทียน มีผลกับกลยุทธ์รวมถึงบอทเทรดของเราด้วย Timeframe ที่เราคุ้นเคยหรือใช้กันบ่อย ๆ จะมี 5m, 15m, 30m, 1h, 4h, 1d โดยที่ m จะเป็นนาที ส่วน h จะเป็นชั่วโมง ส่วน d จะเป็นวัน

buy_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True)
    sell_rsi = IntParameter(low=50, high=100, default=70, space='sell', optimize=True, load=True)
    short_rsi = IntParameter(low=51, high=100, default=70, space='sell', optimize=True, load=True)
    exit_short_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True)

ในส่วนนี้เราจะเห็นว่าเป็นการประกาศตัวแปร แต่ที่สำคัญก็คือ ในส่วนของ IntParameter ซึ่งตัว IntParameter เป็นการกำหนดช่วงหรือ Range ไว้สำหรับการทำ Hyperopt เพื่อหาค่าที่เหมาะสมกับกลยุทธ์ของเรา ในส่วนนี้จะมีผลกับการเปิด/ปิดออเดอร์ โดยใน IntParameter จะมีส่วนที่สำคัญอยู่ 3 ส่วน ก็คือ

  • low = ค่าต่ำสุดของ Range ที่ใช้ทำ Hyperopt
  • high = ค่าสูงสุดของ Range ที่ใช้ทำ Hyperopt
  • default = ค่าเริ่มต้น ถ้าเราไม่ทำ Hyperopt ตัวโปรแกรมก็จะใช้ค่านี้

order_types = {
        'entry': 'limit',
        'exit': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

order_types เป็นชนิดหรือรูปแบบการซื้อขาย รายละเอียดตามนี้เลย

  • entry เป็นการเข้าซื้อหรือเปิดออเดอร์ เราสามารถเลือกได้ว่าจะเอาแบบไหน โดยที่ limit จะเป็นการตั้งราคาไว้รอการจับคู่ ส่วน Market ก็คือ การเปิดออเดอร์โดยเลือกราคาที่ดีที่สุดเลยตามจำนวนเงินที่เราตั้งในการเปิดออเดอร์
  • exit เป็นการออกจากการขายหรือปิดออเดอร์ เหมือนกับ entry ว่าจะเลือกแบบ limit หรือ market
  • stoploss เป็นการตั้งค่าว่า เวลาเกิดการ Stoploss ให้ทำการซื้อขายแบบไหน เหมือนกับ entry, exit เลย สามารถเลือกเป็นแบบ limit หรือ market ก็ได้

populate_indicators

เป็นส่วนของฟังก์ชันในการเพิ่ม Indicator เข้าไป

populate_entry_trend

เป็นส่วนของฟังก์ชันในการเปิดออเดอร์

populate_exit_trend

เป็นส่วนของฟังก์ชันในการปิดออเดอร์

เอาล่ะ เรารู้จักกันไปบ้างแล้วกับรายละเอียดของกลยุทธ์ เดี๋ยวเราจะมาสร้างอันใหม่กัน โดยเราสามารถใช้คำสั่งง่าย ๆ โดยเราเปิด Command Line บน Docker แล้วพิมพ์ได้เลย

freqtrade new-strategy --strategy AwesomeStrategy --template minimal

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

เปิด Visual Studio Code เปิดโปรเจคของเราขึ้นมา และดูว่ามีไฟล์ที่ชื่อว่า AwesomeStrategy.py ขึ้นมามั้ย โดยที่ไฟล์ AwesomeStrategy.py จะอยู่ที่ /userdata/strategies โดยตัวกลยุทธ์ที่ได้มาเขียนด้วยภาษา Python

ผู้เขียนแนะนำว่าให้ใช้ –template เป็นแบบ minimal ไว้ เพราะมันมีรายละเอียดข้างในโค้ดค่อนข้างน้อย เรียบง่าย คืออ่านแล้วเข้าใจง่ายว่าบรรทัดไหนทำอะไร เหมาะแก่การเริ่มต้นและเขียนเอง และแนะนำว่าให้ดู sample_strategy.py ควบคู่กันไป

สำหรับตัวอย่างเริ่มต้นที่เราสร้างมา จะใช้ RSI Indicator ในการเปิด/ปิด ออเดอร์ โดยที่ จะเปิดออเดอร์หรือซื้อเหรียญเมื่อ RSI ข้ามเส้นบนที่ 30 และจะทำการปิดออเดอร์หรือขายเหรียญเมื่อ RSI ข้ามเส้นบนที่ 70 อันนี้เป็นตัวอย่างที่เค้ามีให้

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

มันจะมี 2 คำที่ใช้เกี่ยวกับการเขียนแบบนี้ คือ crossed_above กับ crossed_below

  • crossed_above ตามความหมายก็คือ ข้ามด้านบน
  • crossed_below ตามความหมายก็คือ ข้ามด้านล่าง

จากตัวอย่าง เรามีรูปของจริงให้ดู

Binance

เราจะเห็นว่า crossed_above กับ crossed_below เกิดขึ้นได้ทั้ง 2 ฝั่ง คือ ฝั่งขาขึ้นกับขาลง โดยเส้นประ จะเป็นค่า RSI ที่เราตั้งไว้ ในที่นี้ตั้งไว้ 30 เป็นเส้นประล่าง และ 70 เป็นเส้นประบน ถ้าเส้นสีม่วงข้ามเส้นประลงมาด้านล่าง เราจะเรียกว่า Cross Below แต่ถ้าเส้นสีม่วงข้ามเส้นประขึ้นไปด้านบน เราจะเรียกว่า Cross Above แค่นี้เอง (เส้นสีม่วงสามารถเปลี่ยนได้ แต่ตัวอย่างใช้เป็นสีม่วง)

ถามว่าเรารู้ไปทำไมล่ะ คือแบบนี้ ปกติแล้วคำสั่ง crossed_above กับ crossed_below นั้น มันจะมีเฉพาะในส่วนของ Technical Indicator และ Library ที่เกี่ยวข้องกับการเทรด แต่ถ้าคนเคยเขียนโปรแกรมมา โดยปกติแล้ว เราจะเขียนในลักษณะประมาณนี้ ก็คือ ใช้เครื่องหมาย >, =, < แทน ตัวอย่างเช่น (dataframe[‘rsi’] <= self.buy_rsi.value) หรือ (dataframe[‘rsi’] <= 30) ให้ทำการเปิดออเดอร์หรือซื้อเหรียญ และ (dataframe[‘rsi’] >= self.sell_rsi.value) หรือ (dataframe[‘rsi’] >= 70) ให้ทำการปิดออเดอร์หรือขายเหรียญ

จากตัวอย่างที่ว่ามา การเขียนโดยใช้ >= หรือ <= ในการเทรดมันมีข้อเสียก็คือ มันมีโอกาสที่จะเข้าเงื่อนไขตลอดเวลา ก็คือ ถ้าเป็นฝั่งซื้อ มันอาจจะซื้อเหรียญตลอดเวลาถ้าเราไม่ตั้งจำกัดไว้ และถ้าเป็นฝั่งขาย มันอาจจะขายเหรียญตลอดเวลาถ้าเราไม่ตั้งจำกัดไว้เช่นเดียวกัน ผู้เขียนเคยใช้ Freqtrade ตั้ง >= หรือ <= ก็ไม่เคยเจอนะ อาจเพราะว่าผู้เขียนจำกัด max_open_trades ไว้ด้วยเปล่าไม่แน่ใจ แต่เคยเจอตอนเขียนบอทเองที่ไม่ใช้ Freqtrade เลยต้องใช้วิธีการใช้ crossed_above และ crossed_below เข้ามา แต่เราจะต้องเลือกว่าเราจะใช้ crossed_above หรือ crossed_below เพราะมันจะทำแค่ครั้งเดียว คือ จะข้ามเส้นบนลงมา หรือข้ามเส้นล่างขึ้นบน การตั้ง crossed_above และ crossed_below มีผลกับการทำ Backtesting ด้วย

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

อีกเคสหนึ่ง ถ้าเราตั้ง crossed_above ในฝั่งขาย ก็คือ ถ้าเส้นม่วงข้ามเส้นประ RSI ไปมากกว่า 70 ให้ทำการขาย แบบนี้ก็ต้องระวังเหมือนกัน เพราะบางทีเราอาจจะได้กำไรน้อยลง ทางแก้ก็คือ ใช้ crossed_below เพราะ RSI สูงขึ้น เหรียญจะยังไม่ถูกขาย เพราะค่า RSI นั้นถูกคำนวณตามแท่งเทียนย้อนหลังที่เราตั้งไว้ แปลว่า มีโอกาสที่ถ้าราคาสูง และเกิด Side Way (เหรียญราคาไม่ขยับ) ราคาไม่ลงหรืออาจลงมาเล็กน้อย เดี๋ยวค่า RSI มันจะลงมาข้ามเส้นเอง แปลว่าเราจะได้กำไรจากส่วนนี้มากกว่าการใช้ crossed_above

เอาล่ะ หวังว่าผู้อ่านจะเข้าใจในส่วนของ crossed_below กับ crossed_above มากขึ้น แต่ต้องบอกนะ ว่าให้ทำ Backtesting คู่ ๆ กันไป เพราะที่อธิบายข้างบนเป็นการใช้ crossed_below, crossed_above กับ RSI เท่านั้น บางเคส บางกรณี หรือบาง Indicator อาจไม่ตรงตามที่ผู้เขียนเขียนเสมอไป และผู้เขียนอยากให้ใช้ crossed_below, crossed_above มากกว่าการใช้เครื่องหมาย >, =, <

เรามาเขียนโปรแกรมต่อเลย อธิบายซะยาวเลย 555 เอาล่ะ เราจะใช้ Bollenger Band แบบ Timeframe 15m หรือ 15 นาทีกัน

ขั้นแรกเราต้องเพิ่ม Bollinger Band ในฟังก์ชัน populate_indicators

bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']
        dataframe["bb_percent"] = (
            (dataframe["close"] - dataframe["bb_lowerband"]) /
            (dataframe["bb_upperband"] - dataframe["bb_lowerband"])
        )
        dataframe["bb_width"] = (
            (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"]
        )

ในฟังก์ชัน populate_entry_trend ทำการเปลี่ยนเป็นด้านล่างนี้

 dataframe.loc[
            (
                (qtpylib.crossed_above(dataframe['close'], dataframe['bb_lowerband'])) &
                (dataframe['volume'] > 0)  # Make sure Volume is not 0
            ),
            'enter_long'] = 1

ในฟังก์ชัน populate_exit_trend ทำการเปลี่ยนเป็นด้านล่างนี้

dataframe.loc[
            (
                (qtpylib.crossed_below(dataframe['close'], dataframe['bb_upperband'])) &
                (dataframe['volume'] > 0)  # Make sure Volume is not 0
            ),
            'exit_long'] = 1

ทำการเปลี่ยน Timeframe จาก 5m เป็น 15m

timeframe = '15m'

ทำการลบหรือ Comment buy_rsi กับ sell_rsi ไว้ เพราะเราไม่ได้ใช้

ในส่วนของ order_types แก้ไขจาก limit เป็น market

order_types = {
        'entry': 'market',
        'exit': 'market',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

ตั้งค่า price_side ใน config.json เพิ่มเติม เปลี่ยนจาก “same” เป็น “other”

"entry_pricing": {
        "price_side": "other",
        "use_order_book": true,
        "order_book_top": 1,
        "price_last_balance": 0.0,
        "check_depth_of_market": {
            "enabled": false,
            "bids_to_ask_delta": 1
        }
    },
    "exit_pricing":{
        "price_side": "other",
        "use_order_book": true,
        "order_book_top": 1
    },

เอาล่ะ เช็คไฟล์ config.json ให้เรียบร้อย ตามบทความที่แล้วเลย เสร็จแล้วรันกัน

ก่อนอื่นเราต้องโหลดข้อมูลมาก่อน

freqtrade download-data --exchange binance --pairs ETH/USDT XRP/USDT BTC/USDT --timeframes 15m --prepend --timerange 20221101-20221130 --datadir user_data/data/binance/2022-11

เราจะลองกับ 3 เหรียญนี้ก่อน ตามไฟล์ config.json ตั้งค่า Timeframe เป็น 15m และเลือกเป็นวันที่ประมาณนี้ ก็คือ เดือน 11 ปี 2022 นั่นเอง ก็รันเลย (ที่ต้องเลือกเดือนนี้เพราะถ้าเราไม่ตั้งวันที่ไว้ เดี๋ยวผลของ Backtesting อาจจะไม่เหมือนกัน ใครอยากลองเปลี่ยนก็ไม่ว่ากัน)

เสร็จแล้ว เรามาเริ่มรัน Backtesting กันเลย

freqtrade backtesting --strategy AwesomeStrategy --dry-run-wallet 100 --fee 0.001 --config user_data/config.json --datadir user_data/data/binance/2022-11

พอรันเสร็จแล้ว เราก็จะได้ผลของการ Backtesting ออกมา โดยจากที่ผู้เขียนรันจะได้ Total profit % ที่ -13.61% จะเห็นว่าติดลบ แปลว่าถ้าเราเทรด 3 เหรียญนี้ในเดือนพฤศจิกายน ปี 2022 จะขาดทุนประมาณ -13.61%

Freqtrade Backtesting

อ้าว ติดลบ แล้วใช้บอทมันดียังไง คือแบบนี้ เรามีหน้าที่ทำให้บอทนั้นเก่ง และเราต้องเลือกเหรียญที่จะเทรดให้ดี เพราะฉะนั้น เดี๋ยวผู้เขียนจะมา Optimize หรือเพิ่มประสิทธิภาพให้บอทกัน

ขั้นแรกทำการเปลี่ยนชุดเหรียญใหม่ โดยไปแก้ที่ config.json ให้เปลี่ยนเป็นแบบนี้เลย

"pair_whitelist": [
      "VTHO/USDT",
      "VGX/USDT",
      "NKN/USDT",
      "XVG/USDT",
      "T/USDT",
      "BETA/USDT",
      "SUPER/USDT",
      "DCR/USDT",
      "ATOM/USDT",
      "WIN/USDT",
      "ACH/USDT",
      "AKRO/USDT",
      "XTZ/USDT",
      "ORN/USDT",
      "BIFI/USDT",
      "COCOS/USDT",
      "LTO/USDT",
      "LSK/USDT",
      "EPX/USDT",
      "MBOX/USDT"
    ],
    "pair_blacklist": [
      "BNB/.*",
      "ADADOWN/USDT",
      "BNBDOWN/USDT",
      "BTCDOWN/USDT",
      "ETHDOWN/USDT",
      "LINKDOWN/USDT",
      "XRPDOWN/USDT",
      "ADAUP/USDT",
      "BNBUP/USDT",
      "BTCUP/USDT",
      "ETHUP/USDT",
      "LINKUP/USDT",
      "XRPUP/USDT",
      "AUD/USDT",
      "BUSD/USDT",
      "EUR/USDT",
      "GBP/USDT",
      "LUNA/USDT",
      "LUNC/USDT"
    ]

จากนั้นเรามาโหลดข้อมูลกัน

freqtrade download-data --exchange binance --pairs VTHO/USDT VGX/USDT NKN/USDT XVG/USDT T/USDT BETA/USDT SUPER/USDT DCR/USDT ATOM/USDT WIN/USDT ACH/USDT AKRO/USDT XTZ/USDT ORN/USDT BIFI/USDT COCOS/USDT LTO/USDT LSK/USDT EPX/USDT MBOX/USDT --timeframes 15m --prepend --timerange 20221101-20221130 --datadir user_data/data/binance/2022-11

รัน Backtesting กันเลย

freqtrade backtesting --strategy AwesomeStrategy --dry-run-wallet 100 --fee 0.001 --config user_data/config.json --datadir user_data/data/binance/2022-11

รันเสร็จแล้ว ผู้เขียนได้ Total profit % ที่ 33.77%

Freqtrade Backtesting

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

เอาล่ะ เราจะมาดูกันว่า เราจะสามารถเพิ่ม Performance อย่างไรต่อ

ขั้นแรก เราลองไปแก้ไขโค้ดดู เผื่อดีขึ้น ไปที่ฟังก์ชัน populate_exit_trend ลอง Comment dataframe.loc ออก แล้วรันใหม่

# dataframe.loc[
        #     (
        #         (qtpylib.crossed_below(dataframe['close'], dataframe['bb_upperband'])) &
        #         (dataframe['volume'] > 0)  # Make sure Volume is not 0
        #     ),
        #     'exit_long'] = 1

จากนั้นรัน Backtesting ใหม่

freqtrade backtesting --strategy AwesomeStrategy --dry-run-wallet 100 --fee 0.001 --config user_data/config.json --datadir user_data/data/binance/2022-11

รันใหม่ Total profit % ที่ 30.16% แสดงว่าแย่ลง แสดงว่า แบบเดิมดีกว่า ไปเอา Comment ออก

Freqtrade Backtesting
dataframe.loc[
            (
                (qtpylib.crossed_below(dataframe['close'], dataframe['bb_upperband'])) &
                (dataframe['volume'] > 0)  # Make sure Volume is not 0
            ),
            'exit_long'] = 1

เราจะใช้ Hyperopt กัน ก็รันคำสั่งตามด้านล่างได้เลย

freqtrade hyperopt --hyperopt-loss OnlyProfitHyperOptLoss --spaces roi stoploss --strategy AwesomeStrategy --dry-run-wallet 100 --fee 0.001 --config user_data/config.json --datadir user_data/data/binance/2022-11 -e 100

รอสักพัก จะได้ค่าดังรูปเลย

Freqtrade Hyperopt

จากนั้น รัน Backtesting กัน

freqtrade backtesting --strategy AwesomeStrategy --dry-run-wallet 100 --fee 0.001 --config user_data/config.json --datadir user_data/data/binance/2022-11

จะได้ค่า ดังรูป

Freqtrade Backtesting

Total profit % ที่ 31.08% แย่ลงอีก แสดงว่าค่าเดิมดีกว่า ก็ไปลบ AwesomeStrategy.json ออก ไม่งั้นเวลารัน Backtest มันจะเอาค่าในนี้มา

จากข้างบน เราจะเห็นว่า ค่าที่ดีที่สุดก็คือค่าที่คัดเหรียญไปใส่ตั้งแต่ต้นเลย จากเคสนี้แปลว่า การแก้ไข populate_exit_trend กับการทำ Hyperopt ไม่ได้ช่วยให้ดีขึ้นเลย

จริง ๆ การทดสอบ Backtesting แบบนี้เป็นแค่ตัวอย่างให้ดูเท่านั้น เพราะการคัดเหรียญ เราต้องเขียนโปรแกรมเพิ่มอีก และเวลาเราจะเช็คว่าเหรียญที่เราเลือกมานั้น ดีไม่ดี เราต้องเทสย้อนหลังหลาย ๆ เดือน และเอาผลที่ได้มาเฉลี่ยว่าในอีกกี่เดือนกี่วันข้างหน้า เหรียญและวิธีการแบบนี้จะใช้ได้หรือเปล่า เป็นการประเมินว่าที่เราทำมาจะทำกำไรในอนาคตได้หรือไม่

ขอเพิ่มเติมก่อนจบบทความ ก็คือ จริง ๆ การเขียนโค้ดของ Freqtrade นั้นทำได้มากกว่านี้ แต่ที่เอามาแค่นี้ก็เพราะว่าอยากให้รู้ว่าเบื้องต้นเค้าทำกันอย่างไร ขึ้นอยู่กับคนเขียนโค้ด คนคิดกลยุทธ์เลยว่าจะเอาแบบไหน แต่แนะนำว่าให้ทำ Backtesting ไปด้วย ที่ผู้เขียนเจอมาตอนเขียนโค้ดก็คือ ถ้าเรายิ่งเขียนเงื่อนไขเยอะ กำไรที่ได้ก็จะน้อยลง แต่ระยะยาวอันนี้ผู้เขียนไม่รู้นะ เช่น ถ้าเราใช้ Bollenger Band ในการเทรด ถ้าเดือนไหนเหรียญราคาขึ้น อัลกอริทึมนี้จะทำกำไรได้สูงมาก แต่ถ้าราคาลง ก็จะติดลบมากเช่นกัน แต่ถ้าเราเกิดไปใช้ Bollenger Band คู่กับ RSI หรือ MACD ที่ผู้เขียนเจอมาก็คือ กำไรจะน้อยลงอย่างเห็นได้ชัด และขาดทุนก็น้อยลงอย่างเห็นได้ชัด จึงสรุปได้ว่า ยิ่งเงื่อนไขมาก ๆ กำไร ขาดทุนก็จะน้อยลงเช่นกัน ตามเงื่อนไขที่เราเขียน ระยะยาวอันนี้ไม่รู้ จึงแนะนำว่า เขียนไปทำ Backtesting ไป จะได้ไม่มีปัญหา

สำหรับบทความนี้มีแต่ตัวหนังสือต้องขอโทษผู้อ่านด้วยนะครับ เพราะมันเป็นทฤษฎีจริง ๆ และอยากอธิบายให้เข้าใจ ถ้าไม่งั้นอาจรอดูคลิปพร้อมทำตามก็ได้ จะได้เข้าใจมากขึ้น เดี๋ยวจะทำคลิปตามมาเร็ว ๆ นี้ สำหรับวันนี้ขอจากกันไปก่อน สำหรับบทความหน้าจะเป็นอย่างไรนั้นก็ติดตามกันต่อไปนะครับ สำหรับวันนี้ สวัสดีครับ

ช่องทางการติดต่อ

Email: [email protected]

Website: https://blog.tichaky.com/

Facebook: https://www.facebook.com/tichaky

Youtube: https://www.youtube.com/@tichaky_diary

ทำ Line Message API ด้วย n8n

ใช้ Portainer จัดการ Container กัน

DigitalOcean มีอะไรให้ใช้บ้าง

มาดู Droplets ของ DigitalOcean กัน