def portfolio_byvalue(
    weights, steps, min_values, max_values=1e9, total_portfolio_value=10000
):
    """
    For a long only portfolio, convert the continuous weights to a discrete
    allocation using Mixed Integer Linear Programming. This function assumes
    that we buy some asset based on value instead of shares, and there is a
    limit of minimum value and increasing step.

    :param weights: continuous weights generated from the ``efficient_frontier`` module
    :type weights: dict
    :param min_values: the minimum value for each asset
    :type min_values: int/float or dict
    :param max_values: the maximum value for each asset
    :type max_values: int/float or dict
    :param steps: the minimum value increase for each asset
    :type steps: int/float or dict
    :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
    :return: the number of value 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 total_portfolio_value <= 0:
        raise ValueError("total_portfolio_value must be greater than zero")

    if isinstance(steps, numbers.Real):
        steps = {k: steps for k in weights}
    if isinstance(min_values, numbers.Real):
        min_values = {k: min_values for k in weights}
    if isinstance(max_values, numbers.Real):
        max_values = {k: max_values for k in weights}

    m = pulp.LpProblem("PfAlloc", pulp.LpMinimize)
    vals = {}
    realvals = {}
    usevals = {}
    etas = {}
    abss = {}
    remaining = pulp.LpVariable("remaining", 0)
    for k, w in weights.items():
        if steps.get(k):
            vals[k] = pulp.LpVariable("x_%s" % k, 0, cat="Integer")
            realvals[k] = steps[k] * vals[k]
            etas[k] = w * total_portfolio_value - realvals[k]
        else:
            realvals[k] = vals[k] = pulp.LpVariable("x_%s" % k, 0)
            etas[k] = w * total_portfolio_value - vals[k]
        abss[k] = pulp.LpVariable("u_%s" % k, 0)
        usevals[k] = pulp.LpVariable("b_%s" % k, cat="Binary")
        m += etas[k] <= abss[k]
        m += -etas[k] <= abss[k]
        m += realvals[k] >= usevals[k] * min_values.get(k, steps.get(k, 0))
        m += realvals[k] <= usevals[k] * max_values.get(k, 1e18)
    m += remaining == total_portfolio_value - pulp.lpSum(realvals.values())
    m += pulp.lpSum(abss.values()) + remaining
    m.solve()
    results = {k: pulp.value(val) for k, val in realvals.items()}
    return results, remaining.varValue
def portfolio_byvalue ( weights , steps , min_values , max_values = 1e9 , total_portfolio_value = 10000 ) : import pulp if not isinstance ( weights , dict ) : raise TypeError ( "weights should be a dictionary of {ticker: weight}" ) if total_portfolio_value <= 0 : raise ValueError ( "total_portfolio_value must be greater than zero" ) if isinstance ( steps , numbers . Real ) : steps = { k : steps for k in weights } if isinstance ( min_values , numbers . Real ) : min_values = { k : min_values for k in weights } if isinstance ( max_values , numbers . Real ) : max_values = { k : max_values for k in weights } m = pulp . LpProblem ( "PfAlloc" , pulp . LpMinimize ) vals = { } realvals = { } usevals = { } etas = { } abss = { } remaining = pulp . LpVariable ( "remaining" , 0 ) for k , w in weights . items ( ) : if steps . get ( k ) : vals [ k ] = pulp . LpVariable ( "x_%s" % k , 0 , cat = "Integer" ) realvals [ k ] = steps [ k ] * vals [ k ] etas [ k ] = w * total_portfolio_value - realvals [ k ] else : realvals [ k ] = vals [ k ] = pulp . LpVariable ( "x_%s" % k , 0 ) etas [ k ] = w * total_portfolio_value - vals [ k ] abss [ k ] = pulp . LpVariable ( "u_%s" % k , 0 ) usevals [ k ] = pulp . LpVariable ( "b_%s" % k , cat = "Binary" ) m += etas [ k ] <= abss [ k ] m += - etas [ k ] <= abss [ k ] m += realvals [ k ] >= usevals [ k ] * min_values . get ( k , steps . get ( k , 0 ) ) m += realvals [ k ] <= usevals [ k ] * max_values . get ( k , 1e18 ) m += remaining == total_portfolio_value - pulp . lpSum ( realvals . values ( ) ) m += pulp . lpSum ( abss . values ( ) ) + remaining m . solve ( ) results = { k : pulp . value ( val ) for k , val in realvals . items ( ) } return results , remaining . varValue
For a long only portfolio, convert the continuous weights to a discrete
    allocation using Mixed Integer Linear Programming. This function assumes
    that we buy some asset based on value instead of shares, and there is a
    limit of minimum value and increasing step.

    :param weights: continuous weights generated from the ``efficient_frontier`` module
    :type weights: dict
    :param min_values: the minimum value for each asset
    :type min_values: int/float or dict
    :param max_values: the maximum value for each asset
    :type max_values: int/float or dict
    :param steps: the minimum value increase for each asset
    :type steps: int/float or dict
    :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
    :return: the number of value of each ticker that should be purchased,
    along with the amount of funds leftover.
    :rtype: (dict, float)