# data wrangling
import numpy as np
import pandas as pd
# plots
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from matplotlib.cm import get_cmap
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.font_manager import FontProperties
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap
from mpl_toolkits.mplot3d.art3d import Line3DCollection
# set the type of animations
rc('animation', html='html5')
# ------------------- ADD INDEX DATA BELOW! ------------------------
# generate RANDOM prices (one for each index)
index_returns = np.random.normal(loc=1e-4, scale=5e-3, size=(783, 9))
index_returns = np.vstack((np.zeros(shape=(1, 9)) + 100, index_returns))
# generate the cummulative returns
index_prices = np.cumprod(1 + index_returns, axis=0)
# select the window
window = 261
indexes_rolling = np.zeros(shape=(index_prices.shape[0]-window, 9))
# generate the rolling equity
for i in range(window, index_prices.shape[0], 1):
indexes_rolling[i-window] = (index_prices[i]/index_prices[i-window]) - 1
# generate dataframe with the data
index = pd.date_range('2019-01-01', periods=index_prices.shape[0]-window, freq='B')
columns = ['GER | DAX', 'UK | UKX', 'FR | CAC', 'IT | FTSEMIB', 'ES | IBEX', 'NL | AEX', 'CH | SMI', 'SE | OMX', 'BE | BEL20']
indexes_rolling = pd.DataFrame(indexes_rolling, index=index, columns=columns)
# ------------------- ADD INDEX DATA ABOVE! ------------------------
# create the figure
fig, axes = plt.subplots(1, 2, figsize=(9, 3.6), gridspec_kw={'width_ratios': [.9, .1]})
fig.patch.set_alpha(1)
# make right plot invisible and only update left one
axes[1].axis('off')
ax = axes[0]
# get the cmap to use
cmap = get_cmap('RdYlGn')
# get the slice
current_slice = indexes_rolling.values[:261, :]
index_names = indexes_rolling.columns
index_dates = indexes_rolling.index
# list holding the lines
lines = []
# for each index...
for i in range(current_slice.shape[1]):
# get the coordinates
x = np.array(np.arange(current_slice.shape[0]))
y = np.array(current_slice[:, i])
# crete points and segments to color
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(-0.19, 0.19)
lc = LineCollection(segments, cmap=cmap, norm=norm)
# Set the values used for colormapping
lc.set_array(y)
lc.set_linewidth(2)
lc.set_color(cmap(y[-1] * 2.5 + 0.5))
lc.set_label(index_names[i])
lines.append(ax.add_collection(lc))
# add the grids
ax.legend(loc='center right', bbox_to_anchor=(1.32, 0.5), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'DejaVu Sans Mono'})
ax.yaxis.grid(color='gray', linestyle='dashed')
ax.xaxis.grid(color='gray', linestyle='dashed')
ax.set_xlim(0, current_slice.shape[0]-1)
ax.set_ylim(-0.39, 0.39)
ax.set_yticklabels(['{:.0%}'.format(val) for val in ax.get_yticks()])
ax.set_ylabel('Rolling Equity 1Y')
ax.set_xlabel('Date')
ax.set_xticklabels([index_dates[int(val)].strftime('%m/%y') for val in ax.get_xticks()])
ax.set_facecolor((0, 0, 0, 1.0))
# show the plot
plt.show()
def update_lines_2D(num, data, columns, dates, cmap, lines, ax):
'''
Function that updates the lines of a plot in 2D
'''
# get the slice
current_slice = data[num:261+num, :]
current_dates = dates[num:261+num]
# for each index...
for i in range(current_slice.shape[1]):
# get the coordinates
x = np.array(np.arange(current_slice.shape[0]))
y = np.array(current_slice[:, i])
# crete points and segments to color
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(-0.22, 0.22)
lines[i].set_segments(segments)
lines[i].set_array(y)
lines[i].set_color(cmap(y[-1] * 2.5 + 0.5))
# update the ticks and labels
ax.set_xticklabels([dates[int(val)+num].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''])
ax.legend(loc='center right', bbox_to_anchor=(1.32, 0.5), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'DejaVu Sans Mono'})
# return the lines
return lines
def init_lines_2D():
'''
Function that initiates the lines of a plot in 2D
'''
for line in lines:
line.set_array([])
return lines
# create the animation
line_ani = animation.FuncAnimation(fig=fig,
func=update_lines_2D,
# frames=30,
frames=indexes_rolling.shape[0]-261,
init_func=init_lines_2D,
fargs=(indexes_rolling.values, indexes_rolling.columns, indexes_rolling.index, cmap, lines, ax),
interval=75,
blit=True)
# show the animation
line_ani
# # callback to show the evolution
# progress_callback = lambda i, n: print('Saving frame {:.0%}'.format(i/n)) if int((i/n) * 100) % 10 == 0 else None
# # save the animation
# line_ani.save('./2D_animation.gif', writer='imagemagick', fps=14, dpi=80, codec='h264', bitrate=2048, progress_callback=progress_callback)
# create the figure
fig = plt.figure(figsize=(14.4, 9))
ax = fig.add_subplot(111, projection='3d')
fig.patch.set_alpha(1)
# get the cmap to use
cmap = get_cmap('RdYlGn')
# get the slice
current_slice = indexes_rolling.values[:261, :]
index_names = indexes_rolling.columns
index_dates = indexes_rolling.index
# list holding the lines
lines = []
# for each index...
for i in range(current_slice.shape[1]):
# get the coordinates
x = np.array(np.arange(current_slice.shape[0]))
y = np.tile(i, current_slice.shape[0])
z = np.array(current_slice[:, i])
# crete points and segments to color
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(-0.19, 0.19)
lc = Line3DCollection(segments, cmap=cmap, norm=norm, zorder=current_slice.shape[1]-i)
# Set the values used for colormapping
lc.set_array(z)
lc.set_linewidth(2)
lc.set_color(cmap(z[-1] * 2.5 + 0.5))
lc.set_label(index_names[i])
lines.append(ax.add_collection(lc))
# add the grids
ax.legend(loc='center right', bbox_to_anchor=(1.1, 0.46), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'DejaVu Sans Mono'})
ax.set_zlabel('Rolling Equity 1Y', labelpad=10)
ax.set_zlim(-0.39, 0.39)
ax.set_zticklabels([' '* 3 + '{:.0%}'.format(val) for val in ax.get_zticks()], fontdict={'verticalalignment': 'center', 'horizontalalignment': 'center'})
ax.set_xlabel('Date', labelpad=30)
ax.set_xlim(0, current_slice.shape[0]-1)
ax.set_xticklabels([index_dates[int(val)].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''], rotation=0, fontdict={'verticalalignment': 'top', 'horizontalalignment': 'center'})
ax.set_yticks(np.arange(current_slice.shape[1]))
ax.set_yticklabels([index_names[i] for i in range(current_slice.shape[1])], rotation=-15, fontdict={'verticalalignment': 'center', 'horizontalalignment': 'left'})
ax.w_xaxis.set_pane_color((0, 0, 0, 1.0))
ax.w_yaxis.set_pane_color((0, 0, 0, 1.0))
ax.w_zaxis.set_pane_color((0, 0, 0, 1.0))
ax.view_init(25, -60)
# ------------------------------------------------------------------
x_scale=1.8
y_scale=1
z_scale=1
scale=np.diag([x_scale, y_scale, z_scale, 1.0])
scale=scale*(1.0/scale.max())
scale[3,3]=1.0
def short_proj():
return np.dot(Axes3D.get_proj(ax), scale)
ax.get_proj=short_proj
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
# ------------------------------------------------------------------
# show the plot
plt.show()
def update_lines_3D(num, data, columns, dates, cmap, lines, ax):
'''
Function that updates the lines of a plot in 2D
'''
# get the slice
current_slice = data[num:261+num, :]
current_dates = dates[num:261+num]
# for each index...
for i in range(current_slice.shape[1]):
# get the coordinates
x = np.arange(current_slice.shape[0])
y = np.tile(i, current_slice.shape[0])
z = np.array(current_slice[:, i])
# crete points and segments to color
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(-0.19, 0.19)
lines[i].set_segments(segments)
lines[i].set_array(z)
lines[i].set_color(cmap(z[-1] * 2.5 + 0.5))
# update the ticks and labels
ax.set_xticklabels([dates[int(val)+num].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''], rotation=0, fontdict={'verticalalignment': 'top', 'horizontalalignment': 'center'})
ax.legend(loc='center right', bbox_to_anchor=(1.1, 0.46), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'DejaVu Sans Mono'})
# return the lines
return lines
def init_lines_3D():
for line in lines:
line.set_array([])
return lines
# create the animation
line_ani = animation.FuncAnimation(fig=fig,
func=update_lines_3D,
# frames=30,
frames=indexes_rolling.shape[0]-261,
init_func=init_lines_3D,
fargs=(indexes_rolling.values, indexes_rolling.columns, indexes_rolling.index, cmap, lines, ax),
interval=75,
blit=True)
# show the animation
line_ani
# # callback to show the evolution
# progress_callback = lambda i, n: print('Saving frame {:.0%}'.format(i/n)) if int((i/n) * 100) % 10 == 0 else None
# # save the animation
# line_ani.save('./3D_animation.gif', writer='imagemagick', fps=14, dpi=80, codec='h264', bitrate=2048, progress_callback=progress_callback)
# create the figure
fig = plt.figure(figsize=(14.4, 9))
ax = fig.add_subplot(111, projection='3d')
fig.patch.set_alpha(1)
# get the cmap to use
cmap = get_cmap('RdYlGn')
# get the slice
# current_slice = indexes_rolling.values[:261, :]
current_slice = indexes_rolling.values[:int(261/2), :]
index_names = indexes_rolling.columns
index_dates = indexes_rolling.index
# list holding the lines
lines = []
# for each index...
for i in range(current_slice.shape[1]):
# get the coordinates
x = np.array(np.arange(current_slice.shape[0]))
y = np.tile(i, current_slice.shape[0])
z = np.array(current_slice[:, i])
# crete points and segments to color
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(-0.19, 0.19)
lc = Line3DCollection(segments, cmap=cmap, norm=norm, zorder=current_slice.shape[1]-i)
# Set the values used for colormapping
lc.set_array(z)
lc.set_linewidth(2)
lc.set_color(cmap(z[-1] * 2.5 + 0.5))
lc.set_label(index_names[i])
lines.append(ax.add_collection(lc))
# list holding the mesh lines
mesh_lines = []
# for each day...
for j in range(current_slice.shape[0]):
if j % 1 == 0:
# get the coordinates
x = np.tile(j, current_slice.shape[1])
y = np.arange(current_slice.shape[1])
z = np.array(current_slice[j, :])
# crete points and segments to color
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(-0.19, 0.19)
lc = Line3DCollection(segments, cmap=cmap, norm=norm)
# Set the values used for colormapping
lc.set_array(z)
lc.set_linewidth(2)
mesh_lines.append(ax.add_collection(lc))
# add the grids
ax.legend(loc='center right', bbox_to_anchor=(1.1, 0.46), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'DejaVu Sans Mono'})
ax.set_zlabel('Rolling Equity 1Y', labelpad=10)
ax.set_zlim(-0.39, 0.39)
ax.set_zticklabels([' '* 3 + '{:.0%}'.format(val) for val in ax.get_zticks()], fontdict={'verticalalignment': 'center', 'horizontalalignment': 'center'})
ax.set_xlabel('Date', labelpad=30)
ax.set_xlim(0, current_slice.shape[0]-1)
ax.set_xticklabels([index_dates[int(val)].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''], rotation=0, fontdict={'verticalalignment': 'top', 'horizontalalignment': 'center'})
ax.set_yticks(np.arange(current_slice.shape[1]))
ax.set_yticklabels([index_names[i]for i in range(current_slice.shape[1])], rotation=-15, fontdict={'verticalalignment': 'center', 'horizontalalignment': 'left'})
ax.w_xaxis.set_pane_color((0, 0, 0, 1.0))
ax.w_yaxis.set_pane_color((0, 0, 0, 1.0))
ax.w_zaxis.set_pane_color((0, 0, 0, 1.0))
ax.view_init(25, -60)
# ------------------------------------------------------------------
x_scale=1.8
y_scale=1
z_scale=1
scale=np.diag([x_scale, y_scale, z_scale, 1.0])
scale=scale*(1.0/scale.max())
scale[3,3]=1.0
def short_proj():
return np.dot(Axes3D.get_proj(ax), scale)
ax.get_proj=short_proj
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
# ------------------------------------------------------------------
# show the plot
plt.show()
def update_mesh_lines_3D(num, data, columns, dates, cmap, lines, mesh_lines, ax):
'''
Function that updates the lines of a plot in 2D
'''
# get the slice
# current_slice = data[num:261+num, :]
current_slice = data[num:int(261/2)+num, :]
# for each index...
for i in range(current_slice.shape[1]):
# get the coordinates
x = np.arange(current_slice.shape[0])
y = np.tile(i, current_slice.shape[0])
z = np.array(current_slice[:, i])
# crete points and segments to color
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(-0.19, 0.19)
lines[i].set_segments(segments)
lines[i].set_array(z)
lines[i].set_color(cmap(z[-1] * 2.5 + 0.5))
# counter to check the current mesh line
counter = 0
# for each day...
for j in range(current_slice.shape[0]):
if j % 1 == 0:
# get the coordinates
x = np.tile(j, current_slice.shape[1])
y = np.arange(current_slice.shape[1])
z = np.array(current_slice[j, :])
# crete points and segments to color
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# Set the values used for colormapping
norm = plt.Normalize(-0.22, 0.22)
mesh_lines[counter].set_segments(segments)
mesh_lines[counter].set_array(z)
counter += 1
# update the ticks and labels
ax.set_xticklabels([dates[int(val)+num].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''], rotation=0, fontdict={'verticalalignment': 'top', 'horizontalalignment': 'center'})
ax.legend(loc='center right', bbox_to_anchor=(1.1, 0.46), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'DejaVu Sans Mono'})
# return the lines
return lines
def init_mesh_lines_3D():
for line in lines:
line.set_array([])
return lines
# create the animation
line_ani = animation.FuncAnimation(fig=fig,
func=update_mesh_lines_3D,
# frames=30,
frames=indexes_rolling.shape[0]-int(261/2),
init_func=init_mesh_lines_3D,
fargs=(indexes_rolling.values, indexes_rolling.columns, indexes_rolling.index, cmap, lines, mesh_lines, ax),
interval=100,
blit=True)
# show the animation
line_ani
# # callback to show the evolution
# progress_callback = lambda i, n: print('Saving frame {:.0%}'.format(i/n)) if int((i/n) * 100) % 10 == 0 else None
# # save the animation
# line_ani.save('./3D_mesh_animation.gif', writer='imagemagick', fps=14, dpi=80, codec='h264', bitrate=2048, progress_callback=progress_callback)