{ "cells": [ { "cell_type": "markdown", "id": "2b076a6d", "metadata": {}, "source": [ "\n", "# 手續費模型(Commission Models)" ] }, { "cell_type": "markdown", "id": "34c071e5", "metadata": {}, "source": [ "- 交易成本(transaction costs)被廣泛認為是影響投資績效的重要因素。它們不僅影響投資績效,還影響了將資產轉換成現金的難易度。\n", "\n", "- 在真實世界的交易中存在許多種類的交易成本,其中一種是**直接成本(direct cost)**。直接成本包含了**手續費(commission)、證交稅**等等,這些成本是在交易前已經**提前知道的成本**。此類成本皆可以使用 Zipline 中的 **commission models** 進行模擬。這也是回測(backtesting)的一大目的,考量投資策略在真實世界運行的可能性。" ] }, { "cell_type": "markdown", "id": "e98b4f1a", "metadata": {}, "source": [ "## zipline.api.set_commission(self, equities=None, futures=None)\n", "\n", "設定回測時所使用的手續費模型。\n", "\n", "\n", "> ### Parameters:\n", "> - equities *(EquityCommissionModel, optional)* -用於交易股票的手續費模型。\n", "> - EquityCommissionModel:`zipline.finance.commission`\n", "> - futures *(FutureCommissionModel, optional)* -用於交易期貨的手續費模型。(目前不支援)\n", "> - FutureCommissionModel:`zipline.finance.commission`\n", "> \n", "> ### Raises:\n", "> **SetCommissionPostInit**-`set_commission` **只能**在 `initialize` 階段使用。\n", "> \n", "> ### Notes:\n", "> - `set_commission` 只能一次用**一種**方法。\n", "> - 手續費計算時,**價格以成交日收盤價為準,數量也以成交時為準**。也就是說,如果因為股數變動造成 amount 有任何變化,計算上都是用成交時新的 amount。\n", "> \n", "> ### See also:\n", "> - `zipline.finance.commission.PerShare`\n", "> - `zipline.finance.commission.PerTrade`\n", "> - `zipline.finance.commission.PerDollar`\n", "> - `zipline.finance.commission.Custom_TW_Commission`\n", "> \n", "> ### Examples:\n", "> ```python\n", "> from zipline.api import set_commission\n", "> from zipline.finance import commission\n", "> \n", "> def initialize(context):\n", "> set_commission(commission.<其中一種commission models>)\n", "> ```" ] }, { "cell_type": "markdown", "id": "e8c80c22", "metadata": {}, "source": [ "## class zipline.finance.commission.CommissionModel\n", "> 手續費模型的抽象基類(Abstract Base Class)。\n", "> \n", "> 手續費模型是用來計算每筆交易需繳交多少手續費。Zipline 目前有五種模型:\n", "> \n", "> 1. `PerDollar`:按照交易金額抽成計算。\n", "> 2. `PerTrade`:一筆交易收取一筆固定費用。\n", "> 3. `PerShare`:按照下單的股數計算費用,同時還可以設定一個最低費用。\n", "> 4. `Custom_TW_Commission`:台灣專用的手續費模型。\n", "> 5. `NoCommission`:不收任何手續費。\n", "\n", "## class zipline.finance.commission.PerDollar(cost=0.0015)\n", "> - 按照交易金額抽成計算。\n", "> - 僅適用於 equities。\n", "> \n", "> ### Parameters:\n", "> - cost *(float, optional)*-每交易一元的股票所需支付的固定費用。預設為 0.0015 元。\n", "\n", "## class zipline.finance.commission.PerTrade(cost=0.0)\n", "> - 一筆交易收取一筆固定費用。\n", "> - 適用於 equities 與 futures。\n", "> \n", "> ### Parameters:\n", "> - cost *(float, optional)*-每進行一筆交易所需支付的固定費用。預設為 0 元。\n", "\n", "## class zipline.finance.commission.PerShare(cost=0.001, min_trade_cost=0.0)\n", "> - 按照下單的股數計算費用,同時還可以設定一個最低費用。\n", "> - 僅適用於 equities。\n", "> - 為**預設模型**。\n", "> \n", "> ### Parameters:\n", "> - cost *(float, optional)*-每交易一股的股票所需支付的固定費用。預設為 0.001 元。\n", "> - min_trade_cost *(float, optional)*-最低費用,預設為無最低費用。\n", "\n", "## class zipline.finance.commission.Custom_TW_Commission(min_trade_cost=20, discount=1.0, tax = 0.003)\n", "> - 台灣股票適用的手續費模型,考量券商手續費(費率:0.001425)及證交稅,同時還可以設定一個最低費用。\n", "> - 台灣交易股票時的情況的情況:在台灣交易股票時,主要有兩個直接成本:**手續費**及**證交稅**。`Custom_TW_Commission` 可模擬這兩項成本。\n", "> - 手續費\n", "> - **買進**或**賣出**時皆須繳交。\n", "> - 計算方式為:成交價 × 成交股數 × 0.1425 % × 折扣(折扣預設是 1,沒有折扣)。\n", "> - 手續費有**最低價格**(預設是 20 元)。\n", "> \n", "> - 證交稅\n", "> - **賣出**時才要繳交。\n", "> - 計算方式為:成交價 × 成交股數 × 證交稅率(證交稅率預設是 0.3%)。\n", "> - 僅適用於 equities。\n", "> \n", "> ### Parameters:\n", "> - min_trade_cost *(float, optional)*-最低費用。預設為 20 元。\n", "> - discount *(float, optional)*-券商手續費折扣比率。預設為 1,代表沒有折扣。\n", "> - tax *(float, optional)*-證交稅率,預設為 0.003。\n", "> \n", "> ### Notes:\n", "> - 手續費計算時,**價格以成交日收盤價為準,數量也以成交時為準**,也就是說,如果因為股數變動造成 amount 有任何變化,計算上都是用成交時新的 amount。\n", "\n", "## class zipline.finance.commission.NoCommission()\n", "> - 不收任何手續費,此模型主要用來測試。\n", "> - 適用於 equities 與 futures。" ] }, { "cell_type": "markdown", "id": "530a1d1a", "metadata": {}, "source": [ "### Examples-CommissionModel\n", "以下範例比較各種模型計算方法。" ] }, { "cell_type": "markdown", "id": "2ae06196", "metadata": {}, "source": [ "#### Import settings" ] }, { "cell_type": "code", "execution_count": 1, "id": "ee5cae9e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Merging daily equity files:\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[2023-11-27 03:31:44.753277] INFO: zipline.data.bundles.core: Ingesting tquant.\n" ] } ], "source": [ "import pandas as pd\n", "import datetime\n", "import tejapi\n", "import os\n", "import warnings\n", "from logbook import Logger, StderrHandler, INFO\n", "warnings.filterwarnings('ignore')\n", "\n", "# set log\n", "log_handler = StderrHandler(format_string='[{record.time:%Y-%m-%d %H:%M:%S.%f}]: ' +\n", " '{record.level_name}: {record.func_name}: {record.message}',\n", " level=INFO)\n", "log_handler.push_application()\n", "log = Logger('CommissionModel')\n", "\n", "# tej_key\n", "os.environ['TEJAPI_KEY'] = \"your key\" \n", "os.environ['TEJAPI_BASE'] = \"https://api.tej.com.tw\"\n", "\n", "# date\n", "# set date\n", "start='2022-12-01'\n", "end='2022-12-31'\n", "os.environ['mdate'] = '20221201 20221231'\n", "\n", "tz = 'UTC'\n", "start_dt, end_dt = pd.Timestamp(start, tz = tz), pd.Timestamp(end, tz = tz)\n", "\n", "# calendar\n", "calendar_name='TEJ'\n", "\n", "# bundle_name\n", "bundle_name = 'tquant'\n", "\n", "# ticker\n", "os.environ['ticker'] = '1216 IR0001'\n", "\n", "# ingest\n", "!zipline ingest -b tquant" ] }, { "cell_type": "code", "execution_count": 2, "id": "b1e1a97c", "metadata": {}, "outputs": [], "source": [ "from zipline.finance import commission, slippage\n", "from zipline.api import *\n", "\n", "from zipline import run_algorithm\n", "from zipline.utils.calendar_utils import get_calendar\n", "\n", "from zipline.utils.run_algo import (get_transaction_detail,\n", " get_record_vars)" ] }, { "cell_type": "markdown", "id": "304f53e5", "metadata": {}, "source": [ "#### 比較各種模型計算方法\n", "- 注意四個 `initialize` 函數的差別。\n", " - `initialize_perdollar`:commission.PerTrade(cost=0.5)\n", " - `initialize_pertrade`:commission.PerTrade(cost=0.5)\n", " - `initialize_pershare`:commission.PerShare(cost=0.001, min_trade_cost=5.0)\n", " - `initialize_Custom`:commission.Custom_TW_Commission(min_trade_cost=20, discount = 1.0, tax = 0.003)\n", "- 這個範例將滑價模型設定為:`slippage.FixedSlippage(spread=0.00)`。其中,spread 設定為 0,這樣會比較好觀察結果,因為滑價會導致成交價格改變。若有滑價則會使用考慮滑價後的價格計算手續費。" ] }, { "cell_type": "code", "execution_count": 3, "id": "bd78b018", "metadata": {}, "outputs": [], "source": [ "def initialize_perdollar(context):\n", " context.i = 0\n", " context.tickers = ['1216']\n", " context.asset = [symbol(ticker) for ticker in context.tickers] \n", " set_slippage(slippage.FixedSlippage(spread=0.00))\n", " \n", " # set_commission\n", " set_commission(equities=commission.PerDollar(cost=0.001))\n", " \n", " set_benchmark(symbol('IR0001'))\n", " \n", "def initialize_pertrade(context):\n", " context.i = 0\n", " context.tickers = ['1216']\n", " context.asset = [symbol(ticker) for ticker in context.tickers] \n", " set_slippage(slippage.FixedSlippage(spread=0.00))\n", " \n", " # set_commission\n", " set_commission(equities=commission.PerTrade(cost=0.5))\n", " \n", " set_benchmark(symbol('IR0001'))\n", " \n", "def initialize_pershare(context):\n", " context.i = 0\n", " context.tickers = ['1216']\n", " context.asset = [symbol(ticker) for ticker in context.tickers] \n", " set_slippage(slippage.FixedSlippage(spread=0.00))\n", " \n", " # set_commission\n", " set_commission(equities=commission.PerShare(cost=0.001, min_trade_cost=5.0))\n", " \n", " set_benchmark(symbol('IR0001'))\n", " \n", "def initialize_Custom(context):\n", " context.i = 0\n", " context.tickers = ['1216']\n", " context.asset = [symbol(ticker) for ticker in context.tickers] \n", " set_slippage(slippage.FixedSlippage(spread=0.00))\n", " \n", " # set_commission\n", " set_commission(equities=commission.Custom_TW_Commission(min_trade_cost=20,\n", " discount = 1.0,\n", " tax = 0.003))\n", " \n", " set_benchmark(symbol('IR0001'))\n", " \n", "def handle_data(context, data):\n", "\n", " if context.i == 0:\n", " for asset in context.asset:\n", " order_target(asset, 1000)\n", " if context.i == 2:\n", " for asset in context.asset:\n", " order_target(asset, 0)\n", " \n", " record(close=data.current(context.asset, 'close'))\n", " context.i += 1\n", "\n", "capital_base = 1e5" ] }, { "cell_type": "code", "execution_count": 4, "id": "16f41ed9", "metadata": {}, "outputs": [], "source": [ "closing_price = tejapi.get('TWN/APIPRCD',\n", " coid=['1216'],\n", " opts={'columns':['mdate','coid','close_d']},\n", " mdate={'gte':start_dt,'lte':end_dt },\n", " paginate=True)\n", "\n", "perdollar = run_algorithm(start=start_dt,\n", " end=end_dt,\n", " initialize=initialize_perdollar,\n", " handle_data=handle_data,\n", " capital_base=capital_base,\n", " trading_calendar=get_calendar(calendar_name),\n", " bundle=bundle_name)\n", "\n", "pertrade = run_algorithm(start=start_dt,\n", " end=end_dt,\n", " initialize=initialize_pertrade,\n", " handle_data=handle_data,\n", " capital_base=capital_base,\n", " trading_calendar=get_calendar(calendar_name),\n", " bundle=bundle_name)\n", "\n", "pershare = run_algorithm(start=start_dt,\n", " end=end_dt,\n", " initialize=initialize_pershare,\n", " handle_data=handle_data,\n", " capital_base=capital_base,\n", " trading_calendar=get_calendar(calendar_name),\n", " bundle=bundle_name)\n", "\n", "Custom = run_algorithm(start=start_dt,\n", " end=end_dt,\n", " initialize=initialize_Custom,\n", " handle_data=handle_data,\n", " capital_base=capital_base,\n", " trading_calendar=get_calendar(calendar_name),\n", " bundle=bundle_name)" ] }, { "cell_type": "markdown", "id": "6fe6f9b2", "metadata": {}, "source": [ "#### Explanations\n", "12/1" ] }, { "cell_type": "code", "execution_count": 5, "id": "54d27163", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", " | mdate | \n", "coid | \n", "close_d | \n", "
---|---|---|---|
None | \n", "\n", " | \n", " | \n", " |
0 | \n", "2022-12-01 | \n", "1216 | \n", "65.9 | \n", "
1 | \n", "2022-12-02 | \n", "1216 | \n", "65.0 | \n", "