docs: 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