public void CostBasisAcrossTransfers() { UiDispatcher.CurrentDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; MyMoney m = new MyMoney(); Security s = m.Securities.NewSecurity(); s.Name = "MSFT"; Account a = m.Accounts.AddAccount("Ameritrade"); Account a2 = m.Accounts.AddAccount("Fidelity"); AddInvestment(m, s, a, DateTime.Parse("1/1/2000"), 10, 1, InvestmentType.Buy); m.StockSplits.AddStockSplit(new StockSplit() { Date = DateTime.Parse("6/1/2000"), Numerator = 2, Denominator = 1, Security = s }); AddInvestment(m, s, a, DateTime.Parse("1/1/2001"), 20, 2, InvestmentType.Buy); Transaction transfer = AddInvestment(m, s, a, DateTime.Parse("1/1/2002"), 30, 2, InvestmentType.Remove); m.Transfer(transfer, a2); // now should be able to sell 10 left in this account (after split) AddInvestment(m, s, a, DateTime.Parse("1/1/2003"), 10, 3, InvestmentType.Sell); // and we should have 30 in the other account AddInvestment(m, s, a2, DateTime.Parse("1/1/2004"), 30, 5, InvestmentType.Sell); // Ok, now let's if the cost basis is correct! CostBasisCalculator calc = new CostBasisCalculator(m, DateTime.Now); List <SecurityPurchase> holdings = new List <SecurityPurchase>(calc.GetHolding(a).GetHoldings()); Assert.AreEqual <int>(0, holdings.Count); // should have nothing left. // should have 3 separate cost basis records to cover what we sold. List <SecuritySale> sales = new List <SecuritySale>(calc.GetSales()); Assert.AreEqual <int>(3, sales.Count); SecuritySale s1 = sales[0]; SecuritySale s2 = sales[1]; SecuritySale s3 = sales[2]; // since the sale from Ameritrade account happened first it should be returned first Assert.AreEqual <decimal>(2, s1.CostBasisPerUnit); // $2, no splits Assert.AreEqual(a, s1.Account); // Ameritrade Assert.AreEqual <decimal>(Math.Round(10M, 5), Math.Round(s1.UnitsSold, 5)); // Notice here that the Fidelity account inherited the cost basis records correctly // from the Ameritrade account as a result of the "Transfer" that happened above. Assert.AreEqual <decimal>(Math.Round(1M / 2M, 5), Math.Round(s2.CostBasisPerUnit, 5)); // $1 after 2:1 split Assert.AreEqual(a2, s2.Account); // Fidelity Assert.AreEqual <decimal>(Math.Round(20M, 5), Math.Round(s2.UnitsSold, 5)); Assert.AreEqual <decimal>(2, s3.CostBasisPerUnit); // $2, no splits Assert.AreEqual(a2, s2.Account); // Fidelity Assert.AreEqual <decimal>(Math.Round(10M, 5), Math.Round(s3.UnitsSold, 5)); }
void WriteCapitalGains(IReportWriter writer, SecuritySale data) { writer.StartRow(); writer.StartCell(); writer.WriteParagraph(data.Security.Name); writer.EndCell(); writer.StartCell(); writer.WriteNumber(Rounded(data.UnitsSold, 3)); writer.EndCell(); writer.StartCell(); if (data.DateAcquired == null) { writer.WriteNumber("VARIOUS"); } else { writer.WriteNumber(data.DateAcquired.Value.ToShortDateString()); } writer.EndCell(); writer.StartCell(); writer.WriteNumber(data.CostBasisPerUnit.ToString("C")); writer.EndCell(); writer.StartCell(); writer.WriteNumber(data.TotalCostBasis.ToString("C")); writer.EndCell(); writer.StartCell(); writer.WriteNumber(data.DateSold.ToShortDateString()); writer.EndCell(); writer.StartCell(); writer.WriteNumber(data.SalePricePerUnit.ToString("C")); writer.EndCell(); writer.StartCell(); writer.WriteNumber(data.SaleProceeds.ToString("C")); writer.EndCell(); writer.StartCell(); writer.WriteNumber(GiveUpTheFractionalPennies(data.TotalGain).ToString("C")); writer.EndCell(); writer.EndRow(); }
/// <summary> /// Consolidate capital gains information. If the securities are the same and the sale date is the same then /// combine them, and mark the "DateAquired" as null (reported as 'various'). /// </summary> private List <SecuritySale> Consolidate(List <SecuritySale> list, bool consolidateOnDateSold) { List <SecuritySale> result = new List <SecuritySale>(); SecuritySale previous = null; foreach (SecuritySale cg in list) { if (previous == null || previous.Security != cg.Security || (!consolidateOnDateSold && previous.DateAcquired != cg.DateAcquired) || (previous.SalePricePerUnit != cg.SalePricePerUnit) || (consolidateOnDateSold && previous.DateSold != cg.DateSold)) { previous = cg; result.Add(cg); } else { previous.Consolidate(cg); } } return(result); }
private static void WriteRecordFormat4(TextWriter writer, SecuritySale data, int refnum) { writer.WriteLine("TD"); writer.WriteLine("N" + refnum); writer.WriteLine("C1"); writer.WriteLine("L1"); // convention is to stick the quantity sold in the payee field. writer.WriteLine("P" + (int)data.UnitsSold + " " + data.Security); if (refnum == 673) { writer.WriteLine("D"); } else if (data.DateAcquired == null) { writer.WriteLine("DVARIOUS"); } else { writer.WriteLine("D" + data.DateAcquired.Value.ToShortDateString()); } writer.WriteLine("D" + data.DateSold.ToShortDateString()); if (refnum == 673) { writer.WriteLine("$"); } else { writer.WriteLine("$" + Round(true, data.TotalCostBasis)); } if (refnum == 673) { writer.WriteLine("$" + Round(true, data.SaleProceeds)); } else { writer.WriteLine("$" + Round(true, data.SaleProceeds)); } writer.WriteLine("^"); // end of record }