def portfolio_lp(
    weights, latest_prices, min_allocation=0.01, total_portfolio_value=10000
):
    """
    For a long only portfolio, convert the continuous weights to a discrete allocation
    using Mixed Integer Linear Programming. This can be thought of as a clever way to round
    the continuous weights to an integer number of shares

    :param weights: continuous weights generated from the ``efficient_frontier`` module
    :type weights: dict
    :param latest_prices: the most recent price for each asset
    :type latest_prices: pd.Series or dict
    :param min_allocation: any weights less than this number are considered negligible,
                           defaults to 0.01
    :type min_allocation: float, optional
    :param total_portfolio_value: the desired total value of the portfolio, defaults to 10000
    :type total_portfolio_value: int/float, optional
    :raises TypeError: if ``weights`` is not a dict
    :raises TypeError: if ``latest_prices`` isn't a series
    :raises ValueError: if not ``0 < min_allocation < 0.3``
    :return: the number of shares of each ticker that should be purchased, along with the amount
             of funds leftover.
    :rtype: (dict, float)
    """
    import pulp

    if not isinstance(weights, dict):
        raise TypeError("weights should be a dictionary of {ticker: weight}")
    if not isinstance(latest_prices, (pd.Series, dict)):
        raise TypeError("latest_prices should be a pd.Series")
    if min_allocation > 0.3:
        raise ValueError("min_allocation should be a small float")
    if total_portfolio_value <= 0:
        raise ValueError("total_portfolio_value must be greater than zero")

    m = pulp.LpProblem("PfAlloc", pulp.LpMinimize)
    vals = {}
    realvals = {}
    etas = {}
    abss = {}
    remaining = pulp.LpVariable("remaining", 0)
    for k, w in weights.items():
        if w < min_allocation:
            continue
        vals[k] = pulp.LpVariable("x_%s" % k, 0, cat="Integer")
        realvals[k] = latest_prices[k] * vals[k]
        etas[k] = w * total_portfolio_value - realvals[k]
        abss[k] = pulp.LpVariable("u_%s" % k, 0)
        m += etas[k] <= abss[k]
        m += -etas[k] <= abss[k]
    m += remaining == total_portfolio_value - pulp.lpSum(realvals.values())
    m += pulp.lpSum(abss.values()) + remaining
    m.solve()
    results = {k: val.varValue for k, val in vals.items()}
    return results, remaining.varValue
def portfolio_lp ( weights , latest_prices , min_allocation = 0.01 , total_portfolio_value = 10000 ) : import pulp if not isinstance ( weights , dict ) : raise TypeError ( "weights should be a dictionary of {ticker: weight}" ) if not isinstance ( latest_prices , ( pd . Series , dict ) ) : raise TypeError ( "latest_prices should be a pd.Series" ) if min_allocation > 0.3 : raise ValueError ( "min_allocation should be a small float" ) if total_portfolio_value <= 0 : raise ValueError ( "total_portfolio_value must be greater than zero" ) m = pulp . LpProblem ( "PfAlloc" , pulp . LpMinimize ) vals = { } realvals = { } etas = { } abss = { } remaining = pulp . LpVariable ( "remaining" , 0 ) for k , w in weights . items ( ) : if w < min_allocation : continue vals [ k ] = pulp . LpVariable ( "x_%s" % k , 0 , cat = "Integer" ) realvals [ k ] = latest_prices [ k ] * vals [ k ] etas [ k ] = w * total_portfolio_value - realvals [ k ] abss [ k ] = pulp . LpVariable ( "u_%s" % k , 0 ) m += etas [ k ] <= abss [ k ] m += - etas [ k ] <= abss [ k ] m += remaining == total_portfolio_value - pulp . lpSum ( realvals . values ( ) ) m += pulp . lpSum ( abss . values ( ) ) + remaining m . solve ( ) results = { k : val . varValue for k , val in vals . items ( ) } return results , remaining . varValue
For a long only portfolio, convert the continuous weights to a discrete allocation
    using Mixed Integer Linear Programming. This can be thought of as a clever way to round
    the continuous weights to an integer number of shares

    :param weights: continuous weights generated from the ``efficient_frontier`` module
    :type weights: dict
    :param latest_prices: the most recent price for each asset
    :type latest_prices: pd.Series or dict
    :param min_allocation: any weights less than this number are considered negligible,
                           defaults to 0.01
    :type min_allocation: float, optional
    :param total_portfolio_value: the desired total value of the portfolio, defaults to 10000
    :type total_portfolio_value: int/float, optional
    :raises TypeError: if ``weights`` is not a dict
    :raises TypeError: if ``latest_prices`` isn't a series
    :raises ValueError: if not ``0 < min_allocation < 0.3``
    :return: the number of shares of each ticker that should be purchased, along with the amount
             of funds leftover.
    :rtype: (dict, float)