Exemple #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()
Exemple #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()
Exemple #3
0
def _get_time_series_list(timestamps, names, values, properties):
    ts_list = []
    for name in names:
        line_type, color = None, None
        if properties is not None and name in properties:
            if 'line_type' in properties[name]: line_type = properties[name]['line_type']
            if 'color' in properties[name]: color = properties[name]['color']
        y = getattr(values, name)
        if not len(y): continue
        if y.dtype.type in [np.str_, np.object_, np.datetime64]: continue
        ts = TimeSeries(name, timestamps, y, line_type = line_type, color = color)
        ts_list.append(ts)
    return ts_list
Exemple #4
0
def _get_time_series_list(timestamps: np.ndarray, 
                          names: Sequence[str], 
                          values: SimpleNamespace, 
                          properties: Optional[PlotPropertiesType]) -> List[TimeSeries]:
    '''
    For plotting, create a list of TimeSeries objects from the arrays in the values object
    '''
    ts_list = []
    for name in names:
        line_type: Optional[str] = None
        color: Optional[str] = None
        if properties is not None and name in properties:
            if 'line_type' in properties[name]: line_type = properties[name]['line_type']
            if 'color' in properties[name]: color = properties[name]['color']
        y = getattr(values, name)
        if not len(y): continue
        if y.dtype.type in [np.str_, np.object_, np.datetime64]: continue
        attrib = LinePlotAttributes(line_type=line_type, color=color)
        ts = TimeSeries(name, timestamps, y, display_attributes=attrib)
        ts_list.append(ts)
    return ts_list
Exemple #5
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()
Exemple #6
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()