Exemplo n.º 1
0
        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));
        }
Exemplo n.º 2
0
        private decimal WriteSecurities(IReportWriter writer, List <PieData> data, string prefix, Predicate <Account> filter, out bool hasNoneType)
        {
            hasNoneType = false;
            decimal balance = 0;
            Dictionary <SecurityType, decimal> byType = new Dictionary <SecurityType, decimal>();

            CostBasisCalculator calc = new CostBasisCalculator(this.myMoney, DateTime.Now);

            // compute summary
            foreach (var securityTypeGroup in calc.GetHoldingsBySecurityType(filter))
            {
                SecurityType stype = securityTypeGroup.Key;
                decimal      sb    = 0;
                byType.TryGetValue(stype, out sb);

                foreach (SecurityPurchase sp in securityTypeGroup.Value)
                {
                    sb += sp.MarketValue;
                }
                byType[stype] = sb;
            }

            if (byType.Count > 0)
            {
                foreach (SecurityType st in new SecurityType[] { SecurityType.Bond,
                                                                 SecurityType.MutualFund, SecurityType.Equity, SecurityType.MoneyMarket, SecurityType.ETF, SecurityType.Reit, SecurityType.Futures,
                                                                 SecurityType.None })
                {
                    decimal sb = 0;
                    if (byType.TryGetValue(st, out sb))
                    {
                        string caption = prefix + Security.GetSecurityTypeCaption(st);
                        if (sb > 0)
                        {
                            data.Add(new PieData()
                            {
                                Name = caption, Total = sb
                            });
                            if (st == SecurityType.None)
                            {
                                hasNoneType = true;
                                caption    += "*";
                            }
                        }
                        WriteRow(writer, caption, sb);
                        balance += sb;
                    }
                }
            }
            else
            {
                WriteRow(writer, "N/A", 0);
            }
            return(balance);
        }
Exemplo n.º 3
0
        public void Generate(IReportWriter writer)
        {
            flowwriter = writer as FlowDocumentReportWriter;

            calc = new CostBasisCalculator(this.myMoney, this.reportDate);

            string heading = "Investment Portfolio Summary";

            if (this.account != null)
            {
                heading += " for " + account.Name + " (" + account.AccountId + ")";
            }

            writer.WriteHeading(heading);

            if (reportDate.Date != DateTime.Today)
            {
                writer.WriteSubHeading("As of " + reportDate.Date.AddDays(-1).ToLongDateString());
            }

            totalMarketValue = 0;
            totalGainLoss    = 0;

            // outer table contains 2 columns, left is the summary table, right is the pie chart.
            writer.StartTable();
            writer.StartColumnDefinitions();
            writer.WriteColumnDefinition("Auto", 100, double.MaxValue);
            writer.WriteColumnDefinition("Auto", 100, double.MaxValue);
            writer.EndColumnDefinitions();
            writer.StartRow();
            writer.StartCell();

            writer.StartTable();
            writer.StartColumnDefinitions();

            foreach (double minWidth in new double[] { 300, 100, 100 })
            {
                writer.WriteColumnDefinition("Auto", minWidth, double.MaxValue);
            }
            writer.EndColumnDefinitions();
            writer.StartHeaderRow();
            writer.StartCell();
            writer.WriteParagraph("Security Type");
            writer.EndCell();
            writer.StartCell();
            writer.WriteNumber("Market Value");
            writer.EndCell();
            writer.StartCell();
            writer.WriteNumber("Gain/Loss");
            writer.EndCell();
            writer.EndRow();

            List <SecurityPieData> data = new List <SecurityPieData>();

            decimal cash = this.myMoney.GetInvestmentCashBalance(account);

            if (cash > 0)
            {
                writer.StartRow();
                writer.StartCell();
                writer.WriteParagraph("Cash");
                writer.EndCell();
                writer.StartCell();
                writer.WriteNumber(cash.ToString("C"));
                writer.EndCell();
                writer.EndRow();

                data.Add(new SecurityPieData()
                {
                    Total = RoundToNearestCent(cash),
                    Name  = "Cash"
                });
            }

            totalMarketValue += cash;

            if (account == null)
            {
                WriteSummary(writer, data, "Tax Deferred ", new Predicate <Account>((a) => { return(a.IsTaxDeferred); }));
                WriteSummary(writer, data, "", new Predicate <Account>((a) => { return(!a.IsTaxDeferred); }));
            }
            else
            {
                WriteSummary(writer, data, "", new Predicate <Account>((a) => { return(a == account); }));
            }

            writer.StartHeaderRow();
            writer.StartCell();
            writer.WriteParagraph("Total");
            writer.EndCell();
            writer.StartCell();
            writer.WriteNumber(totalMarketValue.ToString("C"));
            writer.EndCell();
            writer.StartCell();
            writer.WriteNumber(totalGainLoss.ToString("C"));
            writer.EndCell();
            writer.EndRow();
            writer.EndTable();

            writer.EndCell();
            // pie chart
            Chart chart = new Chart();

            chart.MinWidth            = 400;
            chart.MinHeight           = 300;
            chart.BorderThickness     = new Thickness(0);
            chart.Padding             = new Thickness(0);
            chart.Margin              = new Thickness(0, 00, 0, 0);
            chart.VerticalAlignment   = VerticalAlignment.Top;
            chart.HorizontalAlignment = HorizontalAlignment.Left;

            PieSeries series = new PieSeries();

            series.IndependentValueBinding = new Binding("Name");
            series.DependentValueBinding   = new Binding("Total");
            chart.Series.Add(series);
            series.ItemsSource = data;

            writer.StartCell();
            writer.WriteElement(chart);
            writer.EndCell();

            // end the outer table.
            writer.EndTable();

            totalMarketValue = 0;
            totalGainLoss    = 0;

            List <SecuritySale> errors = new List <SecuritySale>(calc.GetPendingSales(new Predicate <Account>((a) => { return(a == account); })));

            if (errors.Count > 0)
            {
                writer.WriteSubHeading("Pending Sales");

                foreach (var sp in errors)
                {
                    writer.WriteParagraph(string.Format("Pending sale of {1} units of '{2}' from account '{0}' recorded on {3}", sp.Account.Name, sp.UnitsSold, sp.Security.Name, sp.DateSold.ToShortDateString()));
                }
            }


            if (account == null)
            {
                WriteDetails(writer, "Tax Deferred ", new Predicate <Account>((a) => { return(a.IsTaxDeferred); }));
                WriteDetails(writer, "", new Predicate <Account>((a) => { return(!a.IsTaxDeferred); }));
            }
            else
            {
                WriteDetails(writer, "", new Predicate <Account>((a) => { return(a == account); }));
            }
        }
Exemplo n.º 4
0
        public void Generate(IReportWriter writer)
        {
            flowwriter = writer as FlowDocumentReportWriter;

            calc = new CostBasisCalculator(this.myMoney, this.reportDate);

            string heading = "Investment Portfolio Summary";

            if (this.selectedGroup != null)
            {
                heading = "Investment Portfolio - " + this.selectedGroup.Type;
            }
            if (this.account != null)
            {
                heading += " for " + account.Name + " (" + account.AccountId + ")";
            }

            writer.WriteHeading(heading);

            if (reportDate.Date != DateTime.Today)
            {
                writer.WriteSubHeading("As of " + reportDate.Date.AddDays(-1).ToLongDateString());
            }

            totalMarketValue = 0;
            totalGainLoss    = 0;

            // outer table contains 2 columns, left is the summary table, right is the pie chart.
            writer.StartTable();
            writer.StartColumnDefinitions();
            writer.WriteColumnDefinition("Auto", 100, double.MaxValue);
            writer.WriteColumnDefinition("Auto", 100, double.MaxValue);
            writer.EndColumnDefinitions();
            writer.StartRow();
            writer.StartCell();

            writer.StartTable();
            writer.StartColumnDefinitions();

            writer.WriteColumnDefinition("30", 30, 30);
            foreach (double minWidth in new double[] { 300, 100, 100 })
            {
                writer.WriteColumnDefinition("Auto", minWidth, double.MaxValue);
            }
            writer.EndColumnDefinitions();

            var series = new ChartDataSeries()
            {
                Name = "Portfolio"
            };
            IList <ChartDataValue> data = series.Values;

            if (account == null)
            {
                if (this.selectedGroup != null)
                {
                    WriteSummary(writer, data, TaxStatus.Taxable, null, null, false);
                }
                else
                {
                    WriteSummary(writer, data, TaxStatus.TaxFree, "Tax Free ", new Predicate <Account>((a) => { return(!a.IsClosed && a.IsTaxFree && IsInvestmentAccount(a)); }), true);
                    WriteSummary(writer, data, TaxStatus.TaxDeferred, "Tax Deferred ", new Predicate <Account>((a) => { return(!a.IsClosed && a.IsTaxDeferred && IsInvestmentAccount(a)); }), true);
                    WriteSummary(writer, data, TaxStatus.Taxable, "Taxable ", new Predicate <Account>((a) => { return(!a.IsClosed && !a.IsTaxDeferred && !a.IsTaxFree && IsInvestmentAccount(a)); }), true);
                }
            }
            else
            {
                WriteSummary(writer, data, account.TaxStatus, "", new Predicate <Account>((a) => { return(a == account); }), false);
            }

            WriteHeaderRow(writer, "Total", totalMarketValue.ToString("C"), totalGainLoss.ToString("C"));
            writer.EndTable();

            writer.EndCell();
            // pie chart
            AnimatingPieChart chart = new AnimatingPieChart();

            chart.Width               = 400;
            chart.Height              = 300;
            chart.BorderThickness     = new Thickness(0);
            chart.Padding             = new Thickness(0);
            chart.Margin              = new Thickness(0, 00, 0, 0);
            chart.VerticalAlignment   = VerticalAlignment.Top;
            chart.HorizontalAlignment = HorizontalAlignment.Left;
            chart.Series              = series;
            chart.ToolTipGenerator    = OnGenerateToolTip;
            chart.PieSliceClicked    += OnPieSliceClicked;

            writer.StartCell();
            writer.WriteElement(chart);
            writer.EndCell();

            // end the outer table.
            writer.EndTable();

            totalMarketValue = 0;
            totalGainLoss    = 0;

            if (this.selectedGroup != null)
            {
                WriteDetails(writer, "", this.selectedGroup);
            }
            else
            {
                List <SecuritySale> errors = new List <SecuritySale>(calc.GetPendingSales(new Predicate <Account>((a) => { return(a == account); })));
                if (errors.Count > 0)
                {
                    writer.WriteSubHeading("Pending Sales");

                    foreach (var sp in errors)
                    {
                        writer.WriteParagraph(string.Format("Pending sale of {1} units of '{2}' from account '{0}' recorded on {3}", sp.Account.Name, sp.UnitsSold, sp.Security.Name, sp.DateSold.ToShortDateString()));
                    }
                }

                if (account == null)
                {
                    WriteDetails(writer, "Tax Free ", new Predicate <Account>((a) => { return(!a.IsClosed && a.IsTaxFree && IsInvestmentAccount(a)); }));
                    WriteDetails(writer, "Tax Deferred ", new Predicate <Account>((a) => { return(!a.IsClosed && a.IsTaxDeferred && IsInvestmentAccount(a)); }));
                    WriteDetails(writer, "Taxable ", new Predicate <Account>((a) => { return(!a.IsClosed && !a.IsTaxFree && !a.IsTaxDeferred && IsInvestmentAccount(a)); }));
                }
                else
                {
                    WriteDetails(writer, "", new Predicate <Account>((a) => { return(a == account); }));
                }
            }
        }
Exemplo n.º 5
0
        public void Generate(IReportWriter writer)
        {
            flowwriter = writer as FlowDocumentReportWriter;

            calc = new CostBasisCalculator(this.myMoney, this.reportDate);

            string heading = "Investment Portfolio Summary";

            if (this.account != null)
            {
                heading += " for " + account.Name + " (" + account.AccountId + ")";
            }

            writer.WriteHeading(heading);

            if (reportDate.Date != DateTime.Today)
            {
                writer.WriteSubHeading("As of " + reportDate.Date.AddDays(-1).ToLongDateString());
            }

            totalMarketValue = 0;
            totalGainLoss    = 0;

            // outer table contains 2 columns, left is the summary table, right is the pie chart.
            writer.StartTable();
            writer.StartColumnDefinitions();
            writer.WriteColumnDefinition("Auto", 100, double.MaxValue);
            writer.WriteColumnDefinition("Auto", 100, double.MaxValue);
            writer.EndColumnDefinitions();
            writer.StartRow();
            writer.StartCell();

            writer.StartTable();
            writer.StartColumnDefinitions();

            foreach (double minWidth in new double[] { 300, 100, 100 })
            {
                writer.WriteColumnDefinition("Auto", minWidth, double.MaxValue);
            }
            writer.EndColumnDefinitions();



            List <SecurityPieData> data = new List <SecurityPieData>();

            if (account == null)
            {
                WriteSummary(writer, data, TaxableIncomeType.None, "Retirement Tax Free ", new Predicate <Account>((a) => { return(!a.IsClosed && !a.IsTaxDeferred && a.Type == AccountType.Retirement); }), true);
                WriteSummary(writer, data, TaxableIncomeType.All, "Retirement ", new Predicate <Account>((a) => { return(!a.IsClosed && a.IsTaxDeferred && a.Type == AccountType.Retirement); }), true);
                WriteSummary(writer, data, TaxableIncomeType.All, "Tax Deferred ", new Predicate <Account>((a) => { return(!a.IsClosed && a.IsTaxDeferred && a.Type == AccountType.Brokerage); }), true);
                WriteSummary(writer, data, TaxableIncomeType.Gains, "", new Predicate <Account>((a) => { return(!a.IsClosed && !a.IsTaxDeferred && a.Type == AccountType.Brokerage); }), true);
            }
            else
            {
                TaxableIncomeType taxableIncomeType;

                if (account.IsTaxDeferred)
                {
                    taxableIncomeType = TaxableIncomeType.All;
                }
                else
                {
                    if (account.Type == AccountType.Retirement)
                    {
                        // Currently treating this combination as tax free
                        taxableIncomeType = TaxableIncomeType.None;
                    }
                    else
                    {
                        taxableIncomeType = TaxableIncomeType.Gains;
                    }
                }

                WriteSummary(writer, data, taxableIncomeType, "", new Predicate <Account>((a) => { return(a == account); }), false);
            }

            WriteheaderRow(writer, "Total", totalMarketValue.ToString("C"), totalGainLoss.ToString("C"));
            writer.EndTable();

            writer.EndCell();
            // pie chart
            Chart chart = new Chart();

            chart.MinWidth            = 400;
            chart.MinHeight           = 300;
            chart.BorderThickness     = new Thickness(0);
            chart.Padding             = new Thickness(0);
            chart.Margin              = new Thickness(0, 00, 0, 0);
            chart.VerticalAlignment   = VerticalAlignment.Top;
            chart.HorizontalAlignment = HorizontalAlignment.Left;

            PieSeries series = new PieSeries();

            series.IndependentValueBinding = new Binding("Name");
            series.DependentValueBinding   = new Binding("Total");
            chart.Series.Add(series);
            series.ItemsSource = data;

            writer.StartCell();
            writer.WriteElement(chart);
            writer.EndCell();

            // end the outer table.
            writer.EndTable();

            totalMarketValue = 0;
            totalGainLoss    = 0;

            List <SecuritySale> errors = new List <SecuritySale>(calc.GetPendingSales(new Predicate <Account>((a) => { return(a == account); })));

            if (errors.Count > 0)
            {
                writer.WriteSubHeading("Pending Sales");

                foreach (var sp in errors)
                {
                    writer.WriteParagraph(string.Format("Pending sale of {1} units of '{2}' from account '{0}' recorded on {3}", sp.Account.Name, sp.UnitsSold, sp.Security.Name, sp.DateSold.ToShortDateString()));
                }
            }

            if (account == null)
            {
                WriteDetails(writer, "Retirement Tax Free ", new Predicate <Account>((a) => { return(!a.IsClosed && !a.IsTaxDeferred && a.Type == AccountType.Retirement); }));
                WriteDetails(writer, "Retirement ", new Predicate <Account>((a) => { return(!a.IsClosed && a.IsTaxDeferred && a.Type == AccountType.Retirement); }));
                WriteDetails(writer, "Tax Deferred ", new Predicate <Account>((a) => { return(!a.IsClosed && a.IsTaxDeferred && a.Type == AccountType.Brokerage); }));
                WriteDetails(writer, "", new Predicate <Account>((a) => { return(!a.IsClosed && !a.IsTaxDeferred && a.Type == AccountType.Brokerage); }));
            }
            else
            {
                WriteDetails(writer, "", new Predicate <Account>((a) => { return(a == account); }));
            }
        }
Exemplo n.º 6
0
        private decimal WriteSecurities(IReportWriter writer, IList <ChartDataValue> data, string prefix, Predicate <Account> filter, out bool hasNoneType)
        {
            hasNoneType = false;
            decimal balance = 0;
            Dictionary <SecurityType, decimal>       byType       = new Dictionary <SecurityType, decimal>();
            Dictionary <SecurityType, SecurityGroup> groupsByType = new Dictionary <SecurityType, SecurityGroup>();

            CostBasisCalculator calc = new CostBasisCalculator(this.myMoney, DateTime.Now);

            // compute summary
            foreach (var securityTypeGroup in calc.GetHoldingsBySecurityType(filter))
            {
                SecurityType stype = securityTypeGroup.Type;
                decimal      sb    = 0;
                byType.TryGetValue(stype, out sb);

                foreach (SecurityPurchase sp in securityTypeGroup.Purchases)
                {
                    sb += sp.MarketValue;
                }
                byType[stype]       = sb;
                groupsByType[stype] = securityTypeGroup;
            }

            if (byType.Count > 0)
            {
                foreach (SecurityType st in new SecurityType[] { SecurityType.Bond,
                                                                 SecurityType.MutualFund, SecurityType.Equity, SecurityType.MoneyMarket, SecurityType.ETF, SecurityType.Reit, SecurityType.Futures,
                                                                 SecurityType.Private, SecurityType.None })
                {
                    decimal sb = 0;
                    if (byType.TryGetValue(st, out sb))
                    {
                        var    color   = GetRandomColor();
                        string caption = Security.GetSecurityTypeCaption(st);
                        if (sb > 0)
                        {
                            string tooltip = caption;
                            if (!string.IsNullOrEmpty(prefix))
                            {
                                tooltip = prefix + " " + tooltip;
                            }
                            data.Add(new ChartDataValue()
                            {
                                Label = tooltip, Value = (double)sb, Color = color, UserData = groupsByType[st]
                            });
                            if (st == SecurityType.None)
                            {
                                hasNoneType = true;
                                caption    += "*";
                            }
                        }
                        WriteRow(writer, color, caption, sb);
                        balance += sb;
                    }
                }
            }
            else
            {
                WriteRow(writer, GetRandomColor(), "N/A", 0);
            }
            return(balance);
        }