Optimisation de portefeuille avec Python (modèle de distribution moyenne de Markovitz)

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.

Acquisition des données de cours de bourse

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. hoge.png

Créer des données de panneau pour l'actif cible

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.

Modèle de variance moyenne de Markovitz et sa solution

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.

Modèle de dispersion moyenne de Markowitz

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.

\begin{align} \underset{\bf x}{\rm minimize} ~~~ &{\bf x}^T \Sigma {\bf x} \\\ {\rm subject~to} ~~~ &{\bf r}^T {\bf x} = \sum_{i=1}^{n} r_i x_i \geq r_e \\\ &{\|\| {\bf x} \|\|}\_{1} = \sum_{i=1}^{n} x_i = 1 \\\ &x_i \geq 0 ~~ (i = 1, \cdots, n) \end{align}

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.

Comment utiliser CVXOPT

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.

\begin{align} \underset{\bf x}{\rm minimize} ~~~ &\frac{1}{2} {\bf x}^{T} P {\bf x} + {\bf q}^{T} {\bf x} \\\ {\rm subject~to} ~~~ & G {\bf x} \leq {\bf h} \\\ &A {\bf x} = {\bf b} \end{align}

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, $ P = 2 \cdot \Sigma, ~~~ q = {\bf 0}, ~~~ G = - {\bf I}\_n, ~~~ h = - r_e, ~~~ A = {\bf 1}\_n^T, ~~~ b = 1 $ Il devient.

référence:

Calculé en Python

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

Optimisation de portefeuille avec Python (modèle de distribution moyenne de Markovitz)
Moyenne harmonique par Python (en utilisant SciPy)
Optimisation bayésienne très simple avec Python
[Python] Modèle gaussien mixte avec Pyro
Explication du modèle d'optimisation de la production par Python
Résolvons le portefeuille avec une optimisation continue
Optimisation apprise avec OR-Tools [Planification linéaire: modèle en plusieurs étapes]
[Python] Clustering avec un modèle gaussien infiniment mélangé
Résolution du modèle Lorenz 96 avec Julia et Python
Python en optimisation
FizzBuzz en Python3
Grattage avec Python
Statistiques avec python
Grattage avec Python
Python avec Go
Twilio avec Python
Intégrer avec Python
Jouez avec 2016-Python
AES256 avec python
Testé avec Python
python commence par ()
avec syntaxe (Python)
Bingo avec python
Zundokokiyoshi avec python
Excel avec Python
Micro-ordinateur avec Python
Cast avec python
Simulez une bonne date de Noël avec un modèle optimisé Python
Je veux gérer l'optimisation avec python et cplex
Estimation la plus probable de la moyenne et de la variance avec TensorFlow
[# 2] Créez Minecraft avec Python. ~ Dessin du modèle et implémentation du lecteur ~
Calculer la moyenne, la médiane, la plus fréquente, la variance, l'écart type en Python
Simulation COVID-19 avec python (modèle SIR) ~~ avec carte de chaleur préfectorale ~~
Découvrez Wasserstein GAN avec le modèle Keras et l'optimisation TensorFlow