Ceci est un résumé des conseils pour le backtesting avec Python. Finissons rapidement le prétraitement gênant et concentrons-nous sur la création d'un modèle! Tel est le but. Bien que cela ne soit pas présenté dans l'article, vous pouvez essayer divers modèles de sélection de portefeuille tels que le modèle multifactoriel, car vous pouvez obtenir des données de macro avec pandas-datareader
.
Overview: --Préparez un environnement pour le backtesting avec Python.
Tout d'abord, installez pandas-datareader
dans votre environnement.
pandas-datareader
est un package Python pratique (compatible pandas.Dataframe) qui vous permet de télécharger des données de marché telles que les cours des actions via l'API Web. IEX, Banque mondiale, OCDE, Yahoo! Finance, FRED, [Stooq] Vous pouvez lire les données acquises sur le code python en accédant à une API telle que (https://stooq.com/q/?s=usdkrw) en interne. Veuillez vous référer au Document officiel pour une utilisation détaillée.
# Install pandas-datareader (latest version)
pip install git+https://github.com/pydata/pandas-datareader.git
# Or install pandas-datareader (stable version)
pip install pandas-datareader
Cette fois, les produits cibles sont des actions cotées à la Bourse de Tokyo (TSE).
La plupart des données publiées sur le Web se trouvent sur le marché américain, mais le site le plus puissant de Pologne stooq.com est Tokyo Securities Trading. Les données passées du lieu sont ouvertes au public. Utilisez pandas-datareader
pour obtenir des données de marque individuelles de stooq.
Fondamentalement, vous pouvez exécuter pandas_datareader.stooq.StooqDailyReader ()
. Pour les arguments, spécifiez le code du titre (ou symbole boursier) enregistré sur chaque marché et le site (Yahoo!, Stooq, ...) de la source de divulgation des données.
Un code de titres à 4 chiffres est attribué aux actions gérées par la Bourse de Tokyo, nous allons donc l'utiliser cette fois. (Exemple: Toyota Motor est une action dont le code valeur est ** 7203 ** sur le TSE. À NYSE, il est négocié comme une action dont le symbole boursier est ** TM **.)
À titre de test, prenons les données sur le cours de l'action de Toyota Motor Corporation (TSE: 7203) et représentons-les.
import datetime
import pandas_datareader
start = datetime.datetime(2015, 1, 1)
end = datetime.datetime(2020, 11, 30)
stockcode = "7203.jp" # Toyota Motor Corporation (TM)
df = pandas_datareader.stooq.StooqDailyReader(stockcode, start, end).read()
df = df.sort_values(by='Date',ascending=True)
display(df) # Show dataframe
-----
Open High Low Close Volume
Date
2015-01-05 6756.50 6765.42 6623.43 6704.69 10653925
2015-01-06 6539.48 6601.09 6519.83 6519.83 13870266
2015-01-07 6480.52 6685.05 6479.64 6615.40 12837377
2015-01-08 6698.46 6748.46 6693.98 6746.69 11257646
2015-01-09 6814.56 6846.70 6752.92 6795.80 11672928
... ... ... ... ... ...
2020-11-04 7024.00 7054.00 6976.00 6976.00 6278100
2020-11-05 6955.00 7032.00 6923.00 6984.00 5643400
2020-11-06 7070.00 7152.00 7015.00 7019.00 11092900
2020-11-09 7159.00 7242.00 7119.00 7173.00 7838600
2020-11-10 7320.00 7360.00 7212.00 7267.00 8825700
Les données quotidiennes de transition du cours des actions ont été acquises sous forme de pandas.
Tracons le contenu du df
que nous venons de créer. (Utilisez essentiellement le cours de clôture)
# Plot timeseries (2015/1/1 - 2020/11/30)
plt.figure(figsize=(12,8))
plt.plot(df.index, df["Close"].values)
plt.show()
Comme le montre la figure ci-dessous, la transition du cours de clôture (Close) pourrait être facilement tracée.
Pour vous préparer à résoudre le problème d'optimisation du portefeuille, créez des données de panel pour plusieurs actifs (actions) et organisez-les comme un objet pandas.Dataframe.
Cette fois, nous sélectionnerons 5 actions listées dans TOPIX 500 et les utiliserons comme actifs cibles d'investissement. De plus, en tant que prétraitement, le «prix de clôture» est converti en «taux de profit basé sur le prix de clôture». Veuillez changer le code de cette partie en fonction de la situation.
import datetime
import numpy as np
import pandas as pd
import pandas_datareader.data as web
import pandas_datareader.stooq as stooq
def get_stockvalues_tokyo(stockcode, start, end, use_ratio=False):
"""
stockcode: market code of each target stock (ex. "NNNN") defined by the Tokyo stock market.
start, end: datetime object
"""
# Get index data from https://stooq.com/
df = stooq.StooqDailyReader(f"{stockcode}.jp", start, end).read()
df = df.sort_values(by='Date',ascending=True)
if use_ratio:
df = df.apply(lambda x: (x - x[0]) / x[0] )
return df
def get_paneldata_tokyo(stockcodes, start, end, use_ratio=False):
# Use "Close" value only
dfs = []
for sc in stockcodes:
df = get_stockvalues_tokyo(sc, start, end, use_ratio)[['Close']]
df = df.rename(columns={'Close': sc})
dfs.append(df)
df_concat = pd.concat(dfs, axis=1)
return df_concat
Créez des données de panneau en utilisant get_paneldata_tokyo ()
.
start = datetime.datetime(2015, 1, 1)
end = datetime.datetime(2020, 11, 30)
stockcodes=["1301", "1762", "1820", "1967", "2127"]
df = get_paneldata_tokyo(stockcodes, start, end, use_ratio=True)
display(df) # return ratio daily
-----
1301 1762 1820 1967 2127
Date
2015-01-05 0.000000 0.000000 0.000000 0.000000 0.000000
2015-01-06 -0.010929 -0.018385 -0.033937 -0.002265 -0.038448
2015-01-07 -0.014564 -0.020433 -0.059863 -0.013823 -0.059680
2015-01-08 -0.007302 -0.016338 -0.057883 -0.013823 -0.039787
2015-01-09 0.000000 -0.004490 -0.031938 -0.025407 -0.043770
... ... ... ... ... ...
2020-10-29 0.096138 -0.032923 -0.030777 0.858573 5.682321
2020-10-30 0.093336 -0.039657 -0.041199 0.832831 5.704266
2020-11-02 0.107748 -0.026188 -0.032198 0.845702 5.418978
2020-11-04 0.099341 -0.024392 -0.020829 0.858573 5.704266
2020-11-05 0.069315 -0.014964 -0.042147 0.904909 6.055390
Avec cela, les données de panel de chaque actif à évaluer ont été acquises.
La détermination d'un ratio d'investissement approprié pour plusieurs actifs à investir s'appelle ** optimisation du portefeuille **. Cette fois, nous utiliserons le ** modèle de variance moyenne ** proposé par Markowitz comme paramètre de problème d'optimisation de portefeuille le plus élémentaire.
Dans le modèle de diversification moyenne de Markowitz, nous considérons un problème d'optimisation qui «minimise la diversification du portefeuille» sous la contrainte que «le rendement attendu du portefeuille est supérieur à une certaine valeur».
En général, pour un portefeuille d'actifs $ n $ co, la diversification du portefeuille est une forme quadratique d'une matrice de covariance entre les actifs $ n $ co, donc ce problème d'optimisation est un quadratique Il devient une classe de programmation, QP) et est formulé comme suit.
La première formule de contrainte exige que le rendement attendu du portefeuille soit supérieur ou égal à une certaine valeur ($ = r_e $). Les deuxième et troisième formules de contraintes sont explicites à partir de la définition de portefeuille. Si vous autorisez les ventes à vide d'actifs, vous souhaiterez peut-être supprimer la troisième contrainte.
Ce problème de planification secondaire (QP) est résolu en utilisant le package d'optimisation convexe de Python CVXOPT. Lorsque vous traitez des problèmes de planification secondaires avec CVXOPT, organisez les problèmes d'optimisation que vous souhaitez résoudre dans le format généralisé suivant.
Calculez les paramètres «P», «q», «G», «h», «A» et exécutez la fonction «cvxopt.solvers.qp ()» pour trouver la solution et la valeur optimales. Pour le modèle moyen / distribué de Markowitz,
référence:
Calculez les statistiques requises à partir des données de panneau «df» de l'actif cible.
Matrice de covariance entre les actifs du portefeuille $ \ Sigma $:
df.cov() # Covariance matrix
-----
1301 1762 1820 1967 2127
1301 0.024211 0.015340 0.018243 0.037772 0.081221
1762 0.015340 0.014867 0.015562 0.023735 0.038868
1820 0.018243 0.015562 0.025023 0.029918 0.040811
1967 0.037772 0.023735 0.029918 0.109754 0.312827
2127 0.081221 0.038868 0.040811 0.312827 1.703412
Rendement attendu de chaque actif du portefeuille $ {\ bf r} $:
df.mean().values # Expected returns
-----
array([0.12547322, 0.10879767, 0.07469455, 0.44782516, 1.75209493])
Résolvez le problème d'optimisation à l'aide de CVXOPT.
import cvxopt
def cvxopt_qp_solver(r, r_e, cov):
# CVXOPT QP Solver for Markowitz' Mean-Variance Model
# See https://cvxopt.org/userguide/coneprog.html#quadratic-programming
# See https://cdn.hackaday.io/files/277521187341568/art-mpt.pdf
n = len(r)
r = cvxopt.matrix(r)
P = cvxopt.matrix(2.0 * np.array(cov))
q = cvxopt.matrix(np.zeros((n, 1)))
G = cvxopt.matrix(np.concatenate((-np.transpose(r), -np.identity(n)), 0))
h = cvxopt.matrix(np.concatenate((-np.ones((1,1)) * r_e, np.zeros((n,1))), 0))
A = cvxopt.matrix(1.0, (1, n))
b = cvxopt.matrix(1.0)
sol = cvxopt.solvers.qp(P, q, G, h, A, b)
return sol
r = df.mean().values # Expected returns
r_e = 1.45 * # Lower bound for portfolio's return
cov = df.cov() # Covariance matrix
# Solve QP and derive optimal portfolio
sol = cvxopt_qp_solver(r, r_e, cov)
x_opt = np.array(sol['x'])
print(x_opt)
print("Variance (x_opt) :", sol["primal objective"])
-----
pcost dcost gap pres dres
0: 4.3680e-03 -8.6883e-02 5e+00 2e+00 2e+00
1: 9.1180e-02 -2.2275e-01 5e-01 1e-01 1e-01
2: 2.1337e-02 -6.0274e-02 8e-02 2e-16 1e-16
3: 1.0483e-02 -1.7810e-03 1e-02 1e-16 3e-17
4: 4.9857e-03 1.5180e-03 3e-03 2e-16 8e-18
5: 4.0217e-03 3.6059e-03 4e-04 3e-17 1e-17
6: 3.7560e-03 3.7107e-03 5e-05 3e-17 1e-18
7: 3.7187e-03 3.7168e-03 2e-06 1e-17 4e-18
8: 3.7169e-03 3.7168e-03 2e-08 1e-16 6e-18
Optimal solution found.
[ 5.56e-05]
[ 1.00e+00]
[ 1.76e-05]
[ 3.84e-07]
[ 2.63e-07]
Variance (x_opt): 0.003716866155475511 #Diversification optimale du portefeuille
La solution optimale (ratio d'investissement optimal pour chaque actif) et la valeur optimale (diversification du portefeuille lorsque le ratio d'investissement optimal est appliqué) ont été obtenues. La solution optimale basée sur le modèle de diversification moyen utilisé cette fois est appelée «portefeuille de diversification minimale» car elle met l'accent sur l'optimalité du risque (diversification) du portefeuille.
Dans de nombreux cas, un ratio élevé qui prend en compte le taux de profit (taux d'inflation) des actifs sans risque est utilisé comme indice d'évaluation du taux de profit. Il existe plusieurs façons d'effectuer des backtests, veuillez donc vous référer à des livres et articles spécialisés.
Merci d'avoir lu jusqu'au bout!
Recommended Posts