{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "d230d4c5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Merging daily equity files:\n", "Currently used TEJ API key call quota 1803/9223372036854775807 (0.0%)\n", "Currently used TEJ API key data quota 49629380/9223372036854775807 (0.0%)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[2024-03-04 05:03:06.531022] INFO: zipline.data.bundles.core: Ingesting tquant.\n", "[2024-03-04 05:03:38.696492] INFO: zipline.data.bundles.core: Ingest tquant successfully.\n" ] } ], "source": [ "import os \n", "\n", "StockList1 = ['1227', '1234', '1304', '1314', '1434', '1440', '1476', '1504', '1507', '1590', '1605', '1704', '1710', '1717', '1723', '1789',\n", " '1802', '1907', '2006', '2015', '2049', '2101', '2103', '2106', '2204', '2227', '2327', '2337', '2344', '2356', '2360', '2362',\n", " '2379', '2384', '2385', '2392', '2395', '2448', '2449', '2450', '2451', '2489', '2501', '2504', '2511', '2542', '2545', '2548',\n", " '2603', '2606', '2607', '2608', '2609', '2610', '2615', '2618', '2707', '2723', '2727', '2809', '2812', '2823', '2834', '2845',\n", " '2847', '2855', '2884', '2887', '2888', '2889', '2903', '2915', '3034', '3037', '3044', '3149', '3189', '3406', '3702', '4938',\n", " '4958', '5522', '5871', '6005', '6176', '6239', '6269', '6286', '8008', '8046', '8078', '8422', '9904', '9907', '9914', '9917', '9921',\n", " '9933', '9940', '9945', '2458', '5264', '2206', '1201', '2347', '3231', '5534', '6116', '9910', '1477', '2353', '6271', '1319',\n", " '1722', '2059', '3060', '3474', '3673', '2393', '2376', '2439', '3682', '1262', '2201', '2377', '3576', '2352', '2838', '8150',\n", " '2324', '2231', '8454', '2833', '6285', '6409', '1536', '1702', '2313', '2498', '2867', '6415', '6456', '9938', '2383', '4137', '6452',\n", " '1707', '1589', '2849', '6414', '8464', '2355', '2345', '3706', '2023', '2371', '1909', '2633', '3532', '9941', '2492', '3019',\n", " '3443', '4915', '4943', '1229', '2441', '2027', '3026', '1210', '2104', '2456', '5269', '8341', '2354', '3005', '3481', '6669',\n", " '2409', '3023', '6213', '2404', '3533', '6278', '6592', '3653', '3661', '3665', '2301', '3714', '2883', '2890', '6531', '1904',\n", " '2014', '2105', '2108', '2474', '2637', '6781', '1102', '4919', '1402', '3035', '3036', '4961', '6719', '6770', '2368', '1795',\n", " '6550', '6789', '3017', '1101', '1216', '1301', '1303', '1326', '2002', '2207', '2303', '2308', '2311', '2317', '2325', '2330',\n", " '2357', '2382', '2412', '2454', '2801', '2880', '2881', '2882', '2885', '2886', '2891', '2892', '2912', '3008', '3045', '3697',\n", " '4904', '5880', '6505', '2408', '3711', '5876']\n", "\n", "tickers = \" \".join(StockList1)\n", "\n", "os.environ['TEJAPI_BASE'] = 'https://api.tej.com.tw'\n", "os.environ['TEJAPI_KEY'] = 'your_key'\n", "\n", "os.environ['mdate'] = '20170101 20230726'\n", "os.environ['ticker'] = tickers\n", "\n", "!zipline ingest -b tquant\n", "\n", "from zipline.pipeline import Pipeline\n", "from zipline.TQresearch.tej_pipeline import run_pipeline\n", "from zipline.pipeline.data import TWEquityPricing\n", "from zipline.pipeline.factors import SimpleMovingAverage" ] }, { "cell_type": "markdown", "id": "0297dc5e", "metadata": {}, "source": [ "## Filters\n", "Filters 是一個將資產在某個時間點的特徵轉換為**布林值**的函數 :\n", "\n", "```\n", "F(asset, timestamp) -> boolean\n", "```\n", "**在 Pipeline 中,Filters 用於縮小股票池的大小**,這裡有兩種 `Filter` 常見的創建方式:使用 `Factor`/ `Classifier` 方法或使用比較運算子(comparison operators,`<`, `<=`, `!=`, `eq`, `>`, `>=`)。\n", "\n", "### Comparison Operators\n", "對 `Factors` 和 `Classifier` 使用比較運算子就可以創建 Filters。\n", "\n", "有鑒於我們尚未介紹到 `Classifier`,我們僅使用 `Factors` 舉例,下方的範例建立了一個當最近一期收盤價大於 $20 時會返回 `True` 的 filter 。" ] }, { "cell_type": "code", "execution_count": 2, "id": "4b4b27b0", "metadata": {}, "outputs": [], "source": [ "last_close_price = TWEquityPricing.close.latest\n", "close_price_filter = last_close_price > 20" ] }, { "cell_type": "markdown", "id": "169f2d58", "metadata": {}, "source": [ "而以下這個範例建立一個新的 filter,只要近 10 天收盤價平均值低於近 30 天收盤價平均值,該過濾器就會返回 True。" ] }, { "cell_type": "code", "execution_count": 3, "id": "8256aafe", "metadata": {}, "outputs": [], "source": [ "mean_close_10 = SimpleMovingAverage(inputs=[TWEquityPricing.close], window_length=10)\n", "mean_close_30 = SimpleMovingAverage(inputs=[TWEquityPricing.close], window_length=30)\n", "mean_crossover_filter = mean_close_10 < mean_close_30" ] }, { "cell_type": "markdown", "id": "35ee0055", "metadata": {}, "source": [ "記住,在每天中每檔證券都會擁有自己的 `True` 或 `False` 值。" ] }, { "cell_type": "markdown", "id": "51622e08", "metadata": {}, "source": [ "### Factor/Classifier Methods\n", "很多種 `Factor` 和 `Classifier` 都會回傳 `Filters`。\n", "\n", "但同樣地,由於我們尚未討論到 `Classifier`,目前我們僅使用 `Factor`(稍後我們會討論 `Classifier`)。\n", "\n", "`Factor.top(n)`會建立一個 `Filter` ,當排名在指定 `Factor` 前 `n` 名的證券,會標記為 `True` ,其餘標記為 `False`。\n", "\n", "下面的範例建立了一個會每天將最近一期收盤價排名前30名的證券標示為`True` 的filter。" ] }, { "cell_type": "code", "execution_count": 4, "id": "503afe3a", "metadata": {}, "outputs": [], "source": [ "last_close_price = TWEquityPricing.close.latest\n", "top_close_price_filter = last_close_price.top(30)" ] }, { "cell_type": "markdown", "id": "6ec445dd", "metadata": {}, "source": [ "## Dollar Volume Filter\n", "作為第一個範例,我們創建一個 Filter 。如果該證券過去 30 日平均交易量大於 $1, 000,000, 000 則回傳 `True` 值。\n", "\n", "為了實現這個效果,我們首先需要創建一個 `AverageDollarVolume` factor 以計算過去 30 日平均交易量。讓我們匯入 __built-in factor__ `AverageDollarVolume`。" ] }, { "cell_type": "code", "execution_count": 5, "id": "c0942b13", "metadata": {}, "outputs": [], "source": [ "from zipline.pipeline.factors import AverageDollarVolume" ] }, { "cell_type": "markdown", "id": "6c1e168e", "metadata": {}, "source": [ "接著,讓我們實例化 `AverageDollarVolume` factor。" ] }, { "cell_type": "code", "execution_count": 6, "id": "23a2db1c", "metadata": {}, "outputs": [], "source": [ "dollar_volume = AverageDollarVolume(window_length=30)" ] }, { "cell_type": "markdown", "id": "0480e041", "metadata": {}, "source": [ "在預設情況下, `AverageDollarVolume` 會使用 `TWEquityPricing.close` 與 `TWEquityPricing.volume` 作為 `inputs`,所以我們不須去定義他們。" ] }, { "cell_type": "markdown", "id": "f84dba52", "metadata": {}, "source": [ "現在我們有了平均交易量 factor ,我們可以以布林值表達式創建 filter。下方程式創建了一個當證券的平均成交量 `dollar_volume` 大於 $1, 000,000, 000 時會回傳 `True` 的 filter。" ] }, { "cell_type": "code", "execution_count": 7, "id": "8bc44caf", "metadata": {}, "outputs": [], "source": [ "high_dollar_volume = (dollar_volume > 1000000000)" ] }, { "cell_type": "markdown", "id": "d4eac677", "metadata": {}, "source": [ "為了知道 fitler 的實際結果,我們將它作為一個欄位加入先前課程中創立的 pipeline 中。此外,如同在 [lecture/Factors](https://github.com/tejtw/TQuant-Lab/blob/main/lecture/Factors.ipynb) 中所介紹的,我們額外加入了`percent_difference`。" ] }, { "cell_type": "code", "execution_count": 8, "id": "918d80d1", "metadata": {}, "outputs": [], "source": [ "def make_pipeline():\n", "\n", " mean_close_10 = SimpleMovingAverage(inputs=[TWEquityPricing.close], window_length=10)\n", " mean_close_30 = SimpleMovingAverage(inputs=[TWEquityPricing.close], window_length=30)\n", "\n", " percent_difference = (mean_close_10 - mean_close_30) / mean_close_30\n", " \n", " dollar_volume = AverageDollarVolume(window_length=30)\n", " high_dollar_volume = (dollar_volume > 1000000000)\n", "\n", " return Pipeline(\n", " columns={\n", " 'percent_difference': percent_difference, \n", " 'high_dollar_volume': high_dollar_volume\n", " }\n", " )" ] }, { "cell_type": "markdown", "id": "57cdfbb8", "metadata": {}, "source": [ "如果我們執行 pipeline ,就會產出 `high_dollar_volume` 欄位,當 filter 條件成立時會回傳布林值 `True`。" ] }, { "cell_type": "code", "execution_count": 9, "id": "ad36bbd0", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
percent_differencehigh_dollar_volume
Equity(0 [1101])0.037521False
Equity(1 [1102])0.023348False
Equity(2 [1201])0.033230False
Equity(3 [1210])0.034966False
Equity(4 [1216])-0.002188False
.........
Equity(235 [9933])0.015381False
Equity(236 [9938])0.027505False
Equity(237 [9940])0.021334False
Equity(238 [9941])0.013461False
Equity(239 [9945])-0.001735False
\n", "

230 rows × 2 columns

\n", "
" ], "text/plain": [ " percent_difference high_dollar_volume\n", "Equity(0 [1101]) 0.037521 False\n", "Equity(1 [1102]) 0.023348 False\n", "Equity(2 [1201]) 0.033230 False\n", "Equity(3 [1210]) 0.034966 False\n", "Equity(4 [1216]) -0.002188 False\n", "... ... ...\n", "Equity(235 [9933]) 0.015381 False\n", "Equity(236 [9938]) 0.027505 False\n", "Equity(237 [9940]) 0.021334 False\n", "Equity(238 [9941]) 0.013461 False\n", "Equity(239 [9945]) -0.001735 False\n", "\n", "[230 rows x 2 columns]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "result = run_pipeline(make_pipeline(), '2023-03-02', '2023-06-01')\n", "result.loc['2023-03-03']" ] }, { "cell_type": "markdown", "id": "5cc296ef", "metadata": {}, "source": [ "## Applying a Screen\n", "在預設情況下,pipeline 每日會針對每個在 bundle 中的資產生成一筆資料。\n", "\n", "但很多時候,我們只關注那些**符合特定條件的證券**(例如,為了能快速成交,我們可能只會關注那些每日交易量足夠大的證券)。我們可以透過 `screen` 告訴 pipeline 去忽略那些 filter 回傳的布林值為 `False` 的證券。 \n", " \n", "為了讓 pipeline 過濾出那些**30日平均交易量**大於 $1, 000,000, 000 的證券,我們可以簡單的將 `high_dollar_volume` filter 作為 `screen` 的參數。\n", "\n", "現在我們的 pipeline 應該會長這樣:" ] }, { "cell_type": "code", "execution_count": 10, "id": "34e40c9d", "metadata": {}, "outputs": [], "source": [ "def make_pipeline():\n", "\n", " mean_close_10 = SimpleMovingAverage(inputs=[TWEquityPricing.close], window_length=10)\n", " mean_close_30 = SimpleMovingAverage(inputs=[TWEquityPricing.close], window_length=30)\n", "\n", " percent_difference = (mean_close_10 - mean_close_30) / mean_close_30\n", " \n", " dollar_volume = AverageDollarVolume(window_length=30)\n", " high_dollar_volume = (dollar_volume > 1000000000)\n", "\n", " return Pipeline(\n", " columns={\n", " 'percent_difference': percent_difference\n", " },\n", " screen=high_dollar_volume\n", " )" ] }, { "cell_type": "markdown", "id": "d977e566", "metadata": {}, "source": [ "當我們執行程式時,pipeline 的輸出僅僅只會包含那些在特定日子滿足 `high_dollar_volume` filter 的證券。" ] }, { "cell_type": "code", "execution_count": 11, "id": "77058008", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "通過 filter 的證券數: 35\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
percent_difference
Equity(25 [1605])0.060100
Equity(34 [1795])0.074224
Equity(53 [2201])0.049502
Equity(60 [2303])0.034036
Equity(61 [2308])0.007811
Equity(64 [2317])0.018607
Equity(67 [2327])0.030150
Equity(68 [2330])0.001321
Equity(85 [2379])0.070902
Equity(95 [2412])0.007754
Equity(102 [2454])0.014696
Equity(108 [2498])0.036708
Equity(115 [2603])0.001640
Equity(119 [2609])0.004344
Equity(120 [2610])0.043368
Equity(121 [2615])0.004935
Equity(122 [2618])-0.000347
Equity(127 [2727])0.241944
Equity(140 [2882])0.015239
Equity(155 [3008])0.008678
Equity(160 [3034])0.084553
Equity(161 [3035])0.056630
Equity(163 [3037])0.019296
Equity(171 [3443])0.200979
Equity(172 [3481])0.116559
Equity(177 [3661])0.039343
Equity(183 [3711])0.009755
Equity(188 [4919])0.046445
Equity(192 [4961])0.116186
Equity(197 [5871])-0.003119
Equity(211 [6415])0.022885
Equity(215 [6531])0.153928
Equity(218 [6669])0.061960
Equity(219 [6719])-0.004284
Equity(223 [8046])0.009388
\n", "
" ], "text/plain": [ " percent_difference\n", "Equity(25 [1605]) 0.060100\n", "Equity(34 [1795]) 0.074224\n", "Equity(53 [2201]) 0.049502\n", "Equity(60 [2303]) 0.034036\n", "Equity(61 [2308]) 0.007811\n", "Equity(64 [2317]) 0.018607\n", "Equity(67 [2327]) 0.030150\n", "Equity(68 [2330]) 0.001321\n", "Equity(85 [2379]) 0.070902\n", "Equity(95 [2412]) 0.007754\n", "Equity(102 [2454]) 0.014696\n", "Equity(108 [2498]) 0.036708\n", "Equity(115 [2603]) 0.001640\n", "Equity(119 [2609]) 0.004344\n", "Equity(120 [2610]) 0.043368\n", "Equity(121 [2615]) 0.004935\n", "Equity(122 [2618]) -0.000347\n", "Equity(127 [2727]) 0.241944\n", "Equity(140 [2882]) 0.015239\n", "Equity(155 [3008]) 0.008678\n", "Equity(160 [3034]) 0.084553\n", "Equity(161 [3035]) 0.056630\n", "Equity(163 [3037]) 0.019296\n", "Equity(171 [3443]) 0.200979\n", "Equity(172 [3481]) 0.116559\n", "Equity(177 [3661]) 0.039343\n", "Equity(183 [3711]) 0.009755\n", "Equity(188 [4919]) 0.046445\n", "Equity(192 [4961]) 0.116186\n", "Equity(197 [5871]) -0.003119\n", "Equity(211 [6415]) 0.022885\n", "Equity(215 [6531]) 0.153928\n", "Equity(218 [6669]) 0.061960\n", "Equity(219 [6719]) -0.004284\n", "Equity(223 [8046]) 0.009388" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "result = run_pipeline(make_pipeline(), '2023-03-02', '2023-03-03')\n", "print('通過 filter 的證券數: %d' % len(result))\n", "result.loc['2023-03-03']" ] }, { "cell_type": "markdown", "id": "3a23525e", "metadata": {}, "source": [ "## Inverting a Filter\n", "`~` 運算符是用於反轉 filter 的,將所有 `True` 轉變為 `False`,反之亦然。例如,我們可以撰寫下列的 filter 去過濾出低交易量的證券。" ] }, { "cell_type": "code", "execution_count": 12, "id": "10989dd3", "metadata": {}, "outputs": [], "source": [ "low_dollar_volume = ~high_dollar_volume" ] }, { "cell_type": "markdown", "id": "8c83e42f", "metadata": {}, "source": [ "這會對過去 30 天內所有平均交易量低於或等於 $1,000,000,000 的證券回傳 `True`。" ] }, { "cell_type": "markdown", "id": "585e0af4", "metadata": {}, "source": [ "其餘 __built-in filters__ 請參考:[Pipeline built-in filters.ipynb](https://github.com/tejtw/TQuant-Lab/blob/main/lecture/Pipeline%20built-in%20filters.ipynb)" ] } ], "metadata": { "kernelspec": { "display_name": "Python [conda env:zipline-tej_v11]", "language": "python", "name": "conda-env-zipline-tej_v11-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.5" } }, "nbformat": 4, "nbformat_minor": 5 }