コード例 #1
0
        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();
            }
        }
コード例 #2
0
        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
            }
        }
コード例 #3
0
        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);
            }
        }
コード例 #4
0
        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();
            }
        }
コード例 #5
0
        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);
        }