Technology

ลำดับชั้นของโมเดลค่าธรรมเนียม QSTrader

2025-05-29 07:13:14


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


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


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


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



ต้นทุนการทำธุรกรรม 

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


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


นอกจากค่าธรรมเนียมของโบรกเกอร์เองแล้ว ยังมีภาษีเฉพาะภูมิภาคที่ต้องชำระด้วย ตัวอย่างที่สำคัญคือภาษีแสตมป์ของสหราชอาณาจักรสำหรับการทำธุรกรรมหุ้น


ในรูปแบบปัจจุบัน QSTrader รองรับโมเดลค่าธรรมเนียมที่เรียบง่ายสองแบบ แรกคือ ZeroFeeModel ซึ่งไม่เรียกเก็บค่าคอมมิชชั่นหรือภาษีใดๆ กับการทำธุรกรรมใดๆ สิ่งนี้มีประโยชน์สำหรับการสร้างการจำลองพื้นฐานที่สามารถเปรียบเทียบกับโมเดลค่าธรรมเนียมที่สมจริงกว่าอื่นๆ ได้ อันที่สองคือ PercentFeeModel ซึ่งใช้ค่าคอมมิชชั่นเป็นเปอร์เซ็นต์และ/หรือภาษีเป็นเปอร์เซ็นต์กับจำนวนการซื้อขายที่ให้มา (เรียกว่า 'การพิจารณา') เราจะอธิบายทั้งหมดนี้ทีละอย่าง



FeeModel 

FeeModel เป็นคลาสฐานนามธรรมที่คลาสย่อยทั้งหมดสืบทอดแบบจำลองค่าธรรมเนียมจากมัน มันให้การกำหนดวิธีการเชิงนามธรรมสามอย่าง: _calc_commission, _calc_tax และ calc_total_cost เครื่องหมายขีดล่างที่นำหน้าสองวิธีแรกบ่งชี้ว่าวิธีเหล่านี้เป็นวิธีแบบกึ่งส่วนตัวที่มีฟังก์ชันการทำงานที่จำเป็นสำหรับคลาสเอง วิธีสุดท้ายคือวิธีการเชื่อมต่อสาธารณะเทียมและใช้ในการคำนวณต้นทุนการทำธุรกรรมของนายหน้าทั้งหมดเมื่อใช้กับการพิจารณาที่ซื้อขาย


โปรดทราบว่าภาษา Python ไม่มีแนวคิดของเมธอดส่วนตัวหรือสาธารณะ แตกต่างจาก C++ หรือ Java แทนที่จะใช้การขีดล่างเดียวที่นำหน้าวิธีการเพื่อบอกให้ลูกค้ารายอื่นของโมดูลทราบว่าวิธีการใดควรเรียกใช้เป็นอินเตอร์เฟซ (ไม่มีขีดล่าง) ในขณะที่วิธีการใดเป็นวิธีการเฉพาะของการใช้งาน (นำหน้าด้วยขีดล่าง)


วิธีการ _calc_commission และ _calc_tax ถูกแยกออกเพื่อให้สามารถคำนวณตรรกะสำหรับแต่ละประเภทของค่าใช้จ่ายได้แยกกัน กรณีนี้จะเป็นเช่นนี้ เช่น เมื่อมีการใช้ค่าคอมมิชชั่นแบบ 'sliding scale' แต่ภาษีเองอาจเป็นเปอร์เซ็นต์คงที่ที่เฉพาะเจาะจงโดยไม่คำนึงถึงจำนวนการพิจารณาที่ซื้อขาย


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


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


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


โค้ดสำหรับ FeeModel มีดังนี้ มันเป็นการนำเสนอคลาสฐานนามธรรมที่ค่อนข้างตรงไปตรงมา:


from abc import ABCMeta, abstractmethod


class FeeModel(object):
    """
    Abstract class to handle the calculation of brokerage
    commission, fees and taxes.
    """

    __metaclass__ = ABCMeta

    @abstractmethod
    def _calc_commission(self, asset, quantity, consideration, broker=None):
        raise NotImplementedError(
            "Should implement _calc_commission()"
        )

    @abstractmethod
    def _calc_tax(self, asset, quantity, consideration, broker=None):
        raise NotImplementedError(
            "Should implement _calc_tax()"
        )

    @abstractmethod
    def calc_total_cost(self, asset, quantity, consideration, broker=None):
        raise NotImplementedError(
            "Should implement calc_total_cost()"
        )



โมเดลค่าธรรมเนียมศูนย์ 

คลาสแรกที่ใช้ประยุกต์อินเตอร์เฟซนามธรรมนี้คือคลาสย่อยที่สืบทอดจาก ZeroFeeModel มันเป็นคลาสที่เรียบง่ายมากที่คืนค่า 0.0 สำหรับทั้ง _calc_tax และ _calc_commission ค่าทั้งสองนี้จะถูกนำมาบวกใน calc_total_cost เพื่อให้ได้ 0.0 สำหรับต้นทุนการซื้อขายรวม


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


รหัสสำหรับ ZeroFeeModel มีดังนี้ ส่วนใหญ่ของบรรทัดโค้ดสำหรับโมเดลนี้เป็น docstrings การนำไปใช้จริงมีน้อยมาก:


from qstrader.broker.fee_model.fee_model import FeeModel


class ZeroFeeModel(FeeModel):
    """
    A FeeModel subclass that produces no commission, fees
    or taxes. This is the default fee model for simulated
    brokerages within QSTrader.
    """

    def _calc_commission(self, asset, quantity, consideration, broker=None):
        """
        Returns zero commission.

        Parameters
        ----------
        asset : `str`
            The asset symbol string.
        quantity : `int`
            The quantity of assets (needed for InteractiveBrokers
            style calculations).
        consideration : `float`
            Price times quantity of the order.
        broker : `Broker`, optional
            An optional Broker reference.

        Returns
        -------
        `float`
            The zero-cost commission.
        """
        return 0.0

    def _calc_tax(self, asset, quantity, consideration, broker=None):
        """
        Returns zero tax.

        Parameters
        ----------
        asset : `str`
            The asset symbol string.
        quantity : `int`
            The quantity of assets (needed for InteractiveBrokers
            style calculations).
        consideration : `float`
            Price times quantity of the order.
        broker : `Broker`, optional
            An optional Broker reference.

        Returns
        -------
        `float`
            The zero-cost tax.
        """
        return 0.0

    def calc_total_cost(self, asset, quantity, consideration, broker=None):
        """
        Calculate the total of any commission and/or tax
        for the trade of size 'consideration'.

        Parameters
        ----------
        asset : `str`
            The asset symbol string.
        quantity : `int`
            The quantity of assets (needed for InteractiveBrokers
            style calculations).
        consideration : `float`
            Price times quantity of the order.
        broker : `Broker`, optional
            An optional Broker reference.

        Returns
        -------
        `float`
            The zero-cost total commission and tax.
        """
        commission = self._calc_commission(asset, quantity, consideration, broker)
        tax = self._calc_tax(asset, quantity, consideration, broker)
        return commission + tax



PercentFeeModel 

แบบจำลองที่สมจริงยิ่งขึ้นของต้นทุนการทำธุรกรรมถูกจัดเตรียมโดยคลาส PercentFeeModel คลาสนี้มีวิธีการเริ่มต้น __init__ ที่มีพารามิเตอร์สองตัว อย่างแรกคือ commission_pct ซึ่งเป็นค่าคอมมิชชั่นนายหน้าร้อยละคงที่ที่จะนำไปใช้กับการพิจารณา อย่างที่สองคือ tax_pct ซึ่งเป็นอัตราภาษีคงที่ที่ใช้กับการพิจารณา ค่าทั้งสองนี้ถูกกำหนดในช่วง [0.0, 1.0] นั่นคือ 1.0 เท่ากับ 100%


ทั้งสองวิธีใช้เปอร์เซ็นต์นี้กับมูลค่าที่แท้จริงของการพิจารณา สิ่งนี้หลีกเลี่ยงค่าคอมมิชชั่นติดลบเมื่อขายสินทรัพย์! ตัวอย่างเช่น การคำนวณค่าคอมมิชชันของนายหน้าดูเหมือนว่า: return self.commission_pct * abs(consideration) การดำเนินการสำหรับวิธีการเก็บภาษีเกือบจะเหมือนกัน


รหัสสำหรับ PercentFeeModel มีดังนี้ เช่นเดียวกับ ZeroFeeModel ส่วนใหญ่ของโค้ดสำหรับโมเดลนี้เป็น docstrings การนำไปใช้จริงมีน้อยมาก:


from qstrader.broker.fee_model.fee_model import FeeModel


class PercentFeeModel(FeeModel):
    """
    A FeeModel subclass that produces a percentage cost
    for tax and commission.

    Parameters
    ----------
    commission_pct : `float`, optional
        The percentage commission applied to the consideration.
        0-100% is in the range [0.0, 1.0]. Hence, e.g. 0.1% is 0.001
    tax_pct : `float`, optional
        The percentage tax applied to the consideration.
        0-100% is in the range [0.0, 1.0]. Hence, e.g. 0.1% is 0.001
    """

    def __init__(self, commission_pct=0.0, tax_pct=0.0):
        super().__init__()
        self.commission_pct = commission_pct
        self.tax_pct = tax_pct

    def _calc_commission(self, asset, quantity, consideration, broker=None):
        """
        Returns the percentage commission from the consideration.

        Parameters
        ----------
        asset : `str`
            The asset symbol string.
        quantity : `int`
            The quantity of assets (needed for InteractiveBrokers
            style calculations).
        consideration : `float`
            Price times quantity of the order.
        broker : `Broker`, optional
            An optional Broker reference.

        Returns
        -------
        `float`
            The percentage commission.
        """
        return self.commission_pct * abs(consideration)

    def _calc_tax(self, asset, quantity, consideration, broker=None):
        """
        Returns the percentage tax from the consideration.

        Parameters
        ----------
        asset : `str`
            The asset symbol string.
        quantity : `int`
            The quantity of assets (needed for InteractiveBrokers
            style calculations).
        consideration : `float`
            Price times quantity of the order.
        broker : `Broker`, optional
            An optional Broker reference.

        Returns
        -------
        `float`
            The percentage tax.
        """
        return self.tax_pct * abs(consideration)

    def calc_total_cost(self, asset, quantity, consideration, broker=None):
        """
        Calculate the total of any commission and/or tax
        for the trade of size 'consideration'.

        Parameters
        ----------
        asset : `str`
            The asset symbol string.
        quantity : `int`
            The quantity of assets (needed for InteractiveBrokers
            style calculations).
        consideration : `float`
            Price times quantity of the order.
        broker : `Broker`, optional
            An optional Broker reference.

        Returns
        -------
        `float`
            The total commission and tax.
        """
        commission = self._calc_commission(asset, quantity, consideration, broker)
        tax = self._calc_tax(asset, quantity, consideration, broker)
        return commission + tax


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


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




อ้างอิง :QSTrader Fee Model Class Hierarchy

จาก https://www.quantstart.com/articles/qstrader-fee-model-class-hierarchy/

ร่วมเเสดงความคิดเห็น :