Stock factor extension 1 (limit factor calculation) - from zero to firm 4

[above]( https://coderx.com.cn/wordpress/?p=172 "Above") introduces the stock daily data download. Starting from this article, the calculation methods of some stock factors will be recorded, which will be used in the preparation of subsequent strategies.

We will implement the strategy of double gods crossing multiple lines. The stock selection condition of the strategy is that the stock forms double gods on the same day (two trading limits at an interval), the K line crosses the 5, 10, 20 and 30 day moving average at the same time, and the 30 day line is above the 60 day line. The closing price of the daily trading limit is defined as buy_point. The purchase price is the opening price and buy on subsequent dates_ The smaller value of point, the selling profit stop price is buy_point*(1+6.18%), stop loss price is buy_point*(1-16.18%).

Before writing this series of articles, the strategy has not been adjusted for parameters, winning rate statistics and back-test analysis. From the perspective of several stocks currently observed, they all have a good increase. Readers can participate in the realization and optimization together to create a firm offer strategy.

After the three-year cycle back test from 2018 to 2020, the annualized return of the strategy is 41.7% and the maximum pullback is 25.8%.

This paper first calculates the limit factor.

Main code analysis

Create a new source file named data_center_v3.py, see the end of the text for all contents. v3 mainly involves three changes:

New calculation limit factor function

def zt(df):

This function is used to calculate the limit factor, where:

  • Parameter df is the DataFrame of the expansion factor to be calculated
  • The return value is the DataFrame containing the extension factor

Here, the daily closing price is 9.8% higher than the previous day's closing price as the trading limit judgment standard. If the limit rises, the factor is True, otherwise it is False.

    df['zt'] = np.where((df['close'].values >= 1.098 * df['preclose'].values), True, False)

The daily limit factor is calculated. Considering that the resumption of rights and the daily limit of gem and sci-tech innovation board are increased by 20%, the daily closing price is increased by 9.8% or more compared with the previous day's closing price as the daily limit.

    return df

Returns the DataFrame containing the extension factor

Add calculation expansion factor

def extend_factor(df):

This function is used to calculate the expansion factor, where:

  • Parameter df is the DataFrame of the expansion factor to be calculated
  • The return value is the DataFrame containing the extension factor

In v3, only the daily limit factor is calculated, and the calculation of double God and other factors will be added later.

    df = df.pipe(zt)

Use pipe to call the function zt to calculate the limit factor

    return df

Returns the DataFrame containing the extension factor.

Modify create data function

def create_data(stock_codes, from_date='1990-12-19', to_date=datetime.date.today().strftime('%Y-%m-%d'), adjustflag='2'):
    """
    Download the daily data of the specified stock within the specified date and calculate the expansion factor

    :param stock_codes: Stock code of data to be downloaded
    :param from_date: Daily line start date
    :param to_date: Daily line end date
    :param adjustflag: Option 1: Post reinstatement option 2: pre reinstatement option 3: no reinstatement option, default to pre reinstatement option
    :return: None
    """

    # Download stock cycle
    for code in stock_codes:
        print('Downloading{}...'.format(code))

        # Log in to BaoStock
        bs.login()

        # Download daily data
        out_df = bs.query_history_k_data_plus(code, g_baostock_data_fields, start_date=from_date, end_date=to_date,
                                              frequency='d', adjustflag=adjustflag).get_data()

        # Logout login
        bs.logout()

        # Eliminate stop disk data
        if out_df.shape[0]:
            out_df = out_df[(out_df['volume'] != '0') & (out_df['volume'] != '')]

The above contents are the same as v2, which can be used for reference v2 analysis content.

        if not out_df.shape[0]:
            continue

If the data is empty, subsequent filtering and expansion factor calculation will not be performed.

        out_df.drop_duplicates(['date'], inplace=True)

In case of duplicate data, add a de re filter.

        if out_df.shape[0] < g_available_days_limit:
            continue

Here, it is set to process only those with more than or equal to g_available_days_limit the stock with the root daily data, if the available daily data is less than g_available_days_limit root, no data will be created to ensure that the expansion factor can be calculated effectively in the future.
At the beginning of the file, we set the global variable:

g_available_days_limit = 250
        convert_list = ['open', 'high', 'low', 'close', 'preclose', 'volume', 'amount', 'turn', 'pctChg']
        out_df[convert_list] = out_df[convert_list].astype(float)

Convert the related field types into float types. These fields downloaded by BaoStock are str types by default. They need to be converted into float types before they can be used for subsequent indicator calculations.

        out_df.reset_index(drop=True, inplace=True)

To reset the index, we need to reset the index to maintain the continuity of the index because we have carried out operations such as de duplication and elimination of stop disk data.

        out_df = extend_factor(out_df)

Call function extend_factor calculates the expansion factor.

        print(out_df)

Print data creation results. Let's take a look at the print results of Sanmei sh.603379:

           date       open       high  ...   pcfNcfTTM  isST     zt
0    2019-04-02  32.652593  32.652593  ...   76.815822     0   True
1    2019-04-03  35.917853  35.917853  ...   84.497404     0   True
2    2019-04-04  39.511735  39.511735  ...   92.952079     0   True
3    2019-04-08  43.462210  43.462210  ...  102.245642     0   True
4    2019-04-09  47.755292  47.755292  ...   97.278109     0  False
..          ...        ...        ...  ...         ...   ...    ...
598  2021-09-13  31.580000  33.380000  ...  -70.841885     0  False
599  2021-09-14  32.520000  35.000000  ...  -71.822773     0  False
600  2021-09-15  33.020000  36.250000  ...  -79.015949     0   True
601  2021-09-16  37.900000  38.000000  ...  -77.381136     0  False
602  2021-09-17  35.510000  39.050000  ...  -85.119250     0   True

[603 rows x 18 columns]

The penultimate line and the last line show the daily limit on September 15 and 17, 2021. Take a look at the K-line chart:

It can also be seen that both September 15 and 17, 2021 are the daily limit, and the calculation results are correct.

Summary

This paper mainly introduces the idea of the strategy to be implemented, completes the calculation of the limit factor, and then continues to introduce the implementation of other factors required by the strategy.

So far, the created data is only used for printing, and storage is not realized. Therefore, as long as the program can run normally, there is no need to wait for the program to run. After all the factors in the subsequent articles are introduced, we will perform multi-threaded calculation and save the results to MySQL.

data_ center_ All codes of v3.py are as follows:

import baostock as bs
import datetime
import sys
import numpy as np

# Available daily line quantity constraints
g_available_days_limit = 250

# BaoStock daily data field
g_baostock_data_fields = 'date,open,high,low,close,preclose,volume,amount,adjustflag,turn,tradestatus,pctChg,peTTM,pbMRQ, psTTM,pcfNcfTTM,isST'


def get_stock_codes(date=None):
    """
    Gets the of the specified date A Stock code list

    If parameter date If it is blank, the date of the last trading day will be returned A Stock code list
    If parameter date If it is not empty and is a trading day, return date Current day A Stock code list
    If parameter date If it is not empty but not a trading day, the non trading day information will be printed and the program will exit

    :param date: date
    :return: A List of stock codes
    """

    # Log in to biostock
    bs.login()

    # Query stock data from BaoStock
    stock_df = bs.query_all_stock(date).get_data()

    # If the length of the acquired data is 0, it means that the date is not a trading day
    if 0 == len(stock_df):

        # If the parameter date is set, the print message indicates that date is a non trading day
        if date is not None:
            print('The currently selected date is a non trading day or there is no trading data, please set it date Is the date of a historical trading day')
            sys.exit(0)

        # If the parameter date is not set, the latest trading day will be found from the history. When the length of stock data obtained is not 0, the latest trading day will be found
        delta = 1
        while 0 == len(stock_df):
            stock_df = bs.query_all_stock(datetime.date.today() - datetime.timedelta(days=delta)).get_data()
            delta += 1

    # Logout login
    bs.logout()

    # Through stock data screening, the stock codes of Shanghai Stock Exchange and Shenzhen Stock Exchange are between sh.600000 and sz.39900
    stock_df = stock_df[(stock_df['code'] >= 'sh.600000') & (stock_df['code'] < 'sz.399000')]

    # Return to stock list
    return stock_df['code'].tolist()


def create_data(stock_codes, from_date='1990-12-19', to_date=datetime.date.today().strftime('%Y-%m-%d'),
                adjustflag='2'):
    """
    Download the daily data of the specified stock within the specified date and calculate the expansion factor

    :param stock_codes: Stock code of data to be downloaded
    :param from_date: Daily line start date
    :param to_date: Daily line end date
    :param adjustflag: Option 1: Post reinstatement option 2: pre reinstatement option 3: no reinstatement option, default to pre reinstatement option
    :return: None
    """

    # Download stock cycle
    for code in stock_codes:
        print('Downloading{}...'.format(code))

        # Log in to BaoStock
        bs.login()

        # Download daily data
        out_df = bs.query_history_k_data_plus(code, g_baostock_data_fields, start_date=from_date, end_date=to_date,
                                              frequency='d', adjustflag=adjustflag).get_data()

        # Logout login
        bs.logout()

        # Eliminate stop disk data
        if out_df.shape[0]:
            out_df = out_df[(out_df['volume'] != '0') & (out_df['volume'] != '')]

        # If the data is empty, it is not created
        if not out_df.shape[0]:
            continue

        # Delete duplicate data
        out_df.drop_duplicates(['date'], inplace=True)

        # Daily data is less than g_available_days_limit, do not create
        if out_df.shape[0] < g_available_days_limit:
            continue

        # Convert numerical data to float type for subsequent processing
        convert_list = ['open', 'high', 'low', 'close', 'preclose', 'volume', 'amount', 'turn', 'pctChg']
        out_df[convert_list] = out_df[convert_list].astype(float)

        # Reset index
        out_df.reset_index(drop=True, inplace=True)

        # Calculate expansion factor
        out_df = extend_factor(out_df)

        print(out_df)


def extend_factor(df):
    """
    Calculate expansion factor

    :param df: Expansion factor to be calculated DataFrame
    :return: With expansion factor DataFrame
    """

    # Use pipe to calculate the limit factor
    df = df.pipe(zt)

    return df


def zt(df):
    """
    Calculate the limit factor

    If the limit rises, the factor is True´╝îOtherwise False
    The closing price of the current day was 9% higher than the closing price of the previous day.8%And above as the trading judgment standard

    :param df: Expansion factor to be calculated DataFrame
    :return: With expansion factor DataFrame
    """

    df['zt'] = np.where((df['close'].values >= 1.098 * df['preclose'].values), True, False)

    return df


if __name__ == '__main__':
    stock_codes = get_stock_codes()
    create_data(stock_codes)

Blog content is only used for communication and learning, does not constitute investment suggestions, and is responsible for its own profits and losses!
Personal blog: https://coderx.com.cn/ (priority update)
Welcome to forward and leave messages. Wechat group has been established for learning and communication. Group 1 is full and group 2 has been created. Interested readers please scan the code and add wechat!

Tags: Python backtrader

Posted on Fri, 26 Nov 2021 16:19:47 -0500 by timlondon