<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>回測價格 &#8211; FinLab</title>
	<atom:link href="https://www.finlab.tw/tag/%E5%9B%9E%E6%B8%AC%E5%83%B9%E6%A0%BC/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.finlab.tw</link>
	<description>深入淺出的量化投資，讓你在在茫茫股海中，找到專屬於自己的投資方法</description>
	<lastBuildDate>Thu, 17 Aug 2023 05:14:04 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.0.9</generator>

<image>
	<url>https://www.finlab.tw/wp-content/uploads/2020/07/favicon.png</url>
	<title>回測價格 &#8211; FinLab</title>
	<link>https://www.finlab.tw</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">179699571</site>	<item>
		<title>客製化選股策略的回測價格序列 &#124; 比較進出場的時間點特性</title>
		<link>https://www.finlab.tw/customed-tw-stock-backtest-price/</link>
					<comments>https://www.finlab.tw/customed-tw-stock-backtest-price/#respond</comments>
		
		<dc:creator><![CDATA[Ben]]></dc:creator>
		<pubDate>Thu, 17 Aug 2023 05:08:58 +0000</pubDate>
				<category><![CDATA[FinLab 量化平台]]></category>
		<category><![CDATA[生產力]]></category>
		<category><![CDATA[回測價格]]></category>
		<category><![CDATA[客製化]]></category>
		<category><![CDATA[歷史回測]]></category>
		<guid isPermaLink="false">https://www.finlab.tw/?p=5481</guid>

					<description><![CDATA[FinLab Package 目前提供收盤價與開盤價序列的選擇，用來模擬回測的進出場，然而在實務上進行時，你會 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="has-medium-font-size">FinLab Package 目前提供收盤價與開盤價序列的選擇，用來模擬回測的進出場，然而在實務上進行時，你會發現若要貼合回測，只有這兩種選擇的話，不一定能滿足操作上的彈性，造成回測與實彈有落差。這篇教學，會教你分析每一種序列選擇的優劣勢，並客製化地打造更多的回測價格序列選擇</p>



<h2>進出場價格序列的比較</h2>



<p class="has-medium-font-size">不同價格在進出上有不同的意義，整理給大家比較：</p>



<ul class="has-medium-font-size"><li>開盤價：此序列的特性是能在條件發生後，儘早入手，優點在超級利多發酵時，有比較高的機會用市價入手，若使用收盤價遇到漲停，就只能乖乖排隊。<br>缺點是價格的波動大，除了早盤當沖單的進場干擾，在某些特殊情況，例如開盤買在接近漲停，收盤跌停，隔日開盤再跌停，這樣短短一天就有30%的價差。在FinLab日週期的架構下，實際進場回延遲訊號日一天，波動大的時候可能造成巨大滑價風險。</li><li>收盤價：最常使用的回測價格序列，一般法人基金如 MSCI 也喜歡在尾盤大舉進場，使用收盤價是有些優點，像是日與日的波動差距最多只有10%，不像開盤價在特殊波動時會被擊殺，收盤價買進也比較能貼合技術指標K線分析。缺點是遇到強勢開盤鎖死的股票會買不到，碰到量少的冷門股票或本身資金較大，有流動性風險，為了買到一定數量，可能使成本拉高，並在賣出時，遇到賣到芭樂價的窘境。</li><li>收盤價與開盤價的均價：折衷收盤價與開盤價的優缺點，各進出一半，務實選擇。</li><li>最高價與最低價的均價：有些人想要慢慢在盤中佈局，不想集中在開盤與收盤，可以用此價格來模擬可能的最大範圍內的買賣均價。缺點是沒考慮到每個價位有不同成交量，若今日最高價只成交1張，實務上你賣到高價的機率較低。</li><li>成交均價：公式為「成交金額」/「成交股數」，解決「最高價與最低價的均價」未考慮到分價量表的問題，比較能貼合盤中進出場回測的模擬。</li></ul>



<h2>客製化 MarketInfo</h2>



<p class="has-medium-font-size">從<a href="https://doc.finlab.tw/reference/backtest/#finlab.backtest.sim" target="_blank" rel="noopener">回測API文件</a>可以知道，有兩個重要的參數我們需要調整：</p>



<figure class="wp-block-table has-medium-font-size"><table><tbody><tr><td><code>trade_at_price</code></td><td>選擇回測之還原股價以收盤價或開盤價計算，預設為&#8217;close&#8217;。可選&#8217;close&#8217;或&#8217;open&#8217;。<strong>TYPE:</strong>&nbsp;<code>str or pd.DataFrame</code><strong>DEFAULT:</strong>&nbsp;<code>'close'</code></td></tr><tr><td><code>market</code></td><td>可選擇<code>'TW_STOCK', 'CRYPTO'</code>，分別為台股或加密貨幣， 或繼承 finlab.market_info.MarketInfo 開發回測市場類別。<strong>TYPE:</strong>&nbsp;<code>str or MarketInfo</code><strong>DEFAULT:</strong>&nbsp;<code>'AUTO'</code></td></tr></tbody></table></figure>



<p class="has-medium-font-size">當 market 為預設的 <code>'TW_STOCK'</code> 時，程式會操作 <code> TWMarketInfo</code> 物件，此物件只有兩種回測序列選擇，分別是收盤價與開盤價，因此若我們想要多一點的選擇，像是「收盤價與開盤價的均價」、「最高價與最低價的均價」、「成交均價」，就必須繼承 <code> TWMarketInfo</code> 物件，去覆寫 <code>get_price() </code>方法，寫法如下，記得要針對不同序列未還原與還原的價格去寫，系統回測會抓<code> adj=True</code> 的價格來回測，策略面板顯示的進出場價格則使用未還原價格。</p>



<pre class="wp-block-code"><code lang="python" class="language-python">from finlab import data
from finlab.backtest import sim
from finlab.dataframe import FinlabDataFrame
import numpy as np
from finlab.market_info import TWMarketInfo
import pandas as pd


class CustomedTWMarketInfo(TWMarketInfo):

    @staticmethod
    def get_price(trade_at_price, adj=True):
        if isinstance(trade_at_price, pd.Series):
            return trade_at_price.to_frame()

        if isinstance(trade_at_price, pd.DataFrame):
            return trade_at_price

        if isinstance(trade_at_price, str):
            if trade_at_price == 'volume':
                return data.get('price:成交股數')
            
            # 開盤價 or 收盤價 or 最高價 or 最低價
            if trade_at_price in ['open', 'close', 'high', 'low']:
                if adj:
                    table_name = 'etl:adj_'
                    price_name = trade_at_price
                else:
                    table_name = 'price:'
                    price_name = {'open': '開盤價', 'close': '收盤價', 'high': '最高價', 'low': '最低價'}[trade_at_price]

                price = data.get(f'{table_name}{price_name}')
                return price
            
            # 收盤價與開盤價的均價
            if trade_at_price=='close_open_avg':
                if adj:
                    adj_open = data.get('etl:adj_open')
                    adj_close = data.get('etl:adj_close')
                    adj_avg_price = round((adj_open + adj_close)/2,2)
                    return adj_avg_price 
                else:
                    open_ = data.get('price:開盤價')
                    close = data.get('price:收盤價')
                    avg_price = round((open_ + close)/2,2)
                    return avg_price 
            
            # 最高價與最低價的均價
            if trade_at_price=='high_low_avg':
                if adj:
                    adj_high = data.get('etl:adj_high')
                    adj_low = data.get('etl:adj_low')
                    adj_avg_price = round((adj_high + adj_low)/2,2)
                    return adj_avg_price 
                else:
                    high = data.get('price:最高價')
                    low = data.get('price:最低價')
                    avg_price = round((high + low)/2,2)
                    return avg_price 
            
            # 成交均價
            if trade_at_price=='transaction_avg':
                vol = data.get('price:成交股數')
                vol_price = data.get('price:成交金額')
                avg_price = round(vol_price/vol,2)
                if adj:
                    close = data.get('price:收盤價')
                    adj_close = data.get('etl:adj_close')
                    adj_avg_price = adj_close/close*avg_price
                    return adj_avg_price
                else:
                    return avg_price


        raise Exception(f'**ERROR: trade_at_price is not allowed (accepted types: pd.DataFrame, pd.Series, str).')</code></pre>



<h2>客製化回測價格套入策略</h2>



<p class="has-medium-font-size">接著我們就可將寫好的 <code>CustomedTWMarketInfo </code>放入本益成長比的策略做測試，並使用「成交均價」來做價格回測。</p>



<h3>範例代碼</h3>



<pre class="wp-block-code"><code lang="python" class="language-python"># 本益成長比策略
def peg_strategy(trade_at_price='close'):
    pe = data.get('price_earning_ratio:本益比')
    rev = data.get('monthly_revenue:當月營收')
    rev_ma3 = rev.average(3)
    rev_ma12 = rev.average(12)
    營業利益成長率 = data.get('fundamental_features:營業利益成長率')
    peg = (pe/營業利益成長率)
    cond1 = rev_ma3/rev_ma12&gt;1.1
    cond2 = rev/rev.shift()&gt;0.9
    cond_all = cond1 &amp; cond2
    result = peg*cond_all
    position = result[result&gt;0].is_smallest(10).reindex(rev.index_str_to_date().index, method='ffill')
    report = sim(position, fee_ratio=1.425/1000/3, stop_loss=0.1, market=CustomedTWMarketInfo(), trade_at_price=trade_at_price, name=trade_at_price,upload=False)
    return report 


r = peg_strategy(trade_at_price='transaction_avg')
r.display()</code></pre>



<h3>回測結果</h3>



<figure class="wp-block-image size-large"><img width="1024" height="777" src="https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.32.22-1024x777.png" alt="截圖 2023 08 17 下午12.32.22" class="wp-image-5482" srcset="https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.32.22-1024x777.png 1024w, https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.32.22-300x228.png 300w, https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.32.22-768x583.png 768w, https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.32.22-1536x1166.png 1536w, https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.32.22.png 1914w" sizes="(max-width: 1024px) 100vw, 1024px" title="客製化選股策略的回測價格序列 | 比較進出場的時間點特性 1"></figure>



<h2>多重比較各種回測價格序列</h2>



<p class="has-medium-font-size">哪一種回測序列最好？可以用 FinLab 優化小工具快速檢測。</p>



<h3>範例代碼</h3>



<pre class="wp-block-code"><code lang="python" class="language-python">from finlab import data
from finlab.backtest import sim
from finlab.optimize.combinations import ReportCollection

reports = {n:peg_strategy(n) for n in ['close', 'open', 'close_open_avg', 'high_low_avg', 'transaction_avg']}
report_collection = ReportCollection(reports)
report_collection.plot_creturns().show()
report_collection.plot_stats('bar').show()
report_collection.plot_stats('heatmap')</code></pre>



<h3>回測結果</h3>



<figure class="wp-block-image size-large"><img loading="lazy" width="1024" height="195" src="https://www.finlab.tw/wp-content/uploads/2023/08/newplot-1-1024x195.png" alt="newplot 1" class="wp-image-5483" srcset="https://www.finlab.tw/wp-content/uploads/2023/08/newplot-1-1024x195.png 1024w, https://www.finlab.tw/wp-content/uploads/2023/08/newplot-1-300x57.png 300w, https://www.finlab.tw/wp-content/uploads/2023/08/newplot-1-768x146.png 768w, https://www.finlab.tw/wp-content/uploads/2023/08/newplot-1-1536x292.png 1536w, https://www.finlab.tw/wp-content/uploads/2023/08/newplot-1-2048x389.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" title="客製化選股策略的回測價格序列 | 比較進出場的時間點特性 2"></figure>



<figure class="wp-block-image size-large"><img loading="lazy" width="1024" height="195" src="https://www.finlab.tw/wp-content/uploads/2023/08/newplot-2-1024x195.png" alt="newplot 2" class="wp-image-5484" srcset="https://www.finlab.tw/wp-content/uploads/2023/08/newplot-2-1024x195.png 1024w, https://www.finlab.tw/wp-content/uploads/2023/08/newplot-2-300x57.png 300w, https://www.finlab.tw/wp-content/uploads/2023/08/newplot-2-768x146.png 768w, https://www.finlab.tw/wp-content/uploads/2023/08/newplot-2-1536x292.png 1536w, https://www.finlab.tw/wp-content/uploads/2023/08/newplot-2-2048x389.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" title="客製化選股策略的回測價格序列 | 比較進出場的時間點特性 3"></figure>



<figure class="wp-block-image size-large"><img loading="lazy" width="1024" height="524" src="https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.34.39-1024x524.png" alt="截圖 2023 08 17 下午12.34.39" class="wp-image-5485" srcset="https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.34.39-1024x524.png 1024w, https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.34.39-300x153.png 300w, https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.34.39-768x393.png 768w, https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.34.39-1536x785.png 1536w, https://www.finlab.tw/wp-content/uploads/2023/08/截圖-2023-08-17-下午12.34.39-2048x1047.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" title="客製化選股策略的回測價格序列 | 比較進出場的時間點特性 4"></figure>



<p class="has-medium-font-size">可以發現使用「開盤價」序列擁有最高的報酬率，但整體來說，五種選擇都差異不會很大，但論夏普率與整體指標評價，則是「收盤價和開盤價的均價」表現最好，也就是「開盤價」與「收盤價」各進出一半，將夏普率從 1.14 提升到1.47，而 avg_mdd(每筆交易對的平均最大回檔) 也從 -10%降低到 -8.9%，能有效降低波動風險。</p>



<h2>結論</h2>



<p class="has-medium-font-size">還有更多回測價格序列想要打造嗎？可以反映給我們開發，或是按照附件的<a href="https://colab.research.google.com/drive/1FCx4ZAjF6Hh2pFy42ka6qHYu_B3ZmFpJ?usp=sharing" target="_blank" rel="noopener"> colab 檔</a> 來自行客製化，讓回測的選擇更有彈性。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.finlab.tw/customed-tw-stock-backtest-price/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5481</post-id>	</item>
	</channel>
</rss>
