code: def calc_sortino_ratio(returns, rf=0., nperiods=None, annualize=True):
      
      if type(rf) is float and rf != 0 and nperiods is None:
          raise Exception('nperiods must be set if rf != 0 and rf is not a price series')
  
      er = returns.to_excess_returns(rf, nperiods=nperiods)
  
      negative_returns = np.minimum(returns[1:], 0.)
      std = np.std(negative_returns, ddof=1)
      res = np.divide(er.mean(), std)
  
      if annualize:
          if nperiods is None:
              nperiods = 1
          return res * np.sqrt(nperiods)
  
      return res
docs: Calculates the `Sortino ratio <https://www.investopedia.com/terms/s/sortinoratio.asp>`_ given a series of returns (see `Sharpe vs. Sortino <https://www.investopedia.com/ask/answers/010815/what-difference-between-sharpe-ratio-and-sortino-ratio.asp>`_).
  
      Args:
          * returns (Series or DataFrame): Returns
          * rf (float, Series): `Risk-free rate <https://www.investopedia.com/terms/r/risk-freerate.asp>`_ expressed in yearly (annualized) terms or return series.
          * nperiods (int): Number of periods used for annualization. Must be
              provided if rf is non-zero and rf is not a price series