Skip to content

amanda-ucc/market-tracker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 

Repository files navigation

Portfolio creation that tracks an index

market-tracker creates a portfolio of 10 to 25 stocks, from a larger pool, that is intended to mimic a benchmark index.

Approach taken is to use the previous years data and run a greedy algorithm that minimize the tracking error with respect to the porfolio weights using SciPy optimize.

The tracking error is measured as follows:

$$ TE = \sqrt{\mathrm{Var}(R_p - R_{\text{index}})} $$

$$ TE = \sqrt{\frac{1}{T} \sum_{t=1}^{T} (R_{p,t} - R_{\text{index},t})^{2}} $$

The greedy algorithm functions as follows:

Two are included due to constrainsts so we start with those two and determine the next stock to add by minimizing a 3 stock portfolio with respect to the weights for each of the availble candidate stocks. The stock that makes the 3 stock portfolio have the lowest tracking error is included and then we add a 4th stock and so one which produces the lowest tracking error. The algorithm is greedy in that it assumes the path take to get to an optiminal k will be to determine optimal n in order.

def eval_best_among_candidates(
    X_all: pd.DataFrame,
    y: np.ndarray,
    current_selected: list[str],
    candidate_pool: list[str],
    K: int,

    verbose: bool = False,
):
    '''Evalutate the best stock from a candidate pool to add to the current selected list.'''

    best_te = np.inf
    best_ticker = None
    best_weights = None

    for ticker in candidate_pool:
        tickers_sub = current_selected + [ticker]
        X = X_all[tickers_sub].to_numpy()



        ok, w_opt, te = minimize_te_best_weights(X, y, K, None, None) # find the optimal weights for 

        # if optimization failed skip this ticker
        if not ok:
            if verbose:
                print(f"  Skipping {ticker}: TE optimizer failed.")
            continue

        # if it the best tracking error than keep it otherwise do not keep it    
        if te < best_te:
            best_te = te
            best_ticker = ticker
            best_weights = w_opt

        if verbose:
            print(f" Tested {ticker}: TE={te:.6f}")

    return best_ticker, best_weights, best_te

After each of the best 10, 11, 12 ... 25 stock portfolio is determined, it is just a matter of choosing which of those 15 is best.

This approach is determined to be better than calculating a portfolio beta closest to 1 as follows.

$$ \min_{\mathbf{w}} \left| \sum_{i=1}^{N} w_i \beta_i - 1 \right| $$

The reasons which involve diversification, compand and industry risk are outlined in the following academic papers.

Academic References:
https://www.sciencedirect.com/science/article/abs/pii/S1062976914000763
https://link.springer.com/article/10.1007/s10957-022-02116-w

About

Automated portfolio creator that generates a portoflio which mimics a bench mark index by minimizing the tracking error.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors