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