public void Apply(IPortfolioTransaction transaction, IHolding holding, ICashAccount cashAccount) { var aquisition = transaction as Aquisition; if (aquisition == null) { throw new ArgumentException("Expected transaction to be an Aquisition"); } if (!aquisition.Stock.IsEffectiveAt(aquisition.Date)) { throw new StockNotActiveException("Stock is not active"); } decimal cost = aquisition.Units * aquisition.AveragePrice; decimal amountPaid = cost + aquisition.TransactionCosts; decimal costBase = amountPaid; holding.AddParcel(aquisition.Date, aquisition.Date, aquisition.Units, amountPaid, costBase, transaction); if (aquisition.CreateCashTransaction) { var asxCode = aquisition.Stock.Properties[aquisition.Date].AsxCode; cashAccount.Transfer(aquisition.Date, -cost, String.Format("Purchase of {0}", asxCode)); if (aquisition.TransactionCosts > 0.00m) { cashAccount.FeeDeducted(aquisition.Date, aquisition.TransactionCosts, String.Format("Brokerage for purchase of {0}", asxCode)); } } }
public void Apply(IPortfolioTransaction transaction, IHolding holding, ICashAccount cashAccount) { var returnOfCapital = transaction as ReturnOfCapital; if (returnOfCapital == null) { throw new ArgumentException("Expected transaction to be an ReturnOfCapital"); } if (!holding.IsEffectiveAt(returnOfCapital.RecordDate)) { throw new NoSharesOwnedException("No holdings"); } // Reduce cost base of parcels decimal totalAmount = 0; foreach (var parcel in holding.Parcels(returnOfCapital.RecordDate)) { var costBaseReduction = parcel.Properties[returnOfCapital.RecordDate].Units * returnOfCapital.Amount; parcel.Change(returnOfCapital.RecordDate, 0, 0.00m, costBaseReduction, transaction); totalAmount += costBaseReduction; } if (returnOfCapital.CreateCashTransaction) { var asxCode = returnOfCapital.Stock.Properties[returnOfCapital.RecordDate].AsxCode; cashAccount.Transfer(returnOfCapital.Date, totalAmount, String.Format("Return of capital for {0}", asxCode)); } }
public ParcelAudit(Date date, int unitCountChange, decimal costBaseChange, decimal amountChange, IPortfolioTransaction transaction) { Date = date; UnitCountChange = unitCountChange; CostBaseChange = costBaseChange; AmountChange = amountChange; Transaction = transaction; }
public void Apply(IPortfolioTransaction transaction, IHolding holding, ICashAccount cashAccount) { var incomeReceived = transaction as IncomeReceived; if (incomeReceived == null) { throw new ArgumentException("Expected transaction to be an IncomeReceived"); } if (!holding.IsEffectiveAt(incomeReceived.RecordDate)) { throw new NoSharesOwnedException("No holdings"); } // Handle any tax deferred amount recieved if (incomeReceived.TaxDeferred > 0) { var parcels = holding.Parcels(incomeReceived.RecordDate); // Apportion amount between parcels var apportionedAmounts = parcels.Select(x => new ApportionedCurrencyValue() { Units = x.Properties[incomeReceived.RecordDate].Units }).ToArray(); MathUtils.ApportionAmount(incomeReceived.TaxDeferred, apportionedAmounts); // Reduce cost base of parcels var i = 0; foreach (var parcel in parcels) { parcel.Change(incomeReceived.RecordDate, 0, 0.00m, apportionedAmounts[i++].Amount, transaction); } } if (incomeReceived.CreateCashTransaction) { var asxCode = incomeReceived.Stock.Properties[incomeReceived.RecordDate].AsxCode; cashAccount.Transfer(incomeReceived.Date, incomeReceived.CashIncome, String.Format("Distribution for {0}", asxCode)); } var drpCashBalance = holding.DrpAccount.Balance(incomeReceived.Date); var drpAccountCredit = incomeReceived.DrpCashBalance - drpCashBalance; if (drpAccountCredit != 0.00m) { holding.AddDrpAccountAmount(incomeReceived.Date, drpAccountCredit); } }
public void Apply(IPortfolioTransaction transaction, IHolding holding, ICashAccount cashAccount) { var openingBalance = transaction as OpeningBalance; if (openingBalance == null) { throw new ArgumentException("Expected transaction to be an OpeningBalance"); } if (!openingBalance.Stock.IsEffectiveAt(openingBalance.Date)) { throw new StockNotActiveException("Stock is not active"); } holding.AddParcel(openingBalance.Date, openingBalance.AquisitionDate, openingBalance.Units, openingBalance.CostBase, openingBalance.CostBase, transaction); }
public void Apply(IPortfolioTransaction transaction, IHolding holding, ICashAccount cashAccount) { var costBaseAdjustment = transaction as CostBaseAdjustment; if (costBaseAdjustment == null) { throw new ArgumentException("Expected transaction to be an CostBaseAdjustment"); } if (!holding.IsEffectiveAt(costBaseAdjustment.Date)) { throw new NoSharesOwnedException("No holdings"); } // Adjust cost base of parcels foreach (var parcel in holding.Parcels(costBaseAdjustment.Date)) { var costBaseReduction = (parcel.Properties[costBaseAdjustment.Date].CostBase * (1 - costBaseAdjustment.Percentage)).ToCurrency(RoundingRule.Round); parcel.Change(costBaseAdjustment.Date, 0, 0.00m, -costBaseReduction, transaction); } }
public void Apply(IPortfolioTransaction transaction, IHolding holding, ICashAccount cashAccount) { var cashTransaction = transaction as CashTransaction; if (cashTransaction == null) { throw new ArgumentException("Expected transaction to be a CashTransaction"); } var description = ""; if (cashTransaction.Comment != "") { description = cashTransaction.Comment; } else if (cashTransaction.CashTransactionType == BankAccountTransactionType.Deposit) { description = "Deposit"; } else if (cashTransaction.CashTransactionType == BankAccountTransactionType.Fee) { description = "Fee"; } else if (cashTransaction.CashTransactionType == BankAccountTransactionType.Interest) { description = "Interest"; } else if (cashTransaction.CashTransactionType == BankAccountTransactionType.Transfer) { description = "Transfer"; } else if (cashTransaction.CashTransactionType == BankAccountTransactionType.Withdrawl) { description = "Withdrawl"; } cashAccount.AddTransaction(cashTransaction.Date, cashTransaction.Amount, description, cashTransaction.CashTransactionType); }
public void Apply(IPortfolioTransaction transaction, IHolding holding, ICashAccount cashAccount) { var unitCountAdjustment = transaction as UnitCountAdjustment; if (unitCountAdjustment == null) { throw new ArgumentException("Expected transaction to be an UnitCountAdjustment"); } if (!holding.IsEffectiveAt(unitCountAdjustment.Date)) { throw new NoSharesOwnedException("No holdings"); } // Adjust unit count of parcels var ratio = (decimal)unitCountAdjustment.NewUnits / (decimal)unitCountAdjustment.OriginalUnits; foreach (var parcel in holding.Parcels(unitCountAdjustment.Date)) { var units = (int)Math.Ceiling(parcel.Properties[unitCountAdjustment.Date].Units * ratio); parcel.Change(unitCountAdjustment.Date, units, 0.00m, 0.00m, transaction); } }
private void OnCgtEventOccured(Date eventDate, IReadOnlyStock stock, int units, decimal costBase, decimal amountReceived, decimal capitalGain, CgtMethod cgtMethod, IPortfolioTransaction transaction) { // Make a temporary copy of the event to avoid possibility of // a race condition if the last subscriber unsubscribes // immediately after the null check and before the event is raised. var handler = CgtEventOccurred; if (handler != null) { var e = new CgtEventArgs() { EventDate = eventDate, Stock = stock, Units = units, CostBase = costBase, AmountReceived = amountReceived, CapitalGain = capitalGain, CgtMethod = cgtMethod, Transaction = transaction }; handler(this, e); } }
public void DisposeOfParcel(Guid parcelId, Date date, int units, decimal amount, decimal capitalGain, CgtMethod cgtMethod, IPortfolioTransaction transaction) { if (!_Parcels.TryGetValue(parcelId, out var parcel)) { throw new ArgumentException("Parcel is not part of this holding"); } var parcelProperties = parcel.Properties[date]; if (units > parcelProperties.Units) { throw new NotEnoughSharesForDisposal("Not enough shares in parcel"); } // Adjust Parcel decimal costBaseChange; decimal amountChange; if (units == parcelProperties.Units) { amountChange = parcelProperties.Amount; costBaseChange = parcelProperties.CostBase; } else { amountChange = (parcelProperties.Amount * ((decimal)units / parcelProperties.Units)).ToCurrency(RoundingRule.Round); costBaseChange = (parcelProperties.CostBase * ((decimal)units / parcelProperties.Units)).ToCurrency(RoundingRule.Round); } parcel.Change(date, -units, -amountChange, -costBaseChange, transaction); // Adjust holding var holdingProperties = Properties[date]; HoldingProperties newProperties; if (units == holdingProperties.Units) { End(date); newProperties = new HoldingProperties(0, 0.00m, 0.00m); } else { newProperties = new HoldingProperties(holdingProperties.Units - units, holdingProperties.Amount - amountChange, holdingProperties.CostBase - costBaseChange); } _Properties.Change(date, newProperties); OnCgtEventOccured(date, Stock, units, costBaseChange, amount, capitalGain, cgtMethod, transaction); }
public IParcel AddParcel(Date date, Date aquisitionDate, int units, decimal amount, decimal costBase, IPortfolioTransaction transaction) { var parcel = new Parcel(Guid.NewGuid(), date, aquisitionDate, new ParcelProperties(units, amount, costBase), transaction); _Parcels.Add(parcel.Id, parcel); var exisingProperties = Properties[date]; var newProperties = new HoldingProperties(exisingProperties.Units + units, exisingProperties.Amount + amount, exisingProperties.CostBase + costBase); _Properties.Change(date, newProperties); return(parcel); }
public void Change(Date date, int unitChange, decimal amountChange, decimal costBaseChange, IPortfolioTransaction transaction) { if (!EffectivePeriod.Contains(date)) { throw new EffectiveDateException("The parcel is not effective at that date"); } var parcelProperties = _Properties[date]; var newUnits = parcelProperties.Units + unitChange; var newAmount = parcelProperties.Amount + amountChange; var newCostBase = parcelProperties.CostBase + costBaseChange; if (newUnits < 0) { throw new ArgumentException("Units cannot be changed to be less than 0"); } if (newAmount < 0) { throw new ArgumentException("Amount cannot be changed to be less than 0"); } if (newCostBase < 0) { throw new ArgumentException("Costbase cannot be changed to be less than 0"); } ParcelProperties newParcelProperties; if (newUnits == 0) { End(date); newParcelProperties = new ParcelProperties(0, 0.00m, 0.00m); } else { newParcelProperties = new ParcelProperties(newUnits, newAmount, newCostBase); } _Properties.Change(date, newParcelProperties); _Audit.Add(new ParcelAudit(date, unitChange, costBaseChange, amountChange, transaction)); }
public Parcel(Guid id, Date fromDate, Date aquisitionDate, ParcelProperties properties, IPortfolioTransaction transaction) : base(id) { Start(fromDate); AquisitionDate = aquisitionDate; _Properties.Change(fromDate, properties); _Audit.Add(new ParcelAudit(fromDate, properties.Units, properties.CostBase, properties.Amount, transaction)); }
public void Apply(IPortfolioTransaction transaction, IHolding holding, ICashAccount cashAccount) { var disposal = transaction as Disposal; if (disposal == null) { throw new ArgumentException("Expected transaction to be a Disposal"); } if (!holding.IsEffectiveAt(disposal.Date)) { throw new NoSharesOwnedException("No holdings"); } if (holding.Properties[disposal.Date].Units < disposal.Units) { throw new NotEnoughSharesForDisposal("Not enough shares for disposal"); } // Determine which parcels to sell based on CGT method decimal amountReceived = (disposal.Units * disposal.AveragePrice) - disposal.TransactionCosts; var cgtCalculator = new CgtCalculator(); var parcelsSold = cgtCalculator.Calculate(holding.Parcels(disposal.Date), disposal.Date, disposal.Units, amountReceived, CgtCalculator.GetCgtComparer(disposal.Date, disposal.CgtMethod)); // Dispose of select parcels if (disposal.Stock is StapledSecurity) { /* foreach (ParcelSold parcelSold in cgtCalculation.ParcelsSold) * { * var childStocks = _StockQuery.GetChildStocks(stock.Id, disposal.TransactionDate); * * // Apportion amount based on NTA of child stocks * var amountsReceived = PortfolioUtils.ApportionAmountOverChildStocks(childStocks, disposal.TransactionDate, parcelSold.AmountReceived, _StockQuery); * * int i = 0; * foreach (var childStock in childStocks) * { * var childParcels = _PortfolioQuery.GetParcelsForStock(childStock.Id, disposal.TransactionDate, disposal.TransactionDate); * * var childParcel = childParcels.First(x => x.PurchaseId == parcelSold.Parcel.PurchaseId); * DisposeOfParcel(unitOfWork, childParcel, disposal.TransactionDate, parcelSold.UnitsSold, amountsReceived[i].Amount, transaction.Id); * * i++; * } * * }; */ } else { foreach (var parcelSold in parcelsSold) { holding.DisposeOfParcel(parcelSold.Parcel.Id, disposal.Date, parcelSold.UnitsSold, parcelSold.AmountReceived, parcelSold.CapitalGain, parcelSold.CgtMethod, transaction); } } if (disposal.CreateCashTransaction) { var cost = disposal.Units * disposal.AveragePrice; var asxCode = disposal.Stock.Properties[disposal.Date].AsxCode; cashAccount.Transfer(disposal.Date, cost, String.Format("Sale of {0}", asxCode)); if (disposal.TransactionCosts > 0.00m) { cashAccount.FeeDeducted(disposal.Date, disposal.TransactionCosts, String.Format("Brokerage for sale of {0}", asxCode)); } } }