def showProgressBar(self, flag=True): if flag and self.progress == None: self.progress = ProgBar() elif self.progress != None and not flag: if self.progress.isActive(): self.progress.halt() self.progress = None
def optimap(f_xy, X_params, Y_params, heat=True, symm=False): Stats = ["Trades", "Mean", "R | Sharpe", "R | Sterling" ] ## Around which statistical metrics are we going to optimize. arrays = (list, tuple, dict, range, numpy.ndarray, pandas.Series ) ## Indexable arrays for "X" & "Y" parameter arguments. assert isinstance(X_params, arrays) and isinstance( Y_params, arrays), "{TYPE} 'X' & 'Y' params must be indexable arrays." X_labels, Y_labels = X_params, Y_params ## "Grid" df's index/column labels may tentatively be the parameters themselves. ## However, if "X"/"Y" params' array is a dict, keys will be column labels in Grid. Values will be arguments for "f_xy". if isinstance(X_params, dict): X_params, X_labels = list(X_params.values()), list(X_params.keys()) if isinstance(Y_params, dict): Y_params, Y_labels = list(Y_params.values()), list(Y_params.keys()) Grid = pandas.DataFrame( index=X_labels, columns=pandas.MultiIndex.from_product(iterables=(Stats, Y_labels))) Combs = list(itertools.product(range(len(X_params)), range( len(Y_params)))) ## Indexes of all possible "X" & "Y" pairs. ## In cases where "X" & "Y" share exact same indicator nature (e.g.: both SMA), omit repeated/swapped cases. Example: if symm: Combs = [(nx, ny) for nx, ny in Combs if (X_params[nx] <= Y_params[ny]) ] ## "(p1, p2) = (p2, p1)". Keep just one. Prog = ProgBar.ProgBar(steps=len( list(Combs))) ## Create a progress bar made with characters. ## For given parameters "x" and "y", we will run the exercise, find its stats and create a 2D grid. for nx, ny in Combs: ## For every combination of parameters. x_param, y_param, x_label, y_label = X_params[nx], Y_params[ ny], X_labels[nx], Y_labels[ny] try: ## Run the exercise with a function as specified. S = f_xy(x_param, y_param).Stats[symbol][ "Return"] ## From the backtest results, keep only the returns' ".Stats". for stat in Stats: Grid.loc[x_label, (stat, y_label)] = S[stat] except: 1 ## When it's impossible to calculate stats (e.g.: no signals/trades), forget about errors. Prog.up() ## Increase progress bar. Figure, Axes = matplotlib.pyplot.subplots(ncols=len(Stats)) ## Heatmaps will display the most optimal spots in red. Grid.replace(to_replace=[-numpy.inf, numpy.inf], value=numpy.nan, inplace=True) for n, stat in enumerate(Stats): ## Infs ⇧ when denominator is 0. lim = max(abs(Grid[stat].min().min()), abs( Grid[stat].max().max())) * 1.25 ## Y-axes' max span for lines. if heat: Axes[n].contourf(*numpy.meshgrid(X_params, Y_params), Grid[stat].values.T) # Heatmap, 2D. else: Grid[stat].plot( ylim=[-lim * (Grid[stat] < 0).any().any(), lim], ## When no negative numbers found... ax=Axes[n], legend=False, linewidth=2.5) ## ...lowest y-axis point can be 0. Axes[n].set_title(stat, fontweight="bold") if not (heat): Axes[0].legend(fontsize=13) ## Add legend just to the first line plot. matplotlib.pyplot.pause( 1e-13) ## This line avoids a (quite loooong) Tkinter warning print. return Figure, Grid