app.backbone.utils.montecarlo_utils
1import numpy as np 2import pandas as pd 3 4def max_drawdown(equity_curve): 5 running_max = np.maximum.accumulate(equity_curve) 6 drawdown = (equity_curve - running_max) / running_max 7 return np.min(drawdown) * 100 # Convertir el drawdown a porcentaje 8 9 10def montecarlo_statistics_simulation( 11 trade_history, 12 equity_curve, 13 n_simulations, 14 initial_equity, 15 threshold_ruin=0.85, 16 return_raw_curves=False, 17 percentiles=None, 18): 19 20 # Renombro las columnas 21 22 trade_history = trade_history.rename(columns={"ExitTime": "Date"}) 23 trade_history = trade_history[["Date", "NetPnL"]] 24 25 equity_curve = ( 26 equity_curve.reset_index() 27 .rename(columns={"index": "Date"})[["Date", "Equity"]] 28 .sort_values(by="Date") 29 ) 30 31 trade_history["Date"] = pd.to_datetime(trade_history["Date"]) 32 equity_curve["Date"] = pd.to_datetime(equity_curve["Date"]) 33 34 # joineo los dfs por fechas 35 36 full_df = pd.merge(equity_curve, trade_history, on="Date", how="left") 37 38 full_df = full_df[~full_df["NetPnL"].isna()] 39 40 # Porcentaje de ganancia 41 42 full_df["pct"] = full_df["NetPnL"] / full_df["Equity"].shift(1) 43 44 # Parámetros iniciales 45 46 n_steps = len(trade_history) 47 mean_return = full_df["pct"].mean() 48 std_return = full_df["pct"].std() 49 50 drawdowns_pct = [] # Lista para almacenar los drawdowns en porcentaje 51 final_returns_pct = [] # Lista para almacenar los retornos finales en porcentaje 52 ruin_count = 0 # Contador de simulaciones que alcanzan la ruina 53 ruin_threshold = ( 54 initial_equity * threshold_ruin 55 ) # Umbral de ruina en términos de equidad 56 57 # Simulaciones de Montecarlo 58 59 for _ in range(n_simulations): 60 # Generar retornos aleatorios con media y desviación estándar de los históricos 61 62 random_returns = mean_return + std_return * np.random.standard_t(15, size=n_steps) 63 64 # Calcular la curva de equidad acumulada 65 66 synthetic_equity_curve = initial_equity * np.cumprod(1 + random_returns) 67 68 # Calcular drawdown y almacenarlo en porcentaje 69 70 dd_pct = max_drawdown(synthetic_equity_curve) 71 drawdowns_pct.append(dd_pct) 72 73 # Calcular el retorno acumulado porcentual y almacenarlo 74 75 final_return_pct = ( 76 synthetic_equity_curve[-1] / initial_equity - 1 77 ) * 100 # Retorno final en porcentaje 78 final_returns_pct.append(final_return_pct) 79 80 # Verificar si la equidad cae por debajo del umbral de ruina en algún punto 81 82 if np.any(synthetic_equity_curve <= ruin_threshold): 83 ruin_count += 1 84 # Crear un DataFrame separado para los drawdowns y los retornos acumulados en porcentaje 85 86 df_drawdowns = pd.DataFrame({"Drawdown (%)": drawdowns_pct}) 87 df_final_returns_pct = pd.DataFrame({"Final Return (%)": final_returns_pct}) 88 89 # Calcular las estadísticas usando df.describe() para cada DataFrame 90 91 if not percentiles: 92 drawdown_stats = df_drawdowns.describe() 93 return_stats = df_final_returns_pct.describe() 94 else: 95 drawdown_stats = df_drawdowns.describe(percentiles=percentiles) 96 return_stats = df_final_returns_pct.describe(percentiles=percentiles) 97 # Calcular el riesgo de ruina 98 99 risk_of_ruin = ruin_count / n_simulations 100 101 # Agregar el riesgo de ruina a las estadísticas de drawdown 102 103 drawdown_stats.loc["Risk of Ruin"] = risk_of_ruin 104 105 # Combinar las métricas de drawdowns y retornos porcentuales 106 107 combined_stats = pd.concat([drawdown_stats, return_stats], axis=1) 108 if return_raw_curves: 109 return combined_stats, df_drawdowns, df_final_returns_pct 110 111 return combined_stats 112 113def monte_carlo_simulation_v2( 114 equity_curve, 115 trade_history, 116 n_simulations, 117 initial_equity, 118 threshold_ruin, 119 return_raw_curves, 120 percentiles=[0.1, 0.25, 0.5, 0.75, 0.9] 121): 122 """ 123 Simulación de Monte Carlo para un sistema de trading con distribución basada en probabilidades de trades. 124 125 Args: 126 df (pd.DataFrame): DataFrame con columnas 'NetPnL' (Profit and Loss) y 'Type' ('long' o 'short'). 127 equity_start (float): Valor inicial del equity. 128 num_simulations (int): Número de simulaciones a realizar. 129 threshold (float): Umbral para calcular el riesgo de ruina. 130 131 Returns: 132 dict: Resultados estadísticos de las simulaciones, incluyendo drawdowns y retornos. 133 """ 134 # Filtrar trades por tipo y resultados 135 trade_history.ReturnPct = trade_history.ReturnPct / 100 136 137 long_trades = trade_history[trade_history['Size'] > 0] 138 short_trades = trade_history[trade_history['Size'] < 0] 139 140 long_winning_trades = long_trades[long_trades['NetPnL'] > 0] 141 short_winning_trades = short_trades[short_trades['NetPnL'] > 0] 142 long_losing_trades = long_trades[long_trades['NetPnL'] <= 0] 143 short_losing_trades = short_trades[short_trades['NetPnL'] <= 0] 144 145 # Calcular estadísticas para trades 146 prob_trade = len(trade_history) / len(equity_curve) # Probabilidad de realizar un trade 147 prob_long = len(long_trades) / len(trade_history) if len(trade_history) > 0 else 0 148 prob_short = len(short_trades) / len(trade_history) if len(trade_history) > 0 else 0 149 prob_long_winner = len(long_winning_trades) / len(long_trades) if len(long_trades) > 0 else 0 150 prob_short_winner = len(short_winning_trades) / len(short_trades) if len(short_trades) > 0 else 0 151 152 long_win_mean, long_win_std = long_winning_trades['ReturnPct'].mean(), long_winning_trades['ReturnPct'].std() 153 long_loss_mean, long_loss_std = long_losing_trades['ReturnPct'].mean(), long_losing_trades['ReturnPct'].std() 154 short_win_mean, short_win_std = short_winning_trades['ReturnPct'].mean(), short_winning_trades['ReturnPct'].std() 155 short_loss_mean, short_loss_std = short_losing_trades['ReturnPct'].mean(), short_losing_trades['ReturnPct'].std() 156 157 # Inicializar resultados 158 equity_curves = [] 159 drawdowns = [] 160 returns = [] 161 162 ruin_count = 0 163 for _ in range(n_simulations): 164 equity = [initial_equity] # Curva de equity inicial 165 166 for _ in range(len(equity_curve)): 167 # Decidir si se realiza un trade 168 if np.random.rand() < prob_trade: 169 # Decidir si es long o short 170 if np.random.rand() < prob_long: 171 # Decidir si el long es ganador o perdedor 172 if np.random.rand() < prob_long_winner: 173 trade = np.random.normal(long_win_mean, long_win_std) 174 else: 175 trade = np.random.normal(long_loss_mean, long_loss_std) 176 else: 177 # Decidir si el short es ganador o perdedor 178 if np.random.rand() < prob_short_winner: 179 trade = np.random.normal(short_win_mean, short_win_std) 180 else: 181 trade = np.random.normal(short_loss_mean, short_loss_std) 182 else: 183 trade = 0 # No se realiza trade 184 185 # Actualizar la curva de equity 186 equity.append(equity[-1] + equity[-1] * trade) 187 188 # Calcular drawdown 189 peak = np.maximum.accumulate(equity) 190 dd = (equity - peak) / peak * 100 # Drawdown en porcentaje 191 192 # Calcular retorno final 193 ret = ((equity[-1] - initial_equity) / initial_equity) * 100 # Retorno en porcentaje 194 195 if np.any(np.array(equity) <= initial_equity * threshold_ruin): 196 ruin_count += 1 197 198 # Guardar resultados 199 equity_curves.append(equity) 200 drawdowns.append(dd.min()) # Máximo drawdown 201 returns.append(ret) 202 203 df_drawdowns = pd.DataFrame({"Drawdown (%)": drawdowns}) 204 df_final_returns_pct = pd.DataFrame({"Final Return (%)": returns}) 205 206 # Calcular las estadísticas usando df.describe() para cada DataFrame 207 208 if not percentiles: 209 drawdown_stats = df_drawdowns.describe() 210 return_stats = df_final_returns_pct.describe() 211 else: 212 drawdown_stats = df_drawdowns.describe(percentiles=percentiles) 213 return_stats = df_final_returns_pct.describe(percentiles=percentiles) 214 215 # Calcular el riesgo de ruina 216 217 risk_of_ruin = (ruin_count / n_simulations) * 100 218 drawdown_stats.loc["Risk of Ruin"] = risk_of_ruin 219 220 # Combinar las métricas de drawdowns y retornos porcentuales 221 222 combined_stats = pd.concat([drawdown_stats, return_stats], axis=1) 223 if return_raw_curves: 224 return combined_stats, df_drawdowns, df_final_returns_pct 225 226 return combined_stats
def
max_drawdown(equity_curve):
def
montecarlo_statistics_simulation( trade_history, equity_curve, n_simulations, initial_equity, threshold_ruin=0.85, return_raw_curves=False, percentiles=None):
11def montecarlo_statistics_simulation( 12 trade_history, 13 equity_curve, 14 n_simulations, 15 initial_equity, 16 threshold_ruin=0.85, 17 return_raw_curves=False, 18 percentiles=None, 19): 20 21 # Renombro las columnas 22 23 trade_history = trade_history.rename(columns={"ExitTime": "Date"}) 24 trade_history = trade_history[["Date", "NetPnL"]] 25 26 equity_curve = ( 27 equity_curve.reset_index() 28 .rename(columns={"index": "Date"})[["Date", "Equity"]] 29 .sort_values(by="Date") 30 ) 31 32 trade_history["Date"] = pd.to_datetime(trade_history["Date"]) 33 equity_curve["Date"] = pd.to_datetime(equity_curve["Date"]) 34 35 # joineo los dfs por fechas 36 37 full_df = pd.merge(equity_curve, trade_history, on="Date", how="left") 38 39 full_df = full_df[~full_df["NetPnL"].isna()] 40 41 # Porcentaje de ganancia 42 43 full_df["pct"] = full_df["NetPnL"] / full_df["Equity"].shift(1) 44 45 # Parámetros iniciales 46 47 n_steps = len(trade_history) 48 mean_return = full_df["pct"].mean() 49 std_return = full_df["pct"].std() 50 51 drawdowns_pct = [] # Lista para almacenar los drawdowns en porcentaje 52 final_returns_pct = [] # Lista para almacenar los retornos finales en porcentaje 53 ruin_count = 0 # Contador de simulaciones que alcanzan la ruina 54 ruin_threshold = ( 55 initial_equity * threshold_ruin 56 ) # Umbral de ruina en términos de equidad 57 58 # Simulaciones de Montecarlo 59 60 for _ in range(n_simulations): 61 # Generar retornos aleatorios con media y desviación estándar de los históricos 62 63 random_returns = mean_return + std_return * np.random.standard_t(15, size=n_steps) 64 65 # Calcular la curva de equidad acumulada 66 67 synthetic_equity_curve = initial_equity * np.cumprod(1 + random_returns) 68 69 # Calcular drawdown y almacenarlo en porcentaje 70 71 dd_pct = max_drawdown(synthetic_equity_curve) 72 drawdowns_pct.append(dd_pct) 73 74 # Calcular el retorno acumulado porcentual y almacenarlo 75 76 final_return_pct = ( 77 synthetic_equity_curve[-1] / initial_equity - 1 78 ) * 100 # Retorno final en porcentaje 79 final_returns_pct.append(final_return_pct) 80 81 # Verificar si la equidad cae por debajo del umbral de ruina en algún punto 82 83 if np.any(synthetic_equity_curve <= ruin_threshold): 84 ruin_count += 1 85 # Crear un DataFrame separado para los drawdowns y los retornos acumulados en porcentaje 86 87 df_drawdowns = pd.DataFrame({"Drawdown (%)": drawdowns_pct}) 88 df_final_returns_pct = pd.DataFrame({"Final Return (%)": final_returns_pct}) 89 90 # Calcular las estadísticas usando df.describe() para cada DataFrame 91 92 if not percentiles: 93 drawdown_stats = df_drawdowns.describe() 94 return_stats = df_final_returns_pct.describe() 95 else: 96 drawdown_stats = df_drawdowns.describe(percentiles=percentiles) 97 return_stats = df_final_returns_pct.describe(percentiles=percentiles) 98 # Calcular el riesgo de ruina 99 100 risk_of_ruin = ruin_count / n_simulations 101 102 # Agregar el riesgo de ruina a las estadísticas de drawdown 103 104 drawdown_stats.loc["Risk of Ruin"] = risk_of_ruin 105 106 # Combinar las métricas de drawdowns y retornos porcentuales 107 108 combined_stats = pd.concat([drawdown_stats, return_stats], axis=1) 109 if return_raw_curves: 110 return combined_stats, df_drawdowns, df_final_returns_pct 111 112 return combined_stats
def
monte_carlo_simulation_v2( equity_curve, trade_history, n_simulations, initial_equity, threshold_ruin, return_raw_curves, percentiles=[0.1, 0.25, 0.5, 0.75, 0.9]):
114def monte_carlo_simulation_v2( 115 equity_curve, 116 trade_history, 117 n_simulations, 118 initial_equity, 119 threshold_ruin, 120 return_raw_curves, 121 percentiles=[0.1, 0.25, 0.5, 0.75, 0.9] 122): 123 """ 124 Simulación de Monte Carlo para un sistema de trading con distribución basada en probabilidades de trades. 125 126 Args: 127 df (pd.DataFrame): DataFrame con columnas 'NetPnL' (Profit and Loss) y 'Type' ('long' o 'short'). 128 equity_start (float): Valor inicial del equity. 129 num_simulations (int): Número de simulaciones a realizar. 130 threshold (float): Umbral para calcular el riesgo de ruina. 131 132 Returns: 133 dict: Resultados estadísticos de las simulaciones, incluyendo drawdowns y retornos. 134 """ 135 # Filtrar trades por tipo y resultados 136 trade_history.ReturnPct = trade_history.ReturnPct / 100 137 138 long_trades = trade_history[trade_history['Size'] > 0] 139 short_trades = trade_history[trade_history['Size'] < 0] 140 141 long_winning_trades = long_trades[long_trades['NetPnL'] > 0] 142 short_winning_trades = short_trades[short_trades['NetPnL'] > 0] 143 long_losing_trades = long_trades[long_trades['NetPnL'] <= 0] 144 short_losing_trades = short_trades[short_trades['NetPnL'] <= 0] 145 146 # Calcular estadísticas para trades 147 prob_trade = len(trade_history) / len(equity_curve) # Probabilidad de realizar un trade 148 prob_long = len(long_trades) / len(trade_history) if len(trade_history) > 0 else 0 149 prob_short = len(short_trades) / len(trade_history) if len(trade_history) > 0 else 0 150 prob_long_winner = len(long_winning_trades) / len(long_trades) if len(long_trades) > 0 else 0 151 prob_short_winner = len(short_winning_trades) / len(short_trades) if len(short_trades) > 0 else 0 152 153 long_win_mean, long_win_std = long_winning_trades['ReturnPct'].mean(), long_winning_trades['ReturnPct'].std() 154 long_loss_mean, long_loss_std = long_losing_trades['ReturnPct'].mean(), long_losing_trades['ReturnPct'].std() 155 short_win_mean, short_win_std = short_winning_trades['ReturnPct'].mean(), short_winning_trades['ReturnPct'].std() 156 short_loss_mean, short_loss_std = short_losing_trades['ReturnPct'].mean(), short_losing_trades['ReturnPct'].std() 157 158 # Inicializar resultados 159 equity_curves = [] 160 drawdowns = [] 161 returns = [] 162 163 ruin_count = 0 164 for _ in range(n_simulations): 165 equity = [initial_equity] # Curva de equity inicial 166 167 for _ in range(len(equity_curve)): 168 # Decidir si se realiza un trade 169 if np.random.rand() < prob_trade: 170 # Decidir si es long o short 171 if np.random.rand() < prob_long: 172 # Decidir si el long es ganador o perdedor 173 if np.random.rand() < prob_long_winner: 174 trade = np.random.normal(long_win_mean, long_win_std) 175 else: 176 trade = np.random.normal(long_loss_mean, long_loss_std) 177 else: 178 # Decidir si el short es ganador o perdedor 179 if np.random.rand() < prob_short_winner: 180 trade = np.random.normal(short_win_mean, short_win_std) 181 else: 182 trade = np.random.normal(short_loss_mean, short_loss_std) 183 else: 184 trade = 0 # No se realiza trade 185 186 # Actualizar la curva de equity 187 equity.append(equity[-1] + equity[-1] * trade) 188 189 # Calcular drawdown 190 peak = np.maximum.accumulate(equity) 191 dd = (equity - peak) / peak * 100 # Drawdown en porcentaje 192 193 # Calcular retorno final 194 ret = ((equity[-1] - initial_equity) / initial_equity) * 100 # Retorno en porcentaje 195 196 if np.any(np.array(equity) <= initial_equity * threshold_ruin): 197 ruin_count += 1 198 199 # Guardar resultados 200 equity_curves.append(equity) 201 drawdowns.append(dd.min()) # Máximo drawdown 202 returns.append(ret) 203 204 df_drawdowns = pd.DataFrame({"Drawdown (%)": drawdowns}) 205 df_final_returns_pct = pd.DataFrame({"Final Return (%)": returns}) 206 207 # Calcular las estadísticas usando df.describe() para cada DataFrame 208 209 if not percentiles: 210 drawdown_stats = df_drawdowns.describe() 211 return_stats = df_final_returns_pct.describe() 212 else: 213 drawdown_stats = df_drawdowns.describe(percentiles=percentiles) 214 return_stats = df_final_returns_pct.describe(percentiles=percentiles) 215 216 # Calcular el riesgo de ruina 217 218 risk_of_ruin = (ruin_count / n_simulations) * 100 219 drawdown_stats.loc["Risk of Ruin"] = risk_of_ruin 220 221 # Combinar las métricas de drawdowns y retornos porcentuales 222 223 combined_stats = pd.concat([drawdown_stats, return_stats], axis=1) 224 if return_raw_curves: 225 return combined_stats, df_drawdowns, df_final_returns_pct 226 227 return combined_stats
Simulación de Monte Carlo para un sistema de trading con distribución basada en probabilidades de trades.
Args: df (pd.DataFrame): DataFrame con columnas 'NetPnL' (Profit and Loss) y 'Type' ('long' o 'short'). equity_start (float): Valor inicial del equity. num_simulations (int): Número de simulaciones a realizar. threshold (float): Umbral para calcular el riesgo de ruina.
Returns: dict: Resultados estadísticos de las simulaciones, incluyendo drawdowns y retornos.