Negociação Quantitativa: Estratégia Take Profit e Backtesting

Quando compramos fundos ou ações, costumamos usar a estratégia mais simples para tomar decisões: comprar na baixa e vender na alta.

Então, como você expressa essa estratégia em código? A primeira definição do sinal de negociação é: compre a 0,5%, a linha de lucro alvo é de 1,5% e a posição será liberada quando o rendimento alvo for atingido. Em seguida, vamos usar o Python para testar os ganhos dessa estratégia:


obter conjunto de dados

# 安装akshare
!pip3 install akshare --upgrade 
# 调用包
import akshare as ak
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 不显示警告
import warnings
warnings.filterwarnings('ignore')

# 导入上证指数etf
data = ak.fund_etf_fund_info_em('510210','20210122','20230620')


# 筛选要用的时间段
data['year']=pd.to_datetime(data['净值日期']).dt.year # 增加年字段
df = data.loc[data['year']==2023,['净值日期','单位净值']]  # 筛选数据集
df.index=range(len(df)) # 重排索引
df.tail(10) # 查看最近的n条数据

Código detalhado:

!pip3 install akshare Instale o pacote akshare para obter conjuntos de dados gratuitos.

fund_etf_fund_info_em() é a função do akshare para obter ETF, veja aqui para mais funções ↓ Aquisição de dados quantitativos da transação do akshare set_Como usar o blog do akshare_Zi Angzhang-CSDN Blog

pd.to_datetime() pode converter dados para o formato de hora. Originalmente, data['net value date'] está no formato de string; depois de converter para o formato de hora, dt.year pode ser chamado para retornar o ano.

O indexador .loc pode localizar linhas e filtrar colunas; data['year']==2023 retorna dados booleanos, o que significa localizar a linha especificada; ['net value date','unit net value'] significa filtrar o especificado coluna.

.index indica o índice do DateFrame de chamada, a função range() gera uma sequência de inteiros começando em 0 e len() obtém o comprimento ou número de linhas. Como os dados em 2023 são interceptados, o índice não começa em 0, portanto, ele precisa ser reatribuído ao índice.

resultado da operação:


Julgando os sinais de negociação

# 计算环比上一个交易日的涨跌幅
df['涨跌幅'] = df['单位净值'].pct_change()
# 判断买入信号和 对应交易数量
df.loc[df['涨跌幅']<-0.005,'买入数量'] = 1000
# 计算当天交易金额
df['交易金额'] = df['买入数量']*df['单位净值']
df = df.fillna(0.0) # 填补空值
df.tail(30)  

Código detalhado:

pct_change() calcula a razão da cadeia e retorna o rendimento do dia, a fórmula é: razão da cadeia=período atual/período base-1.

df.loc[df['taxa de variação']<-0,005,'quantidade comprada'] usa o indexador .loc, df['taxa de variação']<-0,005 refere-se a uma diminuição de 0,5% em relação ao dia anterior, que é Sinal de negociação; se você comprar 1.000 ações para atender ao sinal de negociação, a 'quantidade de compra' entre colchetes é nosso parâmetro predefinido. Se houver um valor, o valor do resultado 1000 será armazenado no parâmetro padrão.

Depois de ter o número de transações por dia, multiplique pelo preço por ação do dia para obter o valor da transação do dia.

df.fillna(0.0) indica que o valor nulo recebe um valor de 0 para evitar que valores nulos subsequentes afetem o cálculo.

resultado da operação:


backtest

Na etapa de cálculo do sinal de negociação na etapa anterior, a quantidade da transação e o valor da transação comprada no dia são conhecidos, e a retenção histórica e a situação histórica de lucros e perdas devem ser calculadas a seguir.


# 定义全局变量
target_return = 0.015  # 止盈比例,收益率超过此比例时抛出

# 初始化变量
df['总持有数量'] = 0
total_num = 0 # 总持有数量
df['总持有成本'] = 0
total_cost = 0 # 总持有成本
df['持仓成本单价'] = 0
per_cost = 0 # 持仓单价
df['总市值'] = 0
stock = 0
df['盈亏额'] = 0
profit = 0
df['盈亏率'] = 0
profit_rate = 0

df['止盈线利润'] = 0


for i in range(len(df)):
    total_num += df['买入数量'][i] # 累计数量
    total_cost += df['交易金额'][i] # 累计成本
    per_cost = total_cost/total_num
    stock = total_num*df['单位净值'][i]
    profit = stock - total_cost
    profit_rate = df['单位净值'][i]/per_cost-1
    
    df = df.fillna(0.0) # 填补空值    

    if profit_rate >= target_return:
        total_num = 0
        total_cost = 0
        df['止盈线利润'][i] = profit
    
    df['总持有数量'][i] = total_num
    df['总持有成本'][i] = total_cost
    df['持仓成本单价'][i] = per_cost
    df['总市值'][i] = stock
    df['盈亏额'][i] = profit
    df['盈亏率'][i] = profit_rate
    
    
df.tail(30)    

Código detalhado:

Como existe uma lógica de limpar 0 para atingir a taxa de retorno desejada, ela é representada por um loop for:

para i in range(len(df)) refere-se a percorrer cada linha de dados;

se profit_rate >= target_return significa que, quando for maior que o valor predefinido de 0,015, a quantidade de estoque total_num e o custo de estoque total_cost serão todos redefinidos para zero.

O significado de cada fórmula no loop for:

total_num += df['quantidade comprada'][i]: soma o número de ações compradas a cada dia para calcular a quantidade acumulada atual; total_cost
+= df['quantidade da transação'][i]: soma cada dia Calcula o acumulado custo de manutenção;
per_cost = total_cost/total_num: calcula o preço unitário de custo por ação;
estoque = total_num*df['valor líquido unitário'][i]: calcula o valor de mercado total atual;
lucro = estoque - custo_total: lucro total = total valor de mercado - custo total;
taxa_lucro = df['valor líquido unitário'][i]/custo-1: taxa de retorno=preço atual da ação/custo unitário por ação-1;

Uma vez que os resultados do cálculo de cada etapa devem ser armazenados em uma tabela bidimensional para posterior visualização da saída, cada campo a ser salvo precisa ser predefinido e atribuído um valor de 0. E atribua-o a uma variável no final de cada loop.

resultado da operação:


resultado de saída

max_cost = df['总持有成本'].max()
total_profit = df['止盈线利润'].sum()
profit_rate = total_profit/max_cost
sale_times = (df['止盈线利润']>0).sum()

print('最大持有成本:%f,总利润:%f,收益率:%f,达标次数:%s'%(max_cost,total_profit,profit_rate,sale_times))

Código detalhado:

A função max() retorna o valor máximo da coluna 'total holding cost', representando o custo máximo de entrada.

A função sum() retorna a soma da coluna 'Take Profit Line Profit', representando o total de lucros e perdas.

(df['Take Profit Line Profit']>0) é um julgamento condicional e retorna um valor booleano; como True significa 1, pode ser contado com sum().

resultado da operação:

Interpretação dos resultados:

De acordo com o mercado no primeiro semestre de 2023, o custo máximo de manutenção é inferior a 8k e a taxa de retorno com base no custo máximo é de 3,6%; nossa linha de lucro predefinida é de 1,5% e o número de liquidações quando a taxa de retorno alvo é atingida é de 6 vezes.

Usando os dados de 2021 e 2022 para backtesting, a taxa de retorno é próxima à taxa de juros dos depósitos, mas pode haver taxas de transação na oferta real que não foram levadas em consideração. Em geral, embora essa estratégia não esteja perdendo dinheiro , o retorno não é o ideal e precisamos continuar a explorar outras opções de método.

Acho que você gosta

Origin blog.csdn.net/Sukey666666/article/details/131303990
Recomendado
Clasificación