Example #1
0
def _valid_addplot_kwargs():

    valid_linestyles = ('-','solid','--','dashed','-.','dashdot','.','dotted',None,' ','')
    valid_types = ('line','scatter','bar', 'ohlc', 'candle')

    vkwargs = {
        'scatter'     : { 'Default'     : False,
                          'Validator'   : lambda value: isinstance(value,bool) },

        'type'        : { 'Default'     : 'line',
                          'Validator'   : lambda value: value in valid_types },

        'mav'         : { 'Default'     : None,
                          'Validator'   : _mav_validator },
        
        'panel'       : { 'Default'     : 0, 
                          'Validator'   : lambda value: _valid_panel_id(value) },

        'marker'      : { 'Default'     : 'o',
                          'Validator'   : lambda value: _bypass_kwarg_validation(value)  },

        'markersize'  : { 'Default'     : 18,
                          'Validator'   : lambda value: isinstance(value,(int,float)) },

        'color'       : { 'Default'     : None,
                          'Validator'   : lambda value: mcolors.is_color_like(value) or
                                         (isinstance(value,(list,tuple,np.ndarray)) and all([mcolors.is_color_like(v) for v in value])) },

        'linestyle'   : { 'Default'     : None,
                          'Validator'   : lambda value: value in valid_linestyles },

        'width'       : { 'Default'     : None, # width of `bar` or `line` 
                          'Validator'   : lambda value: isinstance(value,(int,float)) or
                                                        all([isinstance(v,(int,float)) for v in value]) },

        'bottom'      : { 'Default'     : 0,  # bottom for `type=bar` plots
                          'Validator'   : lambda value: isinstance(value,(int,float)) or
                                                        all([isinstance(v,(int,float)) for v in value]) },
        'alpha'       : { 'Default'     : 1,  # alpha of `bar`, `line`, or `scatter`
                          'Validator'   : lambda value: isinstance(value,(int,float)) or
                                                        all([isinstance(v,(int,float)) for v in value]) },

        'secondary_y' : { 'Default'     : 'auto',
                          'Validator'   : lambda value: isinstance(value,bool) or value == 'auto' },

        'y_on_right'  : { 'Default'     : None,
                          'Validator'   : lambda value: isinstance(value,bool) },
        
        'ylabel'      : { 'Default'     : None,
                          'Validator'   : lambda value: isinstance(value,str) },

        'ylim'        : {'Default'      : None,
                         'Validator'    : lambda value: isinstance(value, (list,tuple)) and len(value) == 2 
                                                                      and all([isinstance(v,(int,float)) for v in value])},
    }

    _validate_vkwargs_dict(vkwargs)

    return vkwargs
def _valid_plot_kwargs():
    '''
    Construct and return the "valid kwargs table" for the mplfinance.plot() function.
    A valid kwargs table is a `dict` of `dict`s.  The keys of the outer dict are the
    valid key-words for the function.  The value for each key is a dict containing
    2 specific keys: "Default", and "Validator" with the following values:
        "Default"      - The default value for the kwarg if none is specified.
        "Validator"    - A function that takes the caller specified value for the kwarg,
                         and validates that it is the correct type, and (for kwargs with 
                         a limited set of allowed values) may also validate that the
                         kwarg value is one of the allowed values.
    '''

    vkwargs = {
        'columns'                   : { 'Default'     : None, # use default names: ('Open', 'High', 'Low', 'Close', 'Volume')
                                        'Validator'   : lambda value: isinstance(value, (tuple, list))
                                                                   and len(value) == 5
                                                                   and all(isinstance(c, str) for c in value) },
        'type'                      : { 'Default'     : 'ohlc',
                                        'Validator'   : lambda value: value in _get_valid_plot_types() },
 
        'style'                     : { 'Default'     : None,
                                        'Validator'   : _styles._valid_mpf_style },
 
        'volume'                    : { 'Default'     : False,
                                        'Validator'   : lambda value: isinstance(value,bool) or isinstance(value,mpl_axes.Axes) },
 
        'mav'                       : { 'Default'     : None,
                                        'Validator'   : _mav_validator },
        
        'renko_params'              : { 'Default'     : dict(),
                                        'Validator'   : lambda value: isinstance(value,dict) },

        'pnf_params'                : { 'Default'     : dict(),
                                        'Validator'   : lambda value: isinstance(value,dict) },
 
        'study'                     : { 'Default'     : None,
                                        'Validator'   : lambda value: _kwarg_not_implemented(value) }, 
 
        'marketcolors'              : { 'Default'     : None, # use 'style' for default, instead.
                                        'Validator'   : lambda value: isinstance(value,dict) },
 
        'no_xgaps'                  : { 'Default'     : True,  # None means follow default logic below:
                                        'Validator'   : lambda value: _warn_no_xgaps_deprecated(value) },
 
        'show_nontrading'           : { 'Default'     : False, 
                                        'Validator'   : lambda value: isinstance(value,bool) },
 
        'figscale'                  : { 'Default'     : None, # scale base figure size up or down.
                                        'Validator'   : lambda value: isinstance(value,float) or isinstance(value,int) },
 
        'figratio'                  : { 'Default'     : None, # aspect ratio; scaled to 8.0 height
                                        'Validator'   : lambda value: isinstance(value,(tuple,list))
                                                                      and len(value) == 2
                                                                      and isinstance(value[0],(float,int))
                                                                      and isinstance(value[1],(float,int)) },
 
        'figsize'                   : { 'Default'     : None,  # figure size; overrides figratio and figscale
                                        'Validator'   : lambda value: isinstance(value,(tuple,list))
                                                                      and len(value) == 2
                                                                      and isinstance(value[0],(float,int))
                                                                      and isinstance(value[1],(float,int)) },
 
        'linecolor'                 : { 'Default'     : None, # line color in line plot
                                        'Validator'   : lambda value: mcolors.is_color_like(value) },

        'title'                     : { 'Default'     : None, # Figure Title
                                        'Validator'   : lambda value: isinstance(value,(str,dict)) },
 
        'axtitle'                   : { 'Default'     : None, # Axes Title (subplot title)
                                        'Validator'   : lambda value: isinstance(value,str) },
 
        'ylabel'                    : { 'Default'     : 'Price', # y-axis label
                                        'Validator'   : lambda value: isinstance(value,str) },
 
        'ylabel_lower'              : { 'Default'     : None, # y-axis label default logic below
                                        'Validator'   : lambda value: isinstance(value,str) },
 
        'addplot'                   : { 'Default'     : None, 
                                        'Validator'   : lambda value: isinstance(value,dict) or (isinstance(value,list) and all([isinstance(d,dict) for d in value])) },
 
        'savefig'                   : { 'Default'     : None, 
                                        'Validator'   : lambda value: isinstance(value,dict) or isinstance(value,str) or isinstance(value, io.BytesIO) },
 
        'block'                     : { 'Default'     : None, 
                                        'Validator'   : lambda value: isinstance(value,bool) },
 
        'returnfig'                 : { 'Default'     : False, 
                                        'Validator'   : lambda value: isinstance(value,bool) },

        'return_calculated_values'  : {'Default'      : None,
                                       'Validator'    : lambda value: isinstance(value, dict) and len(value) == 0},

        'set_ylim'                  : {'Default'      : None,
                                       'Validator'    : lambda value: _warn_set_ylim_deprecated(value) },
 
        'ylim'                      : {'Default'      : None,
                                       'Validator'    : lambda value: isinstance(value, (list,tuple)) and len(value) == 2 
                                                                      and all([isinstance(v,(int,float)) for v in value])},
 
        'xlim'                      : {'Default'      : None,
                                       'Validator'    : lambda value: isinstance(value, (list,tuple)) and len(value) == 2 
                                                                      and all([isinstance(v,(int,float)) for v in value])},
 
        'set_ylim_panelB'           : {'Default'      : None,
                                       'Validator'    : lambda value: _warn_set_ylim_deprecated(value) },
 
        'hlines'                    : { 'Default'     : None, 
                                        'Validator'   : lambda value: _hlines_validator(value) },
 
        'vlines'                    : { 'Default'     : None, 
                                        'Validator'   : lambda value: _vlines_validator(value) },

        'alines'                    : { 'Default'     : None, 
                                        'Validator'   : lambda value: _alines_validator(value) },
 
        'tlines'                    : { 'Default'     : None, 
                                        'Validator'   : lambda value: _tlines_validator(value) },
       
        'panel_ratios'              : { 'Default'     : None,
                                        'Validator'   : lambda value: isinstance(value,(tuple,list)) and len(value) <= 10 and
                                                                      all([isinstance(v,(int,float)) for v in value]) },

        'main_panel'                : { 'Default'     : 0,
                                        'Validator'   : lambda value: _valid_panel_id(value) },

        'volume_panel'              : { 'Default'     : 1,
                                        'Validator'   : lambda value: _valid_panel_id(value) },

        'num_panels'                : { 'Default'     : None,
                                        'Validator'   : lambda value: isinstance(value,int) and value in range(1,10+1) },

        'datetime_format'           : { 'Default'     : None,
                                        'Validator'   : lambda value: isinstance(value,str) },

        'xrotation'                 : { 'Default'     : 45,
                                        'Validator'   : lambda value: isinstance(value,(int,float)) },

        'axisoff'                   : { 'Default'     : False,
                                        'Validator'   : lambda value: isinstance(value,bool) },

        'closefig'                  : { 'Default'     : 'auto',
                                        'Validator'   : lambda value: isinstance(value,bool) },

        'fill_between'              : { 'Default'     : None,
                                        'Validator'   : lambda value: _num_or_seq_of_num(value) or 
                                                                     (isinstance(value,dict) and 'y1' in value and
                                                                       _num_or_seq_of_num(value['y1'])) },

        'tight_layout'              : { 'Default'     : False,
                                        'Validator'   : lambda value: isinstance(value,bool) },

        'width_adjuster_version'    : { 'Default'     : 'v1',
                                        'Validator'   : lambda value: value in ('v0', 'v1') },

        'scale_width_adjustment'    : { 'Default'     : None,
                                        'Validator'   : lambda value: isinstance(value,dict) and len(value) > 0 },

        'update_width_config'       : { 'Default'     : None,
                                        'Validator'   : lambda value: isinstance(value,dict) and len(value) > 0 },

        'return_width_config'       : { 'Default'     : None,
                                        'Validator'   : lambda value: isinstance(value,dict) and len(value)==0 },

        'saxbelow'                  : { 'Default'     : True,  # Issue#115 Comment#639446764
                                        'Validator'   : lambda value: isinstance(value,bool) },
        
        'scale_padding'             : { 'Default'     : 1.0,   # Issue#193 
                                        'Validator'   : lambda value: _scale_padding_validator(value) },

        'ax'                        : { 'Default'     : None,
                                        'Validator'   : lambda value: isinstance(value,mpl_axes.Axes) },

        'volume_exponent'           : { 'Default'     : None,
                                        'Validator'   : lambda value: isinstance(value,int) or value == 'legacy'},

        'tz_localize'               : { 'Default'     : True,
                                        'Validator'   : lambda value: isinstance(value,bool) },
    }

    _validate_vkwargs_dict(vkwargs)

    return vkwargs
Example #3
0
def _build_panels( figure, config ):
    """
    Create and return a DataFrame containing panel information
    and Axes objects for each panel, etc.

    We allow up to 10 panels, identified by their panel id (panid)
    which is an integer 0 through 9.  

    Parameters
    ----------
    figure       : pyplot.Figure
        figure on which to create the Axes for the panels

    config       : dict
        config dict from `mplfinance.plot()`
        
    Config
    ------
    The following items are used from `config`:

    num_panels   : integer (0-9) or None
        number of panels to create

    addplot      : dict or None
        value for the `addplot=` kwarg passed into `mplfinance.plot()`

    volume_panel : integer (0-9) or None
        panel id (0-number_of_panels)

    main_panel   : integer (0-9) or None
        panel id (0-number_of_panels)

    panel_ratios : sequence or None
        sequence of relative sizes for the panels;

        NOTE: If len(panel_ratios) == number of panels (regardless
        of whether number of panels was specified or inferred),
        then panel ratios are the relative sizes of each panel,
        in panel id order, 0 through N (where N = number of panels).

        If len(panel_ratios) != number of panels, then len(panel_ratios)
        must equal 2, and panel_ratios[0] is the relative size for the 'main'
        panel, and panel_ratios[1] is the relative size for all other panels.

        If the number of panels == 1, the panel_ratios is ignored.

    
Returns
    ----------
    panels  : pandas.DataFrame
        dataframe indexed by panel id (panid) and having the following columns:
          axes           : tuple of matplotlib.Axes (primary and secondary) for each column.
          used secondary : bool indicating whether or not the seconday Axes is in use.
          relative size  : height of panel as proportion of sum of all relative sizes

    """

    num_panels   = config['num_panels']
    addplot      = config['addplot']
    volume       = config['volume']
    volume_panel = config['volume_panel']
    num_panels   = config['num_panels']
    main_panel   = config['main_panel']
    panel_ratios = config['panel_ratios']

    if not _valid_panel_id(main_panel):
        raise ValueError('main_panel id must be integer 0 to 9, but is '+str(main_panel))

    if num_panels is None:  # then infer the number of panels:
        pset = {0} # start with a set including only panel zero
        if addplot is not None:
            if isinstance(addplot,dict):
                addplot = [addplot,]   # make list of dict to be consistent
            elif not _list_of_dict(addplot):
                raise TypeError('addplot must be `dict`, or `list of dict`, NOT '+str(type(addplot)))

            backwards_panel_compatibility = {'main':0,'lower':1,'A':0,'B':1,'C':2}

            for apdict in addplot:
                panel = apdict['panel']
                if panel in backwards_panel_compatibility:
                    panel = backwards_panel_compatibility[panel]
                if not _valid_panel_id(panel):
                    raise ValueError('addplot panel must be integer 0 to 9, but is "'+str(panel)+'"')
                pset.add(panel)

        if volume is True:
            if not _valid_panel_id(volume_panel):
                raise ValueError('volume_panel must be integer 0 to 9, but is "'+str(volume_panel)+'"')
            pset.add(volume_panel)

        pset.add(main_panel)

        pset = sorted(pset)
        missing = [m for m in range(len(pset)) if m not in pset]
        if len(missing) != 0:
            raise ValueError('inferred panel list is missing panels: '+str(missing))

    else:
        if not isinstance(num_panels,int) or num_panels < 1 or num_panels > 10:
            raise ValueError('num_panels must be integer 1 to 10, but is "'+str(volume_panel)+'"')
        pset = range(0,num_panels)

    _nones = [None]*len(pset)
    panels = pd.DataFrame(dict(axes=_nones,
                               relsize=_nones,
                               lift=_nones,
                               height=_nones,
                               used2nd=[False]*len(pset),
                               title=_nones,
                               ylabel=_nones),
                          index=pset)
    panels.index.name = 'panid'

    # Now determine the height for each panel:
    # ( figure, num_panels='infer', addplot=None, volume_panel=None, main_panel=0, panel_ratios=None ):

    if panel_ratios is not None:
        if not isinstance(panel_ratios,(list,tuple)):
            raise TypeError('panel_ratios must be a list or tuple')
        if len(panel_ratios) != len(panels) and not (len(panel_ratios)==2 and len(panels) > 2):
            err  = 'len(panel_ratios) must be 2, or must be same as number of panels'
            err += '\nlen(panel_ratios)='+str(len(panel_ratios))+'  num panels='+str(len(panels))
            raise ValueError(err)
        if len(panel_ratios) == 2 and len(panels) > 2:
            pratios = [panel_ratios[1]]*len(panels)
            pratios[main_panel] = panel_ratios[0]
        else:
            pratios = panel_ratios
    else:
        pratios = [2]*len(panels)
        pratios[main_panel] = 5

    panels['relsize'] = pratios
    #print('len(panels)=',len(panels))
    #print('len(pratios)=',len(pratios))

    #print('pratios=')
    #print(pratios)

    #print('panels=')
    #print(panels)
        
    psum = sum(pratios)
    for panid,size in enumerate(pratios):
        panels.at[panid,'height'] = 0.7 * size / psum

    # Now create the Axes:

    for panid,row in panels.iterrows():
        height = row.height
        lift   = panels['height'].loc[panid+1:].sum()
        panels.at[panid,'lift'] = lift
        if panid == 0:
            # rect = [left, bottom, width, height] 
            ax0 = figure.add_axes( [0.15, 0.18+lift, 0.70, height] )
        else:
            ax0 = figure.add_axes( [0.15, 0.18+lift, 0.70, height], sharex=panels.at[0,'axes'][0] )
        ax1 = ax0.twinx()
        ax1.grid(False)
        if config['saxbelow']:      # issue#115 issuecomment-639446764
            ax0.set_axisbelow(True) # so grid does not show through plot data on any panel.
        elif panid == volume_panel:
            ax0.set_axisbelow(True) # so grid does not show through volume bars.
        panels.at[panid,'axes'] = (ax0,ax1)

    return panels