Skip to content

回測(引用外部 data)

線上連結

pip install FinMind

初始化,設定回測股票代碼、時間區間

import numpy as np
import pandas as pd
from FinMind import strategies
from FinMind.data import DataLoader
from FinMind.strategies.base import Strategy
from ta.momentum import StochasticOscillator

data_loader = DataLoader()
# data_loader.login(user_id, password) # 可選
obj = strategies.BackTest(
    stock_id="0056",
    start_date="2018-01-01",
    end_date="2019-01-01",
    trader_fund=500000.0,
    fee=0.001425,
    data_loader=data_loader,
)
obj.stock_price

將會用以下 data 計算回測

        date stock_id  Trading_Volume  Trading_money   open    max    min  close  spread  Trading_turnover  CashEarningsDistribution  StockEarningsDistribution
0    2018-01-02     2330        18055269     4188555408  231.5  232.5  231.0  232.5     3.0            9954.0                       0.0                        0.0
1    2018-01-03     2330        31706091     7504382512  236.0  238.0  235.5  237.0     4.5           13633.0                       0.0                        0.0
2    2018-01-04     2330        29179613     6963192636  240.0  240.0  236.5  239.5     2.5           10953.0                       0.0                        0.0
3    2018-01-05     2330        23721255     5681934695  240.0  240.0  238.0  240.0     0.5            8659.0                       0.0                        0.0
4    2018-01-08     2330        21846692     5281823362  242.0  242.5  240.5  242.0     2.0           10251.0                       0.0                        0.0
..          ...      ...             ...            ...    ...    ...    ...    ...     ...               ...                       ...                        ...
729  2020-12-25     2330        12581145     6449612552  514.0  515.0  510.0  511.0     1.0           14988.0                       0.0                        0.0
730  2020-12-28     2330        19262886     9890545245  512.0  515.0  509.0  515.0     4.0           16673.0                       0.0                        0.0
731  2020-12-29     2330        20151736    10370562545  515.0  517.0  513.0  515.0     0.0           17186.0                       0.0                        0.0
732  2020-12-30     2330        46705107    24306881615  516.0  525.0  514.0  525.0    10.0           33173.0                       0.0                        0.0
733  2020-12-31     2330        30326332    15989936054  526.0  530.0  524.0  530.0     5.0           25134.0                       0.0                        0.0

設計策略

class ShortSaleMarginPurchaseRatio(Strategy):
    """
    summary:
        策略概念: 券資比越高代表散戶看空,法人買超股票會上漲,這時候賣可以跟大部分散戶進行相反的操作,反之亦然
        策略規則: 券資比>=30% 且法人買超股票, 賣
                券資比<30% 且法人賣超股票 買
    """

    ShortSaleMarginPurchaseTodayRatioThreshold = 0.3

    def load_taiwan_stock_margin_purchase_short_sale(self):
        self.TaiwanStockMarginPurchaseShortSale = (
            self.data_loader.taiwan_stock_margin_purchase_short_sale(
                stock_id=self.stock_id,
                start_date=self.start_date,
                end_date=self.end_date,
            )
        )
        self.TaiwanStockMarginPurchaseShortSale[
            ["ShortSaleTodayBalance", "MarginPurchaseTodayBalance"]
        ] = self.TaiwanStockMarginPurchaseShortSale[
            ["ShortSaleTodayBalance", "MarginPurchaseTodayBalance"]
        ].astype(
            int
        )
        self.TaiwanStockMarginPurchaseShortSale[
            "ShortSaleMarginPurchaseTodayRatio"
        ] = (
            self.TaiwanStockMarginPurchaseShortSale["ShortSaleTodayBalance"]
            / self.TaiwanStockMarginPurchaseShortSale[
                "MarginPurchaseTodayBalance"
            ]
        )

    def load_institutional_investors_buy_sell(self):
        self.InstitutionalInvestorsBuySell = (
            self.data_loader.taiwan_stock_institutional_investors(
                stock_id=self.stock_id,
                start_date=self.start_date,
                end_date=self.end_date,
            )
        )
        self.InstitutionalInvestorsBuySell[["sell", "buy"]] = (
            self.InstitutionalInvestorsBuySell[["sell", "buy"]]
            .fillna(0)
            .astype(int)
        )
        self.InstitutionalInvestorsBuySell = (
            self.InstitutionalInvestorsBuySell.groupby(
                ["date", "stock_id"], as_index=False
            ).agg({"buy": np.sum, "sell": np.sum})
        )
        self.InstitutionalInvestorsBuySell["diff"] = (
            self.InstitutionalInvestorsBuySell["buy"]
            - self.InstitutionalInvestorsBuySell["sell"]
        )

    def create_trade_sign(self, stock_price: pd.DataFrame) -> pd.DataFrame:
        stock_price = stock_price.sort_values("date")
        self.load_taiwan_stock_margin_purchase_short_sale()
        self.load_institutional_investors_buy_sell()
        stock_price = pd.merge(
            stock_price,
            self.InstitutionalInvestorsBuySell[["stock_id", "date", "diff"]],
            on=["stock_id", "date"],
            how="left",
        ).fillna(0)
        stock_price = pd.merge(
            stock_price,
            self.TaiwanStockMarginPurchaseShortSale[
                ["stock_id", "date", "ShortSaleMarginPurchaseTodayRatio"]
            ],
            on=["stock_id", "date"],
            how="left",
        ).fillna(0)
        stock_price.index = range(len(stock_price))
        stock_price["signal"] = 0
        sell_mask = (
            stock_price["ShortSaleMarginPurchaseTodayRatio"]
            >= self.ShortSaleMarginPurchaseTodayRatioThreshold
        ) & (stock_price["diff"] > 0)
        stock_price.loc[sell_mask, "signal"] = -1
        buy_mask = (
            stock_price["ShortSaleMarginPurchaseTodayRatio"]
            < self.ShortSaleMarginPurchaseTodayRatioThreshold
        ) & (stock_price["diff"] < 0)
        stock_price.loc[buy_mask, "signal"] = 1
        return stock_price

回測模擬交易

obj.add_strategy(ShortSaleMarginPurchaseRatio)
obj.simulate()
obj.final_stats

output

MeanProfit          187013.454352
MaxLoss             -17592.160000
FinalProfit         716596.810000
MeanProfitPer           37.400000
FinalProfitPer         143.320000
MaxLossPer              -3.520000
AnnualReturnPer         34.500000
AnnualSharpRatio         1.430000
dtype: float64

交易明細

 obj.trade_detail

output

    stock_id        date  EverytimeProfit  RealizedProfit  UnrealizedProfit  board_lot  hold_cost  hold_volume  signal    tax       fee  trade_price  trader_fund
0       2330  2018-01-03             0.00            0.00              0.00       1000     0.0000            0       0  0.003  0.001425        236.0   500000.000
1       2330  2018-01-04             0.00            0.00              0.00       1000     0.0000            0       0  0.003  0.001425        240.0   500000.000
2       2330  2018-01-05             0.00            0.00              0.00       1000     0.0000            0       0  0.003  0.001425        240.0   500000.000
3       2330  2018-01-08             0.00            0.00              0.00       1000     0.0000            0      -1  0.003  0.001425        242.0   500000.000
4       2330  2018-01-09             0.00            0.00              0.00       1000     0.0000            0      -1  0.003  0.001425        242.0   500000.000
..       ...         ...              ...             ...               ...        ...        ...          ...     ...    ...       ...          ...          ...
728     2330  2020-12-25        692703.01       160992.91         531710.10       1000   245.8705         2000       0  0.003  0.001425        514.0    47251.925
729     2330  2020-12-28        688720.71       160992.91         527727.80       1000   245.8705         2000       0  0.003  0.001425        512.0    47251.925
730     2330  2020-12-29        694694.16       160992.91         533701.25       1000   245.8705         2000       0  0.003  0.001425        515.0    47251.925
731     2330  2020-12-30        696685.31       160992.91         535692.40       1000   245.8705         2000       0  0.003  0.001425        516.0    47251.925
732     2330  2020-12-31        716596.81       160992.91         555603.90       1000   245.8705         2000       0  0.003  0.001425        526.0    47251.925

視覺化

obj.plot()

backtesting