def equal_weight(stocks: List[str], other_args: List[str]): """Equally weighted portfolio, where weight = 1/# of stocks Parameters ---------- stocks: List[str] List of tickers to be included in optimization Returns ------- weights : dict Dictionary of weights where keys are the tickers """ parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="equal", description="Returns an equally weighted portfolio", ) parser.add_argument( "-v", "--value", default=1, type=float, dest="value", help="Amount to allocate to portfolio", ) parser.add_argument( "--pie", action="store_true", dest="pie", default=False, help="Display a pie chart for weights", ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return if len(stocks) < 2: print( "Please have at least 2 loaded tickers to calculate weights.\n" ) return values = {} for stock in stocks: values[stock] = ns_parser.value * round(1 / len(stocks), 5) if ns_parser.pie: pie_chart_weights(values, "Equally Weighted Portfolio") else: display_weights(values) print("") except Exception as e: print(e, "\n")
def efficient_return(stocks: List[str], other_args: List[str]): """Displays a portfolio that minimises volatility for a given target return ('Markowitz portfolio') Parameters ---------- stocks : List[str] List of the stocks to be included in the weights other_args : List[str] argparse other args """ parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="effret", description= "Calculate the 'Markowitz portfolio', minimising volatility for a given target return", ) parser.add_argument( "-p", "--period", default="3mo", dest="period", help="period to get yfinance data from", choices=period_choices, ) parser.add_argument( "-v", "--value", dest="value", help="Amount to allocate to portfolio", type=float, default=1.0, ) parser.add_argument( "-n", "--market-neutral", action="store_true", default=False, dest="market_neutral", help= """whether the portfolio should be market neutral (weights sum to zero), defaults to False. Requires negative lower weight bound.""", ) if "-n" not in other_args: parser.add_argument( "--pie", action="store_true", dest="pie", default=False, help= "Display a pie chart for weights. Only if neutral flag is left False.", ) if other_args: if "-" not in other_args[0]: other_args.insert(0, "-t") parser.add_argument( "-t", "--target-return", type=float, dest="target_return", default=0.1, help="the desired return of the resulting portfolio", ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return if len(stocks) < 2: print( "Please have at least 2 loaded tickers to calculate weights.\n" ) return period = ns_parser.period stock_prices = process_stocks(stocks, period) ef = prepare_efficient_frontier(stock_prices) sp = d_period[ns_parser.period] ef_opt = dict( ef.efficient_return(ns_parser.target_return, ns_parser.market_neutral)) s_title = f"{sp} Weights that minimise volatility for a given target return of {ns_parser.target_return}" weights = { key: ns_parser.value * round(value, 5) for key, value in ef_opt.items() } if not ns_parser.market_neutral: if ns_parser.pie: pie_chart_weights(weights, s_title) ef.portfolio_performance(verbose=True) print("") return print(s_title) display_weights(weights) print("") ef.portfolio_performance(verbose=True) print("") except Exception as e: print(e, "\n")
def max_sharpe(stocks: List[str], other_args: List[str]): """Return a portfolio that maximises the Sharpe Ratio Parameters ---------- stocks : List[str] List of the stocks to be included in the weights other_args : List[str] argparse other args """ parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="maxsharpe", description="Maximise the Sharpe Ratio", ) parser.add_argument( "-p", "--period", default="3mo", dest="period", help="period to get yfinance data from", choices=period_choices, ) parser.add_argument( "-v", "--value", dest="value", help="Amount to allocate to portfolio", type=float, default=1.0, ) parser.add_argument( "--pie", action="store_true", dest="pie", default=False, help="Display a pie chart for weights", ) if other_args: if "-" not in other_args[0]: other_args.insert(0, "-r") parser.add_argument( "-r", "--risk-free-rate", type=float, dest="risk_free_rate", default=0.02, help= """Risk-free rate of borrowing/lending. The period of the risk-free rate should correspond to the frequency of expected returns.""", ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return if len(stocks) < 2: print( "Please have at least 2 loaded tickers to calculate weights.\n" ) return period = ns_parser.period stock_prices = process_stocks(stocks, period) ef = prepare_efficient_frontier(stock_prices) sp = d_period[ns_parser.period] ef_opt = dict(ef.max_sharpe(ns_parser.risk_free_rate)) s_title = f"{sp} Weights that maximize Sharpe ratio with risk free level of {ns_parser.risk_free_rate}" weights = { key: ns_parser.value * round(value, 5) for key, value in ef_opt.items() } if ns_parser.pie: pie_chart_weights(weights, s_title) else: print(s_title) display_weights(weights) print("") ef.portfolio_performance(verbose=True) print("") except Exception as e: print(e, "\n")
def min_volatility(stocks: List[str], other_args: List[str]): """Return a portfolio that optimizes for minimum volatility Parameters ---------- stocks : List[str] List of the stocks to be included in the weights other_args : List[str] argparse other args """ parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="min_volatility", description="Optimizes for minimum volatility", ) parser.add_argument( "-p", "--period", default="3mo", dest="period", help="period to get yfinance data from", choices=period_choices, ) parser.add_argument( "-v", "--value", dest="value", help="Amount to allocate to portfolio", type=float, default=1.0, ) parser.add_argument( "--pie", action="store_true", dest="pie", default=False, help="Display a pie chart for weights", ) try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return if len(stocks) < 2: print( "Please have at least 2 loaded tickers to calculate weights.\n" ) return period = ns_parser.period stock_prices = process_stocks(stocks, period) ef = prepare_efficient_frontier(stock_prices) sp = d_period[ns_parser.period] ef_opt = dict(ef.min_volatility()) s_title = f"{sp} Weights that minimize volatility" weights = { key: ns_parser.value * round(value, 5) for key, value in ef_opt.items() } if ns_parser.pie: pie_chart_weights(weights, s_title) else: print(s_title) display_weights(weights) print("") ef.portfolio_performance(verbose=True) print("") except Exception as e: print(e, "\n")
def property_weighting(stocks: List[str], other_args: List[str]): """Weighted portfolio where each weight is the relative fraction of a specified info property. Parameters ---------- stocks: List[str] List of tickers to be included in optimization other_args : List[str] Command line arguments to be processed with argparse """ parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="property", description= "Returns a portfolio that is weighted based on a selected property info", ) parser.add_argument( "-p", "--property", required=bool("-h" not in other_args), type=check_valid_property_type, dest="property", help="""Property info to weigh. Use one of: previousClose, regularMarketOpen, twoHundredDayAverage, trailingAnnualDividendYield, payoutRatio, volume24Hr, regularMarketDayHigh, navPrice, averageDailyVolume10Day, totalAssets, regularMarketPreviousClose, fiftyDayAverage, trailingAnnualDividendRate, open, toCurrency, averageVolume10days, expireDate, yield, algorithm, dividendRate, exDividendDate, beta, circulatingSupply, regularMarketDayLow, priceHint, currency, trailingPE, regularMarketVolume, lastMarket, maxSupply, openInterest, marketCap, volumeAllCurrencies, strikePrice, averageVolume, priceToSalesTrailing12Months, dayLow, ask, ytdReturn, askSize, volume, fiftyTwoWeekHigh, forwardPE, fromCurrency, fiveYearAvgDividendYield, fiftyTwoWeekLow, bid, dividendYield, bidSize, dayHigh, annualHoldingsTurnover, enterpriseToRevenue, beta3Year, profitMargins, enterpriseToEbitda, 52WeekChange, morningStarRiskRating, forwardEps, revenueQuarterlyGrowth, sharesOutstanding, fundInceptionDate, annualReportExpenseRatio, bookValue, sharesShort, sharesPercentSharesOut, fundFamily, lastFiscalYearEnd, heldPercentInstitutions, netIncomeToCommon, trailingEps, lastDividendValue, SandP52WeekChange, priceToBook, heldPercentInsiders, shortRatio, sharesShortPreviousMonthDate, floatShares, enterpriseValue, threeYearAverageReturn, lastSplitFactor, legalType, lastDividendDate, morningStarOverallRating, earningsQuarterlyGrowth, pegRatio, lastCapGain, shortPercentOfFloat, sharesShortPriorMonth, impliedSharesOutstanding, fiveYearAverageReturn, and regularMarketPrice.""", ) parser.add_argument( "-v", "--value", default=1, type=float, dest="value", help="Amount to allocate to portfolio", ) parser.add_argument( "--pie", action="store_true", dest="pie", default=False, help="Display a pie chart for weights", ) try: if other_args: if "-" not in other_args[0]: other_args.insert(0, "-p") ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return if len(stocks) < 2: print( "Please have at least 2 loaded tickers to calculate weights.\n" ) return weights = {} prop = {} prop_sum = 0 for stock in stocks: stock_prop = yf.Ticker(stock).info[ns_parser.property] if stock_prop is None: stock_prop = 0 prop[stock] = stock_prop prop_sum += stock_prop if prop_sum == 0: print( f"No {ns_parser.property} was found on list of tickers provided", "\n") return for k, v in prop.items(): weights[k] = round(v / prop_sum, 5) * ns_parser.value if ns_parser.pie: pie_chart_weights( weights, "Weighted Portfolio based on " + ns_parser.property) else: display_weights(weights) print("") except Exception as e: print(e, "\n")