Example #1
0
def plot_return_metrics(
        metrics: Mapping[str, Any],
        title: str = None) -> Optional[Tuple[mpl_fig.Figure, mpl.axes.Axes]]:
    '''
    Plot equity, rolling drawdowns and and a boxplot of annual returns given the output of compute_return_metrics.
    '''
    timestamps = metrics['timestamps']
    equity = metrics['equity']
    equity = TimeSeries('equity', timestamps=timestamps, values=equity)
    mdd_date, mdd_start = metrics['mdd_start'], metrics['mdd_date']
    mdd_date_3yr, mdd_start_3yr = metrics['mdd_start_3yr'], metrics[
        'mdd_date_3yr']
    drawdown_lines = [
        DateLine(name='max dd', date=mdd_start, color='red'),
        DateLine(date=mdd_date, color='red'),
        DateLine(name='3y dd', date=mdd_start_3yr, color='orange'),
        DateLine(date=mdd_date_3yr, color='orange')
    ]
    equity_subplot = Subplot(equity,
                             ylabel='Equity',
                             height_ratio=0.6,
                             log_y=True,
                             y_tick_format='${x:,.0f}',
                             date_lines=drawdown_lines,
                             horizontal_lines=[
                                 HorizontalLine(metrics['starting_equity'],
                                                color='black')
                             ])

    rolling_dd = TimeSeries('drawdowns',
                            timestamps=metrics['rolling_dd'][0],
                            values=metrics['rolling_dd'][1])
    zero_line = HorizontalLine(y=0, color='black')
    dd_subplot = Subplot(rolling_dd,
                         ylabel='Drawdowns',
                         height_ratio=0.2,
                         date_lines=drawdown_lines,
                         horizontal_lines=[zero_line])

    years = metrics['bucketed_returns'][0]
    ann_rets = metrics['bucketed_returns'][1]
    ann_ret = BucketedValues('annual returns',
                             bucket_names=years,
                             bucket_values=ann_rets)
    ann_ret_subplot = Subplot(ann_ret,
                              ylabel='Annual Returns',
                              height_ratio=0.2,
                              horizontal_lines=[zero_line])

    plt = Plot([equity_subplot, dd_subplot, ann_ret_subplot], title=title)
    return plt.draw()
Example #2
0
 def plot(self,
          figsize: Tuple[int, int] = (15, 8),
          date_range: Optional[Union[Tuple[str, str],
                                     Tuple[np.datetime64,
                                           np.datetime64]]] = None,
          sampling_frequency: str = None,
          title: str = 'Price / Volume') -> None:
     '''
     Plot a candlestick or line plot depending on whether we have ohlc data or just close prices
     
     Args:
         figsize: Size of the figure (default (15,8))
         date_range: A tuple of strings or numpy datetimes for plotting a smaller sample of the data, e.g. ("2018-01-01", "2018-01-06")
         sampling_frequency: Downsample before plotting.  See pandas frequency strings for possible values.
         title: Title of the graph, default "Price / Volume"
     '''
     if date_range and isinstance(date_range[0], str):
         date_range = strtup2date(date_range)
     data: Union[TradeBarSeries, TimeSeries]
     if self.has_ohlc():
         data = TradeBarSeries('price', self.timestamps, self.o, self.h,
                               self.l, self.c, self.v, self.vwap)
     else:
         data = TimeSeries('price', self.timestamps, self.c)
     subplot = Subplot(data)
     plot = Plot([subplot],
                 figsize=figsize,
                 date_range=date_range,
                 sampling_frequency=sampling_frequency,
                 title=title)
     plot.draw()
Example #3
0
    def plot_2d(self, 
                x: str, 
                y: str = 'all',
                plot_type: str = 'line', 
                figsize: Tuple[float, float] = (15, 8),
                marker: str = 'X', 
                marker_size: int = 50, 
                marker_color: str = 'r', 
                xlim: Tuple[float, float] = None, 
                hspace: float = None) -> None:
        """Creates a 2D plot of the optimization output for plotting 1 parameter and costs.
        
        Args:
            x: Name of the parameter to plot on the x axis, corresponding to the same name in the generator.
            y: Can be one of:
              "cost" 
              The name of another cost variable corresponding to the output from the cost function
              "all", which creates a subplot for cost plus all other costs
            plot_type: line or scatter (default line)
            figsize: Figure size
            marker: Adds a marker to each point in x, y to show the actual data used for interpolation.  You can set this to None to turn markers off.
            hspace: Vertical space between subplots
         """
        if len(self.experiments) == 0: return
        if not has_display(): return

        # Get rid of nans since matplotlib does not like them
        experiments = [experiment for experiment in self.experiments if experiment.valid()]
        if xlim:
            experiments = [experiment for experiment in experiments if experiment.suggestion[x] >= xlim[0] and experiment.suggestion[x] <= xlim[1]]

        xvalues = [experiment.suggestion[x] for experiment in experiments]
        yvalues = []

        if y == 'all':
            yvalues.append(('cost', np.array([experiment.cost for experiment in experiments])))
            other_cost_keys = experiments[0].other_costs.keys()
            for key in other_cost_keys:
                yvalues.append((key, np.array([experiment.other_costs[key] for experiment in experiments])))
        elif y == 'cost':
            yvalues.append(('cost', np.array([experiment.cost for experiment in experiments])))
        else:
            yvalues.append((y, np.array([experiment.other_costs[y] for experiment in experiments])))
            
        xarray = np.array(xvalues)
        x_sort_indices = np.argsort(xarray)
        xarray = xarray[x_sort_indices]
        subplots = []
        for tup in yvalues:
            name = tup[0]
            yarray = tup[1]
            yarray = yarray[x_sort_indices]
            
            disp = LinePlotAttributes(marker=marker, marker_size=marker_size, marker_color=marker_color)
            
            subplots.append(
                Subplot(data_list=[XYData(name, xarray, yarray, display_attributes=disp)], xlabel=x, ylabel=name, xlim=xlim))
            
        plot = Plot(subplots, figsize=figsize, title='Optimizer 1D Test')
        plot.draw()
Example #4
0
    def plot(self, 
             contract_groups: Sequence[ContractGroup] = None, 
             primary_indicators: Sequence[str] = None,
             primary_indicators_dual_axis: Sequence[str] = None,
             secondary_indicators: Sequence[str] = None,
             secondary_indicators_dual_axis: Sequence[str] = None,
             indicator_properties: PlotPropertiesType = None,
             signals: Sequence[str] = None,
             signal_properties: PlotPropertiesType = None, 
             pnl_columns: Sequence[str] = None, 
             title: str = None, 
             figsize: Tuple[int, int] = (20, 15), 
             _date_range: DateRangeType = None, 
             date_format: str = None, 
             sampling_frequency: str = None, 
             trade_marker_properties: PlotPropertiesType = None, 
             hspace: float = 0.15) -> None:
        
        '''
        Plot indicators, signals, trades, position, pnl
        
        Args:
            contract_groups: Contract groups to plot or None (default) for all contract groups. 
            primary indicators: List of indicators to plot in the main indicator section. Default None (plot everything)
            primary indicators: List of indicators to plot in the secondary indicator section. Default None (don't plot anything)
            indicator_properties: If set, we use the line color, line type indicated for the given indicators
            signals: Signals to plot.  Default None (plot everything).
            plot_equity: If set, we plot the equity curve.  Default is True
            title: Title of plot. Default None
            figsize: Figure size.  Default (20, 15)
            date_range: Used to restrict the date range of the graph. Default None
            date_format: Date format for tick labels on x axis.  If set to None (default), will be selected based on date range. 
                See matplotlib date format strings
            sampling_frequency: Downsampling frequency.  The graph may get too busy if you have too many bars of data, 
                so you may want to downsample before plotting.  See pandas frequency strings for possible values. Default None.
            trade_marker_properties: A dictionary of order reason code -> marker shape, marker size, marker color for plotting trades
                with different reason codes. By default we use the dictionary from the :obj:`ReasonCode` class
            hspace: Height (vertical) space between subplots.  Default is 0.15
        '''
        date_range = strtup2date(_date_range)
        if contract_groups is None: contract_groups = self.contract_groups
        if isinstance(contract_groups, ContractGroup): contract_groups = [contract_groups]
        if pnl_columns is None: pnl_columns = ['equity']
        
        for contract_group in contract_groups:
            primary_indicator_names = [ind_name for ind_name in self.indicator_values[contract_group].__dict__
                                       if hasattr(self.indicator_values[contract_group], ind_name)]
            if primary_indicators:
                primary_indicator_names = list(set(primary_indicator_names).intersection(primary_indicators))
            secondary_indicator_names: List[str] = []
            if secondary_indicators:
                secondary_indicator_names = list(secondary_indicators)
            signal_names = [sig_name for sig_name in self.signals.keys() if hasattr(self.signal_values[contract_group], sig_name)]
            if signals:
                signal_names = list(set(signal_names).intersection(signals))
 
            primary_indicator_list = _get_time_series_list(self.timestamps, primary_indicator_names, 
                                                           self.indicator_values[contract_group], indicator_properties)
            secondary_indicator_list = _get_time_series_list(self.timestamps, secondary_indicator_names, 
                                                             self.indicator_values[contract_group], indicator_properties)
            signal_list = _get_time_series_list(self.timestamps, signal_names, self.signal_values[contract_group], signal_properties)
            df_pnl_ = self.df_pnl(contract_group)
            pnl_list = [TimeSeries(pnl_column, 
                                   timestamps=df_pnl_.timestamp.values, 
                                   values=df_pnl_[pnl_column].values) for pnl_column in pnl_columns]
            
            trades = [trade for trade in self._trades if trade.order.contract.contract_group == contract_group]
            if trade_marker_properties:
                trade_sets = trade_sets_by_reason_code(trades, trade_marker_properties, remove_missing_properties=True)
            else:
                trade_sets = trade_sets_by_reason_code(trades)
                
            primary_indicator_subplot = Subplot(
                primary_indicator_list + trade_sets,  # type: ignore # mypy does not allow adding heterogeneous lists
                secondary_y=primary_indicators_dual_axis,
                height_ratio=0.5, 
                ylabel='Primary Indicators')
 
            if len(secondary_indicator_list):
                secondary_indicator_subplot = Subplot(secondary_indicator_list, 
                                                      secondary_y=secondary_indicators_dual_axis,
                                                      height_ratio=0.5, 
                                                      ylabel='Secondary Indicators')
            signal_subplot = Subplot(signal_list, ylabel='Signals', height_ratio=0.167)
            pnl_subplot = Subplot(pnl_list, ylabel='Equity', height_ratio=0.167, log_y=True, y_tick_format='${x:,.0f}')
            position = df_pnl_.position.values
            disp_attribs = FilledLinePlotAttributes()
            pos_subplot = Subplot(
                [TimeSeries('position', timestamps=df_pnl_.timestamp, values=position, display_attributes=disp_attribs)], 
                ylabel='Position', height_ratio=0.167)
            
            title_full = title
            if len(contract_groups) > 1:
                if title is None: title = ''
                title_full = f'{title} {contract_group.name}'
                
            plot_list = []
            if len(primary_indicator_list): plot_list.append(primary_indicator_subplot)
            if len(secondary_indicator_list): plot_list.append(secondary_indicator_subplot)
            if len(signal_list): plot_list.append(signal_subplot)
            if len(position): plot_list.append(pos_subplot)
            if len(pnl_list): plot_list.append(pnl_subplot)
            
            if not len(plot_list): return
                
            plot = Plot(plot_list, 
                        figsize=figsize, 
                        date_range=date_range, 
                        date_format=date_format, 
                        sampling_frequency=sampling_frequency, 
                        title=title_full, 
                        hspace=hspace)
            plot.draw()
Example #5
0
    def plot(self,
             contract_groups=None,
             primary_indicators=None,
             primary_indicators_dual_axis=None,
             secondary_indicators=None,
             secondary_indicators_dual_axis=None,
             indicator_properties=None,
             signals=None,
             signal_properties=None,
             pnl_columns=None,
             title=None,
             figsize=(20, 15),
             date_range=None,
             date_format=None,
             sampling_frequency=None,
             trade_marker_properties=None,
             hspace=0.15):
        '''Plot indicators, signals, trades, position, pnl
        
        Args:
            contract_groups (list of :obj:`ContractGroup`, optional): Contract groups to plot or None (default) for all 
                contract groups. 
            primary indicators (list of str, optional): List of indicators to plot in the main indicator section. 
                Default None (plot everything)
            primary indicators (list of str, optional): List of indicators to plot in the secondary indicator section. 
                Default None (don't plot anything)
            indicator_properties (dict of str : dict, optional): If set, we use the line color, line type indicated 
                for the given indicators
            signals (list of str, optional): Signals to plot.  Default None (plot everything).
            plot_equity (bool, optional): If set, we plot the equity curve.  Default is True
            title (list of str, optional): Title of plot. Default None
            figsize (tuple of int): Figure size.  Default (20, 15)
            date_range (tuple of str or np.datetime64, optional): Used to restrict the date range of the graph.
                Default None
            date_format (str, optional): Date format for tick labels on x axis.  If set to None (default), 
                will be selected based on date range. See matplotlib date format strings
            sampling_frequency (str, optional): Downsampling frequency.  The graph may get too busy if you have too many bars
                of data, in which case you may want to downsample before plotting.  See pandas frequency strings for 
                possible values. Default None.
            trade_marker_properties (dict of str : tuple, optional): A dictionary of 
                order reason code -> marker shape, marker size, marker color for plotting trades with different reason codes.
                Default is None in which case the dictionary from the ReasonCode class is used
            hspace (float, optional): Height (vertical) space between subplots.  Default is 0.15
        '''
        date_range = strtup2date(date_range)
        if contract_groups is None: contract_groups = self.contract_groups
        if isinstance(contract_groups, ContractGroup):
            contract_groups = [contract_groups]
        if pnl_columns is None: pnl_columns = ['equity']

        for contract_group in contract_groups:
            primary_indicator_names = [ind_name for ind_name in self.indicator_values[contract_group].__dict__ \
                                       if hasattr(self.indicator_values[contract_group], ind_name)]
            if primary_indicators:
                primary_indicator_names = list(
                    set(primary_indicator_names).intersection(
                        primary_indicators))
            secondary_indicator_names = []
            if secondary_indicators:
                secondary_indicator_names = secondary_indicators
            signal_names = [
                sig_name for sig_name in self.signals.keys()
                if hasattr(self.signal_values[contract_group], sig_name)
            ]
            if signals:
                signal_names = list(set(signal_names).intersection(signals))

            primary_indicator_list = _get_time_series_list(
                self.timestamps, primary_indicator_names,
                self.indicator_values[contract_group], indicator_properties)
            secondary_indicator_list = _get_time_series_list(
                self.timestamps, secondary_indicator_names,
                self.indicator_values[contract_group], indicator_properties)
            signal_list = _get_time_series_list(
                self.timestamps, signal_names,
                self.signal_values[contract_group], signal_properties)
            df_pnl_ = self.df_pnl(contract_group)
            pnl_list = [
                TimeSeries(pnl_column,
                           timestamps=df_pnl_.timestamp.values,
                           values=df_pnl_[pnl_column].values)
                for pnl_column in pnl_columns
            ]

            trades = [
                trade for trade in self._trades
                if trade.order.contract.contract_group == contract_group
            ]
            if trade_marker_properties:
                trade_sets = trade_sets_by_reason_code(
                    trades,
                    trade_marker_properties,
                    remove_missing_properties=True)
            else:
                trade_sets = trade_sets_by_reason_code(trades)

            primary_indicator_subplot = Subplot(
                primary_indicator_list + trade_sets,
                secondary_y=primary_indicators_dual_axis,
                height_ratio=0.5,
                ylabel='Primary Indicators')

            if len(secondary_indicator_list):
                secondary_indicator_subplot = Subplot(
                    secondary_indicator_list,
                    secondary_y=secondary_indicators_dual_axis,
                    height_ratio=0.5,
                    ylabel='Secondary Indicators')
            signal_subplot = Subplot(signal_list,
                                     ylabel='Signals',
                                     height_ratio=0.167)
            pnl_subplot = Subplot(pnl_list,
                                  ylabel='Equity',
                                  height_ratio=0.167,
                                  log_y=True,
                                  y_tick_format='${x:,.0f}')
            position = df_pnl_.position.values
            pos_subplot = Subplot([
                TimeSeries('position',
                           timestamps=df_pnl_.timestamp,
                           values=position,
                           plot_type='filled_line')
            ],
                                  ylabel='Position',
                                  height_ratio=0.167)

            title_full = title
            if len(contract_groups) > 1:
                if title is None: title = ''
                title_full = f'{title} {contract_group.name}'

            plot_list = []
            if len(primary_indicator_list):
                plot_list.append(primary_indicator_subplot)
            if len(secondary_indicator_list):
                plot_list.append(secondary_indicator_subplot)
            if len(signal_list): plot_list.append(signal_subplot)
            if len(position): plot_list.append(pos_subplot)
            if len(pnl_list): plot_list.append(pnl_subplot)

            if not len(plot_list): return

            plot = Plot(plot_list,
                        figsize=figsize,
                        date_range=date_range,
                        date_format=date_format,
                        sampling_frequency=sampling_frequency,
                        title=title_full,
                        hspace=hspace)
            plot.draw()
Example #6
0
    def plot_3d(self,
                x: str,
                y: str,
                z: str = 'all',
                plot_type: str = 'surface',
                figsize: Tuple[float, float] = (15, 15),
                interpolation: str = 'linear',
                cmap: str = 'viridis',
                marker: str = 'X',
                marker_size: int = 50,
                marker_color: str = 'r',
                xlim: Tuple[float, float] = None,
                ylim: Tuple[float, float] = None,
                hspace: float = None) -> None:
        """Creates a 3D plot of the optimization output for plotting 2 parameters and costs.
        
        Args:
            x: Name of the parameter to plot on the x axis, corresponding to the same name in the generator.
            y: Name of the parameter to plot on the y axis, corresponding to the same name in the generator.
            z: Can be one of:
              "cost" 
              The name of another cost variable corresponding to the output from the cost function
              "all", which creates a subplot for cost plus all other costs
            plot_type: surface or contour (default surface)
            figsize: Figure size
            interpolation: Can be ‘linear’, ‘nearest’ or ‘cubic’ for plotting z points between the ones passed in.  See scipy.interpolate.griddata for details
            cmap: Colormap to use (default viridis).  See matplotlib colormap for details
            marker: Adds a marker to each point in x, y, z to show the actual data used for interpolation.  You can set this to None to turn markers off.
            hspace: Vertical space between subplots
         """

        if len(self.experiments) == 0:
            print('No experiments found')
            return
        if not has_display(): return

        # Get rid of nans since matplotlib does not like them
        experiments = [
            experiment for experiment in self.experiments
            if experiment.valid()
        ]
        if not len(experiments):
            print('No valid experiments found')
            return

        if xlim:
            experiments = [
                experiment for experiment in experiments
                if experiment.suggestion[x] >= xlim[0]
                and experiment.suggestion[x] <= xlim[1]
            ]
        if ylim:
            experiments = [
                experiment for experiment in experiments
                if experiment.suggestion[y] >= ylim[0]
                and experiment.suggestion[y] <= ylim[1]
            ]

        xvalues = [experiment.suggestion[x] for experiment in experiments]
        yvalues = [experiment.suggestion[y] for experiment in experiments]
        zvalues = []

        if z == 'all':
            zvalues.append(
                ('cost',
                 np.array([experiment.cost for experiment in experiments])))
            if len(experiments[0].other_costs):
                other_cost_keys = experiments[0].other_costs.keys()
                for key in other_cost_keys:
                    zvalues.append((key,
                                    np.array([
                                        experiment.other_costs[key]
                                        for experiment in experiments
                                    ])))
        elif z == 'cost':
            zvalues.append(
                ('cost',
                 np.array([experiment.cost for experiment in experiments])))
        else:
            zvalues.append(
                (z,
                 np.array([
                     experiment.other_costs[z] for experiment in experiments
                 ])))

        title: Optional[str]

        subplots = []
        for tup in zvalues:
            name = tup[0]
            zarray = tup[1]
            if plot_type == 'contour':
                zlabel = None
                title = name
            else:
                zlabel = name
                title = None

            disp = SurfacePlotAttributes(marker=marker,
                                         marker_size=marker_size,
                                         marker_color=marker_color,
                                         interpolation=interpolation)

            subplots.append(
                Subplot(data_list=[
                    XYZData(name,
                            xvalues,
                            yvalues,
                            zarray,
                            display_attributes=disp)
                ],
                        title=title,
                        xlabel=x,
                        ylabel=y,
                        zlabel=zlabel,
                        xlim=xlim,
                        ylim=ylim))
        plot = Plot(subplots,
                    figsize=figsize,
                    title='Optimizer 2D Test',
                    hspace=hspace)
        plot.draw()