예제 #1
0
def remove_sold_buys(lots):
    # WARNING: if you sold part of a lot, this won't catch it
    buys = [lot for lot in lots if not lot.has_sell()]
    sells = [lot for lot in lots if lot.has_sell()]
    ret = []
    for buy in buys:
        found = False
        for idx, sell in enumerate(sells):
            if buy.acquition_match(sell):
                found = True
                ret.append(sells.pop(idx))
                break
        if not found:
            print "No match for", buy
            ret.append(buy)
    for sell in sells:
        print "No match for", sell
    ret.extend(sells)
    return ret
예제 #2
0
def remove_sold_buys(lots):
  # WARNING: if you sold part of a lot, this won't catch it
  buys = [lot for lot in lots if not lot.has_sell()]
  sells = [lot for lot in lots if lot.has_sell()]
  ret = []
  for buy in buys:
    found = False
    for idx, sell in enumerate(sells):
      if buy.acquition_match(sell):
        found = True
        ret.append(sells.pop(idx))
        break
    if not found:
      print "No match for", buy
      ret.append(buy)
  for sell in sells:
    print "No match for", sell
  ret.extend(sells)
  return ret
예제 #3
0
def earliest_wash_loss(lots):
  lots.sort(cmp=cmp_by_sell_date)
  for lot in lots:
    if not lot.has_sell():
      return None  # We're done
    if lot.proceeds >= lot.basis:
      continue
    buys = buy_lots_within_window(lots, lot)
    if not buys:
      continue
    return lot
  return None
예제 #4
0
def earliest_wash_loss(lots):
    lots.sort(cmp=cmp_by_sell_date)
    ret = []
    for i, lot in enumerate(lots):
        if not lot.has_sell():
            return None  # We're done
        if lot.proceeds >= lot.basis:
            continue
        buys = buy_lots_within_window(lots, lot)
        if not buys:
            continue
        ret.append(lot)
        # Pull all the next lots w/ the same sell-date into ret if they have losses
        i = i + 1
        while i < len(lots):
            if (lots[i].has_sell() and lots[i].proceeds < lots[i].basis
                    and lots[i].selldate == ret[0].selldate):
                ret.append(lots[i])
                i = i + 1
                continue
            break
        return ret
예제 #5
0
def earliest_wash_loss(lots):
  lots.sort(cmp=cmp_by_sell_date)
  ret = []
  for i, lot in enumerate(lots):
    if not lot.has_sell():
      return None  # We're done
    if lot.proceeds >= lot.basis:
      continue
    buys = buy_lots_within_window(lots, lot)
    if not buys:
      continue
    ret.append(lot)
    # Pull all the next lots w/ the same sell-date into ret if they have losses
    i = i + 1
    while i < len(lots):
      if (lots[i].has_sell() and lots[i].proceeds < lots[i].basis and
          lots[i].selldate == ret[0].selldate):
        ret.append(lots[i])
        i = i + 1
        continue
      break
    return ret
예제 #6
0
def match_lots_to_1099(lots, t1099):
    # Matches lots from statement w/ those from 1099b.
    # The idea is the 1099b has the correct sale date and proceeds, while
    # the raw lots have the real acquition date and basis.
    # We assume the 1099b may have incorrect basis info if wash sales were
    # computed (incorrectly) by the broker.
    # Return value is list of lots with real acquition date and basis
    # (from 'lots') and real proceeds and sale date (from 't1099').
    # Algorithm:
    # For each set of lots that were sold on the same day:
    #   If they have the same count and acquition date, they match. easy.
    #   For lots from statement that don't have matches from 1099,
    #   see how many ways they could match. If just one, do that.
    #   If more than one, then it's a failure and you'd prompt the user.

    def pop_lots_with_selldate(lots, selldate):
        return ([lot for lot in lots if lot.selldate == selldate],
                [lot for lot in lots if lot.selldate != selldate])

    def match_lots(raw, from1099):
        # match will be the same len as from1099, and will indicate for each
        # corresponding lot in from1099, which element in raw it corresponds to.
        print 'match_lots:', len(raw), len(from1099)
        match = [-1] * len(from1099)
        for fromidx in xrange(len(from1099) - 1, -1, -1):
            fromlot = from1099[fromidx]
            found = False
            print 'looking for match for', fromlot
            for rawidx in xrange(len(raw)):
                rawlot = raw[rawidx]
                print '  try:', rawlot
                if rawidx in match:
                    print '  already matched.'
                    continue
                if (rawlot.count == fromlot.count
                        and rawlot.buydate == fromlot.buydate):
                    found = True
                    print 'match found'
                    match[fromidx] = rawidx
                    break
            if not found:
                print "can't find match", fromlot
        if -1 not in match:
            return match
        print "Need assistance matching."
        for rawidx in xrange(len(raw)):
            if rawidx in match:
                continue
            print '%d: %s' % (rawidx, raw[rawidx])
        print "Pick indexes above for each of these lots from 1099b:"
        for i in xrange(len(match)):
            if match[i] != -1:
                continue
            print from1099[i]
        print "Now, you can give your input:"
        for i in xrange(len(match)):
            if match[i] != -1:
                continue
            print from1099[i]
            print "For this lot, which index is corresponding?"
            match[i] = int(raw_input("idx:"))
        return match

    def set_basis(good, bad):
        # Applies basis from good to bad and clears adjustment
        bad.buydate = good.buydate
        bad.basis = bad.count * good.basis / good.count
        bad.code = ''
        bad.adjustment = 0.0

    # First, move over the buy-only lots, as they don't factor in
    ret = [lot for lot in lots if not lot.has_sell()]
    lots = [lot for lot in lots if lot.has_sell()]
    while lots:
        date = lots[0].selldate
        print 'selldate', date
        (lotsraw, lots) = pop_lots_with_selldate(lots, date)
        (lots1099, t1099) = pop_lots_with_selldate(t1099, date)
        matches = match_lots(lotsraw, lots1099)
        for i in xrange(len(matches)):
            set_basis(lotsraw[matches[i]], lots1099[i])
        ret.extend(lots1099)
    return ret
예제 #7
0
def match_lots_to_1099(lots, t1099):
  # Matches lots from statement w/ those from 1099b.
  # The idea is the 1099b has the correct sale date and proceeds, while
  # the raw lots have the real acquition date and basis.
  # We assume the 1099b may have incorrect basis info if wash sales were
  # computed (incorrectly) by the broker.
  # Return value is list of lots with real acquition date and basis
  # (from 'lots') and real proceeds and sale date (from 't1099').
  # Algorithm:
  # For each set of lots that were sold on the same day:
  #   If they have the same count and acquition date, they match. easy.
  #   For lots from statement that don't have matches from 1099,
  #   see how many ways they could match. If just one, do that.
  #   If more than one, then it's a failure and you'd prompt the user.

  def pop_lots_with_selldate(lots, selldate):
    return ([lot for lot in lots if lot.selldate == selldate],
            [lot for lot in lots if lot.selldate != selldate])

  def match_lots(raw, from1099):
    # match will be the same len as from1099, and will indicate for each
    # corresponding lot in from1099, which element in raw it corresponds to.
    print 'match_lots:', len(raw), len(from1099)
    match = [-1] * len(from1099)
    for fromidx in xrange(len(from1099) - 1, -1, -1):
      fromlot = from1099[fromidx]
      found = False
      print 'looking for match for', fromlot
      for rawidx in xrange(len(raw)):
        rawlot = raw[rawidx]
        print '  try:', rawlot
        if rawidx in match:
          print '  already matched.'
          continue
        if (rawlot.count == fromlot.count and
            rawlot.buydate == fromlot.buydate):
          found = True
          print 'match found'
          match[fromidx] = rawidx
          break
      if not found:
        print "can't find match", fromlot
    if -1 not in match:
      return match
    print "Need assistance matching."
    for rawidx in xrange(len(raw)):
      if rawidx in match:
        continue
      print '%d: %s' % (rawidx, raw[rawidx])
    print "Pick indexes above for each of these lots from 1099b:"
    for i in xrange(len(match)):
      if match[i] != -1:
        continue
      print from1099[i]
    print "Now, you can give your input:"
    for i in xrange(len(match)):
      if match[i] != -1:
        continue
      print from1099[i]
      print "For this lot, which index is corresponding?"
      match[i] = int(raw_input("idx:"))
    return match

  def set_basis(good, bad):
    # Applies basis from good to bad and clears adjustment
    bad.buydate = good.buydate
    bad.basis = bad.count * good.basis / good.count
    bad.code = ''
    bad.adjustment = 0.0

  # First, move over the buy-only lots, as they don't factor in
  ret = [lot for lot in lots if not lot.has_sell()]
  lots = [lot for lot in lots if lot.has_sell()]
  while lots:
    date = lots[0].selldate
    print 'selldate', date
    (lotsraw, lots) = pop_lots_with_selldate(lots, date)
    (lots1099, t1099) = pop_lots_with_selldate(t1099, date)
    matches = match_lots(lotsraw, lots1099)
    for i in xrange(len(matches)):
      set_basis(lotsraw[matches[i]], lots1099[i])
    ret.extend(lots1099)
  return ret