Example #1
0
def get_return_vol(data, scale=1, ret=False, plotit=False):
    """
    Calculate return and volatility of given data.
    input:  data    a DataFrame or Series with ETF prices
            scale   factor if convert to annual return. Default 1
                    12 for monthly, 52 for weekly, and 252 for daily data
            ret     indicate whether the input data is return or price (default).
                    default False, and simple return is calculated
            plotit  indicate whether to make plots
    return a DataFrame with tickers as index and columns: "Return", "Volatility"
    """
    from basic.mathe import covariance
    logger = logging.getLogger(__name__)
    if data.ndim == 1:
        data = data.to_frame()
    if ret:
        rts = data
    else:
        rts = get_returns(data, 'simple')
    ret = rts.mean().values * scale
    vol = rts.apply(lambda x: np.sqrt(covariance(x) * scale)).values
    if plotit:
        from invest.plot import return_vol
        return_vol(ret, vol, data.columns)
    return pd.DataFrame({"Return": ret, "Volatility": vol}, index=data.columns)
Example #2
0
def mean_variance_optimization(mean,
                               variance,
                               returns=None,
                               riskfree=None,
                               alloc_lim=None,
                               short_sell=False,
                               strict=True,
                               verbose=True,
                               plotit=False):
    """
    Calculate the portfolio with the lowest risk given returns.
    input:  mean        a numpy array (n,) of mean values
            variance    a numpy array (n,n) of the covariance matrix
            returns     a list of return values to be calculated, default mean(mean)
            riskfree    the risk-free rate, default None.
                        If not None, then a risk free stock is added for consideration
            alloc_lim   a list of two numbers indicating the percentage range of one stock.
                        Default None, i.e. no limit constraint.
            strict      indicate whether the return value should be strict, default True
            verbose     indicate whether to print progress bar, default True.
            plotit      indicate whether to make Return-Volatility plots, default False.
    Returns vol, alloc. A numpy array (m,) of minimum volatility and a numpy array (n,m)
    of allocations. Here m is the number of given returns.
    """
    logger = logging.getLogger(__name__)
    if returns is None:
        returns = [np.mean(mean)]
    returns = np.array(returns)
    if alloc_lim is None:
        var_inv = np.linalg.inv(variance)
        ones = np.ones(mean.shape)
        a = mean.dot(var_inv).dot(mean)
        b = mean.dot(var_inv).dot(ones)
        c = ones.dot(var_inv).dot(ones)
        lambda1 = (c * returns - b) / (a * c - b * b)
        lambda2 = (-b * returns + a) / (a * c - b * b)
        alloc = np.kron(lambda1, var_inv.dot(mean)) + np.kron(
            lambda2, var_inv.dot(ones))
        vol = np.sqrt(c * returns**2 - 2 * b * returns + a)
        if plotit:
            import matplotlib.pyplot as plt
            from invest.plot import return_vol
            return_vol(mean, np.sqrt(np.diag(variance)), [''] * len(returns))
            plt.plot(aloc.Volatility * 100, aloc.Return * 100, '.-')
            xs = np.linspace(np.min(mean), np.max(mean), 200)
            ys = np.sqrt(c * xs**2 - 2 * b * xs + a)
            plt.plot(aloc.Volatility[arg] * 100,
                     aloc.Return[arg] * 100,
                     'rX',
                     markersize=12)
            print("Max Sharpe ratio is {:.2f}".format(sharpe[arg]))
        if riskfree is not None:
            var_inv = np.linalg.inv(variance)
            vol = np.abs(returns - riskfree) / np.sqrt(
                (mean - riskfree).dot(var_inv).dot(mean - riskfree))
            alloc = np.kron(vol * vol / (returns - riskfree),
                            var_inv.dot(mean - riskfree))
Example #3
0
 def initial_plot(self, column='Adj Close', style='week', start='2015-1-1'):
     self._logger_.debug("Initialize plots for portfolio window")
     tickers = self.select.get_right()
     self._data_ = get_returns(resample(read_portfolio(tickers,
                                                       column=column,
                                                       start=start),
                                        column=None,
                                        style=style,
                                        method='close'),
                               style='simple',
                               fillna=False)
     self._data_['benchmark'] = 0
     data = self._data_.iloc[-52:, :-1].dropna(axis=1, how='any')
     rv = get_return_vol(pd.concat([data * 3, -data], axis=1),
                         scale=52,
                         ret=True,
                         plotit=False)
     fig = return_vol(rv.Return, rv.Volatility, rv.index)
     fig.axes[0].plot([0], [0], 'r*')
     return fig, pie_plot([10, 6], labels=['a', 'b'])
Example #4
0
def minimize_risk(data,
                  returns=None,
                  strict=True,
                  riskfree=None,
                  max_alloc=1,
                  short_sell=False,
                  scale=1,
                  ret=False,
                  verbose=True,
                  plotit=False):
    """
    Calculate the portfolio with the lowest risk given returns.
    input:  data        a DataFrame with ETF prices or returns
            returns     a list of return values to use
            strict      indicate whether the return value should be strict, default True
            riskfree    the risk-free return rate, default None.
                        If not None, then a risk free stock is added for consideration
            max_alloc   a number from 0 to 1. The maximum percentage of one stock
            short_sell  indicate whether to allow short sell. Default False.
            scale       factor if convert to annual return. Default 1.
                        12 for monthly, 52 for weekly, 252 for daily
            ret         indicate whether the input data is return or price (default).
                        default False, and simple return is calculated
            verbose     indicate whether to print progress bar, default True.
            plotit      indicate whether to make Return-Volatility plots, default False.
    Returns a DataFrame with columns as [tickers,Volatility,Return] and index as targeted returns
    """
    logger = logging.getLogger(__name__)
    if ret:
        weekly = data
    else:
        weekly = get_returns(data, 'simple')
    ret = weekly.mean().values * scale
    cov = weekly.cov().values * scale
    if short_sell:
        return pd.DataFrame()
    n = data.shape[1]
    if riskfree is None:
        aloc = pd.DataFrame(
            columns=np.append(data.columns, ['Volatility', 'Return']))
        bounds = [(0, max_alloc)] * n
    else:
        ret = np.append(ret, riskfree)
        cov = np.hstack(
            [np.vstack([cov, np.zeros([1, n])]),
             np.zeros([n + 1, 1])])
        aloc = pd.DataFrame(columns=np.append(
            data.columns, ['risk-free', 'Volatility', 'Return']))
        bounds = [(0, max_alloc)] * n + [(0, 1)]
        n += 1
    if returns is None:
        returns = np.linspace(min(ret), max(ret), 25, endpoint=True)

    from scipy.optimize import minimize
    from basic.useful import progress_bar

    def func(alpha):
        def loss(x):
            return x.dot(cov).dot(x)

        def jac(x):
            return cov.dot(x) * 2

        cons1 = {
            'type': 'eq',
            'fun': lambda x: np.ones(n).dot(x) - 1,
            'jac': lambda x: np.ones(n)
        }
        types = 'eq'
        if not strict: types = 'ineq'
        cons2 = {
            'type': types,
            'fun': lambda x: ret.dot(x) - alpha,
            'jac': lambda x: ret
        }
        x = minimize(loss,
                     np.ones(n) / n,
                     jac=jac,
                     constraints=[cons1, cons2],
                     bounds=bounds,
                     method='SLSQP')
        aloc.loc[alpha, :] = np.append(np.round(
            x['x'], 4), [np.sqrt(x['fun']), ret.dot(x['x'])])
        return ""

    progress_bar(returns, func, disable=not verbose)
    if plotit:
        import matplotlib.pyplot as plt
        from invest.plot import return_vol
        vol = np.sqrt(np.diag(cov))
        return_vol(ret, vol, data.columns)
        plt.plot(aloc.Volatility * 100, aloc.Return * 100, '.-')
        sharpe = aloc.Return / aloc.Volatility
        arg = sharpe.argmax()
        plt.plot(aloc.Volatility[arg] * 100,
                 aloc.Return[arg] * 100,
                 'rX',
                 markersize=12)
        print("Max Sharpe ratio is {:.2f}".format(sharpe[arg]))
    return aloc.astype(float)