public async Task UnlockNav(DateTime asOfDate, int fundId) { using (PortfolioAceDbContext context = _contextFactory.CreateDbContext()) { Fund fund = context.Funds.Find(fundId); AccountingPeriodsDIM period = context.Periods.Where(p => p.FundId == fund.FundId && p.AccountingDate == asOfDate).FirstOrDefault(); period.isLocked = false; context.Periods.Update(period); List <TransactionsBO> allTransactions; if (fund.NAVFrequency == "Daily") { allTransactions = context.Transactions.Where(t => t.FundId == fund.FundId && t.TradeDate == asOfDate).Include(t => t.TransactionType).ToList(); } else { // TODO issue here . what if the month is that same as the month the fund was launched??? allTransactions = context.Transactions.Where(t => t.FundId == fund.FundId && t.TradeDate.Month == asOfDate.Month).Include(t => t.TransactionType).ToList(); } List <TransactionsBO> deletedTransactions = new List <TransactionsBO>(); foreach (TransactionsBO transaction in allTransactions) { transaction.isLocked = false; if (transaction.TransactionType.TypeClass == "CapitalTrade") { // this removes all the deposits and withdrawals that where booked for today... deletedTransactions.Add(transaction); } } context.Transactions.UpdateRange(allTransactions); context.Transactions.RemoveRange(deletedTransactions); List <TransferAgencyBO> allInvestorActions; if (fund.NAVFrequency == "Daily") { allInvestorActions = context.TransferAgent.Where(ta => ta.FundId == fund.FundId && ta.TransactionDate == asOfDate).ToList(); } else { allInvestorActions = context.TransferAgent.Where(ta => ta.FundId == fund.FundId && ta.TransactionDate.Month == asOfDate.Month).ToList(); } foreach (TransferAgencyBO action in allInvestorActions) { // Sets the subscriptions and redemptions back to pending status. if (action.IssueType == "Subscription") { action.Units = decimal.Zero; action.NAVPrice = decimal.Zero; action.IsNavFinal = false; } else { action.TradeAmount = decimal.Zero; action.NAVPrice = decimal.Zero; action.IsNavFinal = false; } } context.TransferAgent.UpdateRange(allInvestorActions); NAVPriceStoreFACT navPrice = context.NavPriceData.Where(npd => npd.FinalisedDate == asOfDate && npd.FundId == fund.FundId).FirstOrDefault(); context.NavPriceData.Remove(navPrice); IEnumerable <PositionFACT> storedPositions = context.Positions.Where(p => p.PositionDate == asOfDate && p.FundId == fund.FundId); context.Positions.RemoveRange(storedPositions); IEnumerable <InvestorHoldingsFACT> storedHoldings = context.InvestorHoldings.Where(i => i.HoldingDate == asOfDate && i.FundId == fund.FundId); context.InvestorHoldings.RemoveRange(storedHoldings); await context.SaveChangesAsync(); } }
public async Task LockNav(NavValuations navValuations) { using (PortfolioAceDbContext context = _contextFactory.CreateDbContext()) { DateTime asOfDate = navValuations.AsOfDate; int fundId = navValuations.fund.FundId; AccountingPeriodsDIM period = context.Periods.Where(p => p.FundId == fundId && p.AccountingDate == asOfDate).FirstOrDefault(); period.isLocked = true; context.Entry(period).CurrentValues.SetValues(period); List <TransactionsBO> allTransactions; if (navValuations.fund.NAVFrequency == "Daily") { allTransactions = navValuations.fund.Transactions.Where(t => t.TradeDate == asOfDate).ToList(); } else { allTransactions = navValuations.fund.Transactions.Where(t => t.TradeDate.Month == asOfDate.Month).ToList(); } foreach (TransactionsBO transaction in allTransactions) { transaction.isLocked = true; } context.Transactions.UpdateRange(allTransactions); NAVPriceStoreFACT newNavPrice = new NAVPriceStoreFACT { FinalisedDate = asOfDate, Currency = navValuations.fund.BaseCurrency, FundId = fundId, NAVPeriodId = period.PeriodId, SharesOutstanding = navValuations.SharesOutstanding, NetAssetValue = navValuations.NetAssetValue, NAVPrice = navValuations.NetAssetValuePerShare, }; context.NavPriceData.Add(newNavPrice); List <PositionFACT> newPositions = new List <PositionFACT>(); foreach (ValuedSecurityPosition secPosition in navValuations.SecurityPositions) { PositionFACT newPosition = new PositionFACT { PositionDate = secPosition.AsOfDate, SecurityId = secPosition.Position.Security.SecurityId, AssetClassId = secPosition.Position.Security.AssetClassId, FundId = fundId, AverageCost = secPosition.Position.AverageCost, CurrencyId = secPosition.Position.Security.CurrencyId, MarketValue = secPosition.MarketValueBase, Price = secPosition.MarketPrice, Quantity = secPosition.Position.NetQuantity, RealisedPnl = secPosition.Position.RealisedPnL, UnrealisedPnl = secPosition.UnrealisedPnl }; newPositions.Add(newPosition); } foreach (ValuedCashPosition cashPosition in navValuations.CashPositions) { string currencySecSymbol = $"{cashPosition.CashPosition.Currency.Symbol}c"; SecuritiesDIM securitisedCash = context.Securities.AsNoTracking().Where(s => s.Symbol == currencySecSymbol).Include(s => s.AssetClass).FirstOrDefault(); PositionFACT newPosition = new PositionFACT { PositionDate = cashPosition.AsOfDate, SecurityId = securitisedCash.SecurityId, AssetClassId = securitisedCash.AssetClassId, FundId = fundId, AverageCost = 1, CurrencyId = cashPosition.CashPosition.Currency.CurrencyId, MarketValue = cashPosition.MarketValueBase, Price = cashPosition.fxRate, Quantity = cashPosition.CashPosition.NetQuantity, RealisedPnl = 0, UnrealisedPnl = 0 }; newPositions.Add(newPosition); } await context.Positions.AddRangeAsync(newPositions); List <InvestorHoldingsFACT> newHoldings = new List <InvestorHoldingsFACT>(); foreach (ClientHoldingValuation clientHolding in navValuations.ClientHoldings) { InvestorHoldingsFACT newHolding = new InvestorHoldingsFACT { NetValuation = clientHolding.NetValuation, AverageCost = clientHolding.Holding.AverageCost, HighWaterMark = clientHolding.Holding.Investor.HighWaterMark, ManagementFeesAccrued = clientHolding.ManagementFeesAccrued, Units = clientHolding.Holding.Units, PerformanceFeesAccrued = clientHolding.PerformanceFeesAccrued, HoldingDate = asOfDate, FundId = fundId, InvestorId = clientHolding.Holding.Investor.InvestorId }; newHoldings.Add(newHolding); } await context.InvestorHoldings.AddRangeAsync(newHoldings); var pendingTAs = navValuations.fund.TransferAgent.Where(ta => !ta.IsNavFinal && ta.TransactionDate == asOfDate).ToList(); foreach (var pendingTA in pendingTAs) { SecuritiesDIM security = context.Securities.AsNoTracking().Where(s => s.Symbol == $"{pendingTA.Currency}c").First(); int secId = security.SecurityId; int currId = security.CurrencyId; TransactionTypeDIM tradeType; int custodianId = context.Custodians.AsNoTracking().Where(c => c.Name == "Default").First().CustodianId; // FOR NOW TODO if (pendingTA.IssueType == "Subscription") { //add tradeamount.. pendingTA.Units = pendingTA.TradeAmount / navValuations.NetAssetValuePerShare; pendingTA.NAVPrice = navValuations.NetAssetValuePerShare; pendingTA.IsNavFinal = true; tradeType = context.TransactionTypes.AsNoTracking().Where(tt => tt.TypeName == "Deposit").First(); } else { pendingTA.TradeAmount = pendingTA.Units * navValuations.NetAssetValuePerShare; pendingTA.NAVPrice = navValuations.NetAssetValuePerShare; pendingTA.IsNavFinal = true; tradeType = context.TransactionTypes.AsNoTracking().Where(tt => tt.TypeName == "Withdrawal").First(); //reduce units.. } // id need to create deposit and withdrawals here as well as save these updated TAs // I need to set main custodian for a fund where all subs and reds initially go to.TODO TransactionsBO newCashTrade = new TransactionsBO { SecurityId = secId, Quantity = pendingTA.Units, Price = pendingTA.NAVPrice, TradeAmount = pendingTA.TradeAmount, TradeDate = pendingTA.TransactionDate, SettleDate = pendingTA.TransactionSettleDate, CreatedDate = DateTime.Now, LastModified = DateTime.Now, Fees = decimal.Zero, isActive = true, isLocked = true, isCashTransaction = false, FundId = fundId, TransactionTypeId = tradeType.TransactionTypeId, CurrencyId = currId, Comment = pendingTA.IssueType.ToUpper(), CustodianId = custodianId }; context.Transactions.Add(newCashTrade); context.TransferAgent.Update(pendingTA); } await context.SaveChangesAsync(); // Lock all transactions with this trade Date... DONE // Add to Position SnapShot Fact Table... DONE // Lock Period... DONE // Update TransferAgent Fact Table.... DONE // NavPrices DONE } }
public override async Task ExecuteAsync(object parameter) { try { if (_fundInitialiseVM.dgSeedingInvestors.Count == 0) { throw new ArgumentException("Your fund must have initial investors."); } if (_fundInitialiseVM.dgSeedingInvestors.Count != _fundInitialiseVM.dgSeedingInvestors.ToHashSet().Count) { throw new ArgumentException("You must net an investors seed capital."); } Fund updateFund = _fundInitialiseVM.TargetFund; updateFund.IsInitialised = true; string cashSymbol = $"{updateFund.BaseCurrency}c"; SecuritiesDIM security = _staticReferences.GetSecurityInfo(cashSymbol); TransactionTypeDIM tradeType = _staticReferences.GetTransactionType("Deposit"); CustodiansDIM custodian = _staticReferences.GetCustodian(_fundInitialiseVM.Custodian); List <TransferAgencyBO> subscriptions = new List <TransferAgencyBO>(); List <TransactionsBO> transactions = new List <TransactionsBO>(); List <FundInvestorBO> fundInvestors = new List <FundInvestorBO>(); List <InvestorHoldingsFACT> investorHoldings = new List <InvestorHoldingsFACT>(); foreach (SeedingInvestor seedInvestor in _fundInitialiseVM.dgSeedingInvestors) { if (seedInvestor.SeedAmount >= updateFund.MinimumInvestment) { FundInvestorBO fundInvestor = new FundInvestorBO { InceptionDate = updateFund.LaunchDate, FundId = updateFund.FundId, InvestorId = seedInvestor.InvestorId }; // The highwatermark is only applicable if the fund has a highwatermark... fundInvestor.HighWaterMark = (updateFund.HasHighWaterMark) ? _fundInitialiseVM.NavPrice : (decimal?)null; fundInvestors.Add(fundInvestor); InvestorHoldingsFACT investor = new InvestorHoldingsFACT { ManagementFeesAccrued = decimal.Zero, PerformanceFeesAccrued = decimal.Zero, FundId = updateFund.FundId, HoldingDate = updateFund.LaunchDate, InvestorId = seedInvestor.InvestorId, AverageCost = _fundInitialiseVM.NavPrice, Units = seedInvestor.SeedAmount / _fundInitialiseVM.NavPrice, NetValuation = seedInvestor.SeedAmount, }; investor.HighWaterMark = (updateFund.HasHighWaterMark) ? _fundInitialiseVM.NavPrice : (decimal?)null; investorHoldings.Add(investor); // hwm TransferAgencyBO newSubscription = new TransferAgencyBO { TradeAmount = seedInvestor.SeedAmount, NAVPrice = _fundInitialiseVM.NavPrice, TransactionDate = updateFund.LaunchDate, TransactionSettleDate = updateFund.LaunchDate, Currency = updateFund.BaseCurrency, FundId = updateFund.FundId, Fees = 0, IssueType = "Subscription", Units = seedInvestor.SeedAmount / _fundInitialiseVM.NavPrice, IsNavFinal = true, FundInvestor = fundInvestor }; subscriptions.Add(newSubscription); TransactionsBO newTransaction = new TransactionsBO { SecurityId = security.SecurityId, Quantity = seedInvestor.SeedAmount, Price = decimal.One, TradeAmount = seedInvestor.SeedAmount, TradeDate = updateFund.LaunchDate, SettleDate = updateFund.LaunchDate, CreatedDate = DateTime.Now, LastModified = DateTime.Now, Fees = decimal.Zero, isActive = true, isLocked = true, isCashTransaction = false, FundId = updateFund.FundId, TransactionTypeId = tradeType.TransactionTypeId, CurrencyId = security.CurrencyId, Comment = "Initial Subscription", CustodianId = custodian.CustodianId }; transactions.Add(newTransaction); } else { throw new ArgumentException("The seed amount must be greater than the Funds minimum investment"); } } int PeriodId = _staticReferences.GetPeriod(updateFund.LaunchDate, updateFund.FundId).PeriodId; NAVPriceStoreFACT initialNav = new NAVPriceStoreFACT { FinalisedDate = updateFund.LaunchDate, NAVPrice = _fundInitialiseVM.NavPrice, FundId = updateFund.FundId, NetAssetValue = subscriptions.Sum(ni => ni.TradeAmount), SharesOutstanding = subscriptions.Sum(ni => ni.Units), Currency = updateFund.BaseCurrency, NAVPeriodId = PeriodId }; await _investorService.InitialiseFundAction(updateFund, subscriptions, transactions, initialNav, fundInvestors, investorHoldings); _fundInitialiseVM.CloseAction(); } catch (Exception e) { MessageBox.Show(e.Message); } }
public async Task InitialiseFundAction(Fund fund, List <TransferAgencyBO> investorSubscriptions, List <TransactionsBO> transactions, NAVPriceStoreFACT initialNav, List <FundInvestorBO> fundInvestors, List <InvestorHoldingsFACT> investorHoldings) { using (PortfolioAceDbContext context = _contextFactory.CreateDbContext()) { // I will need to create a positions Fact table AND a investor holdings here too. // position is easy sum all the transactions together... decimal cashBalance = transactions.Sum(s => s.TradeAmount); int assetClassId = context.Securities.Find(transactions[0].SecurityId).AssetClassId; PositionFACT cashPosition = new PositionFACT { PositionDate = fund.LaunchDate, AssetClassId = assetClassId, AverageCost = decimal.One, CurrencyId = transactions[0].CurrencyId, UnrealisedPnl = decimal.Zero, FundId = fund.FundId, MarketValue = cashBalance, Price = decimal.One, Quantity = cashBalance, RealisedPnl = decimal.Zero, SecurityId = transactions[0].SecurityId }; await context.Positions.AddAsync(cashPosition); // lock period AccountingPeriodsDIM period = context.Periods.Find(initialNav.NAVPeriodId); period.isLocked = true; context.Periods.Update(period); await context.InvestorHoldings.AddRangeAsync(investorHoldings); // Saves the fund investors that are attached to the transferagent subscriptions await context.FundInvestor.AddRangeAsync(fundInvestors); // Saves the first Nav await context.NavPriceData.AddAsync(initialNav); // saves the investors to the database context.TransferAgent.AddRange(investorSubscriptions); context.Transactions.AddRange(transactions); // Saves the funds state to initialised context.Funds.Update(fund); await context.SaveChangesAsync(); } }
private void Initialisation(List <ClientHolding> clientHoldings) { // This will initialise the calculations decimal securityNav = SecurityPositions.Sum(sp => sp.MarketValueBase); decimal cashNav = CashPositions.Sum(cp => cp.MarketValueBase); this.UnvaluedPositions = SecurityPositions.Where(sp => !sp.IsValuedBase).Count() + CashPositions.Where(cp => !cp.IsValuedBase).Count(); this.GrossAssetValue = securityNav + cashNav; this.SharesOutstanding = this.fund.TransferAgent.Where(ta => ta.IsNavFinal).Sum(ta => ta.Units); // if i make the units absolute i will have to negative units.. this.GrossAssetValuePerShare = Math.Round(this.GrossAssetValue / this.SharesOutstanding, 5); //accruals int accrualPeriods; if (fund.NAVFrequency == "Daily") { accrualPeriods = (!DateTime.IsLeapYear(AsOfDate.Year) ? 365 : 366); } else { accrualPeriods = 12; } // REMEMBER for performance i need to calculate the holding period return // HPR = (Ending Value) / (Previous Value After Cash Flow) – 1. if (AsOfDate.DayOfWeek == DayOfWeek.Monday) { this.ManagementFeeAmount = 3 * ((GrossAssetValue * fund.ManagementFee) / accrualPeriods); // this will then be weighted on the investors.. } else { this.ManagementFeeAmount = (GrossAssetValue * fund.ManagementFee) / accrualPeriods; // this will then be weighted on the investors.. } this.NetAssetValue = GrossAssetValue - this.ManagementFeeAmount; // this is the NAV after management fees. this.NetAssetValuePerShare = this.NetAssetValue / this.SharesOutstanding; // NAV per share after management fees NAVPriceStoreFACT performancePeriodFact = fund.NavPrices.Where(np => np.FinalisedDate.Month == AsOfDate.Month).OrderBy(np => np.FinalisedDate).FirstOrDefault(); //price at beginning of month decimal performancePeriodStartPrice = (performancePeriodFact == null) ? this.NetAssetValuePerShare : performancePeriodFact.NAVPrice; // if theres no price at the beginning of month this value is the beginning of month... decimal totalPerfFee = decimal.Zero; foreach (ClientHolding holding in clientHoldings) { decimal holdingWeighting = holding.Units / this.SharesOutstanding; decimal perfFee = decimal.Zero; decimal perfStartPrice; // This section determines the start price to use for the performance fee. if (holding.Investor.HighWaterMark == null) { perfStartPrice = performancePeriodStartPrice; } else if (holding.Investor.HighWaterMark > this.NetAssetValuePerShare) { perfStartPrice = this.NetAssetValue; // AKA no performance } else { perfStartPrice = (decimal)holding.Investor.HighWaterMark; } //6/5 20%. how caculate gain... decimal gainPercent = (this.NetAssetValuePerShare / perfStartPrice) - 1; decimal gainValue = (this.NetAssetValuePerShare * holding.Units) - (perfStartPrice * holding.Units); if (gainPercent > 0) { //hurdle calc if (fund.HurdleType == "None") { perfFee = gainValue * (fund.PerformanceFee / 12); // by 12 since the fee is payable monthly for now... } else if (fund.HurdleType == "Soft" && gainPercent >= fund.HurdleRate) { perfFee = gainValue * (fund.PerformanceFee / 12); } else if (fund.HurdleType == "Hard" && gainPercent > fund.HurdleRate) { decimal gainPercentWithHurdle = gainPercent - fund.HurdleRate; decimal gainWithHurdle = (this.NetAssetValuePerShare * holding.Units) - (perfStartPrice * holding.Units * (1 + fund.HurdleRate)); perfFee = gainWithHurdle * (fund.PerformanceFee / 12); } } totalPerfFee += perfFee; ClientHoldingValuation holdingValued = new ClientHoldingValuation(holding, this.GrossAssetValuePerShare); holdingValued.ApplyManagementFee(Math.Round(holdingWeighting * this.ManagementFeeAmount, 2)); // this is the weighted average fee holdingValued.ApplyPerformanceFee(Math.Round(perfFee, 2)); this.ClientHoldings.Add(holdingValued); } this.PerformanceFeeAmount = totalPerfFee; this.NetAssetValue = this.NetAssetValue - totalPerfFee; this.NetAssetValuePerShare = Math.Round(this.NetAssetValue / this.SharesOutstanding, 5); }