En este lab, vamos a ver una forma facil y practica de analizar portfolios de Darwinex en Python mediante backtest, lo he hecho con darwins, pero se puede adaptar a cualquier sistema que tengais! Espero que lo disfruteis!
Recuerda pasarte por nuestro canal de Telegram de Python Para Trading

NOTA: En este estudio en la parte de Darwins basura, se ha añadido un darwin, que para evitar el famoso gestor de riesgo de Darwinex, utilizaba tecnicas agrevisas, siendo el propio nominal de la cuenta el stop de la misma. No tengo claro si los inversores estaban advertidos de la situacion. Mas informacion sobre el gestor de riesgo de darwinex, en el blog de QuantumLabs.
Este blog NO fomenta la inversion en Darwinex, consideramos que es un producto con demasiados riesgos para el inversor retail, basado en promesas de pago y apuntes contables. Pero si aun asi, te resulta interesante, deberias entender bien el subyacente de los darwins y al mismo tiempo conocer el darwin de verdad.
Darwinex es un arma de doble filo, sirve tanto para dar vida y salidas a los gestores profesionales, o gente realmente cualificada, y tambien sirve para dar cobertura legal a toda clase de estafas y engañifas. Este articulo esta totalmente sesgado bajo mi criterio o mi experiencia personal, pero es la tonica general que se vive.
Los darwins considerados basura, los he considerado por el siguiente criterio: Captacion en Telegram o demas redes sociales, tratarse de influencers- alumnos de influencers , o gente sin criterio ninguno , mas que “cuatro lineas mal tiradas, y gestionando el calzoncillos desde su casa “
Hay que dejar claro, que personalmente solo conozco al gestor de SYO, que es una INSTITUCION en el mundo del la gestion, deberia ser la referencia para toda la comunidad Darwinex.
Y esto no lo hago por hateo a nadie, simplemente, por que cuando un INVERSOR añade dinero a su cuenta de DARWINEX para obtener una gestion patrimonial, es CAPITAL que deja de consumir en el presente, para poder disfrutar de una prima en el futuro , la RENTABILIDAD. Invertir en productos, como los que catalogamos en el porfolio basura, son un COSTE DE OPORTUNIDAD, y sin contar que dejamos las cosas al azar y demas factores
Una vez dejado claro el contexto, vamos a ponernos a lo que mas nos gustsa, el codigo el codigo!
Cargamos todas las librerias necesarias¶
import empyrical as ep
import pandas as pd
import datetime
import numpy as np
from matplotlib import pyplot as plt
import matplotlib as mpl
import matplotlib.pylab as pylab
import sys, os
import warnings
%matplotlib inline
mpl.rcParams['figure.dpi']= 300
pd.set_option('display.float_format', lambda x: '%.3f' % x)
sns.set_style("whitegrid")
sns.set_palette("Paired")
pylab.rcParams['figure.figsize'] = 16, 6
warnings.filterwarnings("ignore")
Parte 1. ¿Darwins Buenos?¶
¿Realmente buenos? Retornos pasados NO garantizan retornos futuros, pero si que se puede hacer "arqueologia" sobre lo que hicieron, Consideramos que cada punto de variacion del darwin es 1 dolar, por simplificar
portfolio = {
"guenos/SYO.CSV":1, # ruta/ficherodarwin.csv : nivel de apalancamiento del mismo.
"guenos/THA.csv":1,
"guenos/ZVQ.csv":1,
"guenos/LVS.csv":1
}
Creamos un dataframe con todos los darwins, y ademas los sumamos para poder meterles mano¶
df_sessionlogs = {}
for informe_path in portfolio:
multiplicador = portfolio[informe_path]
df = pd.read_csv(informe_path,parse_dates=True,index_col=['Date'])
df["Price"] = df["Price"] * multiplicador
df_sessionlogs[informe_path] = df
df_portfolio = None
for informe_path in df_sessionlogs:
if df_portfolio is None:
df_portfolio = df_sessionlogs[informe_path]
df_portfolio.columns = [informe_path]
else:
df_portfolio = df_portfolio.join(df_sessionlogs[informe_path].rename(columns={
"Price":informe_path
})).fillna(0)
# Creamos la columna total con el PyG del portfolio
df_portfolio["total"] = df_portfolio.sum(axis=1)
Ahora vamos a calcular el capital minimo con el que entrar en el portfolio, No es la forma optima , pero si una aproximacion¶
# Calculamos la peor sesion
peor_sesion = df_portfolio["total"].min()
# Calculamos el peor DD
cumulative = df_portfolio["total"].cumsum().values
max_return = np.fmax.accumulate(cumulative)
peor_drawdown = np.nanmin(cumulative - max_return)
# Utilizamos una formula "inventada" para llegar a un capital minimo aceptable.
capital_sugerido = abs(round(min(5*peor_drawdown,15*peor_sesion),0))
# Creamos el DF con la informacion necesaria, y lo mostramos
df_estadisticas = pd.DataFrame.from_dict({
"Peor Sesion": peor_sesion,
"Peor Drawdown": peor_drawdown,
"Capital Necesario": capital_sugerido
},"index")
df_estadisticas.columns = ["Darwins Buenos"]
Comprobamos que los DDs existen, y debemos controlarlos si no queremos ser fulminados por el mercado¶
df_estadisticas
Calculamos los retornos del portfolio y el resto de estadisticas para poder analizarlo¶
df_returns = (df_portfolio["total"].cumsum() + capital_sugerido).pct_change().dropna() # retornos en $ + capital sugerido
df_performance = pd.DataFrame.from_dict({
"PyG": df_portfolio["total"].cumsum().values[-1],
"Sesiones Analizadas": len(df_portfolio),
"Sesiones Ganadoras (%)": 100 * len(df_portfolio[df_portfolio["total"]>0]) / len(df_portfolio[df_portfolio["total"]!=0]),
"ROI Anualizado": 100 * ep.annual_return(df_returns),
"CAGR" : 100 * ep.cagr(df_returns),
"Volatilidad Anualizada": 100 * ep.annual_volatility(df_returns),
"Profit Factor": df_returns[df_returns > 0].sum() / abs(df_returns[df_returns < 0].sum()),
"Promedio Sesion Ganadora": df_portfolio[df_portfolio["total"] > 0]["total"].mean(),
"Promedio Sesion Perdedora": df_portfolio[df_portfolio["total"] < 0]["total"].mean(),
"Ratio Sharpe": ep.sharpe_ratio(df_returns),
"Ratio Sortino": ep.sortino_ratio(df_returns),
"Ratio Calmar" : ep.calmar_ratio(df_returns),
"Tail Ratio" : ep.tail_ratio(df_returns),
"Omega Ratio" : ep.omega_ratio(df_returns),
"Peor Sesion": peor_sesion,
"Peor Drawdown": peor_drawdown,
"Min Bankroll NoBust": capital_sugerido,
"VAR95%" : ep.value_at_risk(df_returns,0.95),
},"index")
df_performance.columns = ["Portfolio Bueno"]
df_performance
fig = plt.figure(figsize=(22,9))
plt.plot(df_portfolio.drop("total", axis=1).cumsum())
plt.plot(df_portfolio["total"].cumsum(), linewidth=3);
leg = plt.legend(df_portfolio.columns.values)
plt.show()

Conclusiones del Portfolio Bueno
Con un 55% de sesiones ganadoras, y un promedio de 9 puntos por sesion ganadora, y 7 puntos en las perdedoras, no hay mucho mas que decir, es RENTABLE.
En este caso Darwinex es un BUENO para la comunidad de inversores. Con unos ratios Sharpe de casi 3, un Sortino de 5 y un Calmar de 6, no puedo criticar nada de este porfolio
Ahora toca el turno del show. Los darwins que no son malos no… son MALISIMOS
Si alguno se siente mal que su darwin salga aqui, solo tiene que comentarmelo por privado, asi me reire un rato, lo mejor que puede hacer es centrarse en la operativa, por que en esta lista hay darwins con mas de 140.000€ en inversion.
Repetimos el proceso anterior, esta vez con un portfolio que podrias tener, si te hubieran captado en vivirdelforex , el señor Aker, o demas pseudotraders de internet
import pandas as pd
import empyrical as ep
from matplotlib import pyplot as plt
import matplotlib as mpl
import matplotlib.pylab as pylab
import warnings
warnings.filterwarnings("ignore")
vertedero = {
"malos/BAS.CSV":1,
"malos/CQY.csv":1,
"malos/EZN.csv":1,
"malos/HSM.csv":1
}
df_sessionlogs_vertedero = {}
for informe_path in vertedero:
multiplicador = vertedero[informe_path]
df = pd.read_csv(informe_path,parse_dates=True,index_col=['Date'])
df["Price"] = df["Price"] * multiplicador
df_sessionlogs_vertedero[informe_path] = df
df_portfolio_basurero = None
for informe_path in df_sessionlogs_vertedero:
if df_portfolio_basurero is None:
df_portfolio_basurero = df_sessionlogs_vertedero[informe_path]
df_portfolio_basurero.columns = [informe_path]
else:
df_portfolio_basurero = df_portfolio_basurero.join(df_sessionlogs_vertedero[informe_path].rename(columns={
"Price":informe_path
})).fillna(0)
df_portfolio_basurero["total"] = df_portfolio_basurero.sum(axis=1)
peor_sesion_malo = df_portfolio_basurero["total"].min()
cumulative_malo = df_portfolio_basurero["total"].cumsum().values
max_return_malo = np.fmax.accumulate(cumulative_malo)
peor_drawdown_malo = np.nanmin(cumulative_malo - max_return_malo)
capital_sugerido_malo = abs(round(min(5*peor_drawdown_malo,15*peor_sesion_malo),0))
df_estadisticas_malas = pd.DataFrame.from_dict({
"Peor Sesion": peor_sesion_malo,
"Peor Drawdown": peor_drawdown_malo,
"Capital Necesario": capital_sugerido_malo
},"index")
df_estadisticas_malas.columns = ["Darwins BASURA"]
df_estadisticas_malas
df_returns_malo = (df_portfolio_basurero["total"].cumsum() + capital_sugerido_malo).pct_change().dropna()
df_performance_malo = pd.DataFrame.from_dict({
"PyG": df_portfolio_basurero["total"].cumsum().values[-1],
"Sesiones Analizadas": len(df_portfolio_basurero),
"Sesiones Ganadoras (%)": 100 * len(df_portfolio_basurero[df_portfolio_basurero["total"]>0]) / len(df_portfolio_basurero[df_portfolio_basurero["total"]!=0]),
"ROI Anualizado": 100 * ep.annual_return(df_returns_malo),
"CAGR" : 100 * ep.cagr(df_returns_malo),
"Volatilidad Anualizada": 100 * ep.annual_volatility(df_returns_malo),
"Profit Factor": df_returns_malo[df_returns_malo > 0].sum() / abs(df_returns_malo[df_returns_malo < 0].sum()),
"Promedio Sesion Ganadora": df_portfolio_basurero[df_portfolio_basurero["total"] > 0]["total"].mean(),
"Promedio Sesion Perdedora": df_portfolio_basurero[df_portfolio_basurero["total"] < 0]["total"].mean(),
"Ratio Sharpe": ep.sharpe_ratio(df_returns_malo),
"Ratio Sortino": ep.sortino_ratio(df_returns_malo),
"Ratio Calmar" : ep.calmar_ratio(df_returns_malo),
"Tail Ratio" : ep.tail_ratio(df_returns_malo),
"Omega Ratio" : ep.omega_ratio(df_returns_malo),
"Peor Sesion": peor_sesion_malo,
"Peor Drawdown": peor_drawdown_malo,
"Min Bankroll NoBust": capital_sugerido_malo,
"VAR95%" : ep.value_at_risk(df_returns_malo,0.95),
},"index")
df_performance_malo.columns = ["El Basurero"]
df_performance_malo
# Graficamos el portfolio
fig = plt.figure(figsize=(22,9))
plt.plot(df_portfolio_basurero.drop("total", axis=1).cumsum())
plt.plot(df_portfolio_basurero["total"].cumsum(), linewidth=3);
leg = plt.legend(df_portfolio_basurero.columns.values)
plt.show()

Hemos llegado a la miga, los ratios del portfolio basura, me podria poner a hacer daño gratuitamente , pero solo voy a comentar un poco y por encima los ratios mas llamativos.
Para empezar ganan solo el 50% de las veces, lo que yo llamo, pure luck o random walk, ademas cuando ganan es menos, que cuando pierden, estamos ante un claro ejemplo de un sistema perdedor, unas ventajas rotas, unos numeros de mierda, y UNA ESTAFA EN TODA REGLA
Un producto asi, no se lo desearia ni a mis peores enemigos, el coste de oportunidad de estar invertido en estos darwins, es altisimo, ademas sin contar que darwinex tiene un freno de mano, que hara que no se hundan en la miseria, por que , seguro que la estrategia subyacente, funciona peor que el liquidativo del darwin.
Conclusion, ¿Que haces aqui invertido? ¿Acaso te gusta perder dinero?
Si deseas ver mas articulos de portfolios te recomendamos este y este