public IEnumerable<Dividend> CalculateDividends(int year, Deposit deposit, IEnumerable<GeneralMeeting> generalMeetings) { Guard.AgainstNull(() => deposit, () => generalMeetings); var generalMeetingsInYear = generalMeetings.Where(p => p.MeetingDate.Year == year && p.DividendRate > 0); foreach (var generalMeeting in generalMeetingsInYear) { var position = _stockPositionCalculator.GetStockPosition( deposit, generalMeeting.Stock.Splitted.Id, generalMeeting.MeetingDate); var quantity = position.Quantity; if (quantity > 0) { var createdDividend = deposit.GetDividendForGeneralMeeting(generalMeeting.Id); bool isCreated = false; bool isDifferent = false; if (createdDividend != null) { isCreated = true; isDifferent = createdDividend.Quantity != quantity; } var dividend = new Dividend(0, quantity, generalMeeting.Id, deposit.Id, generalMeeting, isCreated, isDifferent); yield return dividend; } } }
public IEnumerable<StockPosition> GetStockPositions(Deposit deposit) { var stockPositions = from trade in deposit.Trades group trade by trade.Stock.Splitted.Id into g let quantity = g.Sum(t => t.QuantitySignedSplitted()) where quantity > 0 select new StockPosition(g.Key, quantity); return stockPositions; }
public StockPosition GetStockPosition(Deposit deposit, int stockId, DateTime date) { Guard.AgainstNull(() => deposit); var quantity = deposit.Trades .Where(p => p.TradeDate.Date <= date.Date && p.Stock.Splitted.Id == stockId) .Sum(t => t.QuantitySignedSplitted(date)); return new StockPosition(stockId, quantity); }
public void UpdateDeposit(Deposit deposit) { Guard.AgainstNull(() => deposit); deposit.Validate(); using (var uow = _uowFactory.Create()) { uow.Repo<IDepositRepository>().Update(deposit); uow.SaveChanges(); } }
public Deposit CreateDeposit(Deposit deposit) { Guard.AgainstNull(() => deposit); deposit.Validate(); using (var uow = _uowFactory.Create()) { uow.Repo<IDepositRepository>().Add(deposit); uow.SaveChanges(); return deposit; } }
public Trade(int id, bool isBuy, int quantity, decimal price, decimal commission, DateTime tradeDate, int depositId, int stockId, Deposit deposit, Stock stock) { Id = id; IsBuy = isBuy; Quantity = quantity; Price = price; Commission = commission; TradeDate = tradeDate; DepositId = depositId; StockId = stockId; Deposit = deposit; Stock = stock; }
public TradeInfoDto CalculateTradeInfo(bool isBuy, decimal tradePrice, int quantity, int stockId, Deposit deposit) { Guard.AgainstNull(() => deposit); //TODO: Modtag som parameter når vi har realtime-kurser. var realtimePrice = 150m; var stockQuantity = 0; var depositValue = 0m; var originalTradeValue = _stockPositionCalculator.CalculateMarketValue(quantity, tradePrice); var currentTradeValue = _stockPositionCalculator.CalculateMarketValue(quantity, realtimePrice); var stockValueInDeposit = (isBuy ? currentTradeValue : -currentTradeValue); var depositPositions = _stockPositionCalculator.GetStockPositions(deposit).ToArray(); if (depositPositions.Any()) { //TODO: Use real price when the system supports up-to-date prices. var fakePrice = 200; var existingPosition = depositPositions.SingleOrDefault(p => p.StockId == stockId); if (existingPosition != null) { stockQuantity = existingPosition.Quantity; stockValueInDeposit += _stockPositionCalculator.CalculateMarketValue(existingPosition.Quantity, fakePrice); //existingPosition.Value; } depositValue += depositPositions.Sum(p => _stockPositionCalculator.CalculateMarketValue(p.Quantity, fakePrice));// p.Value); } // Ved køb skal værdien på den nye handel lægges til den eksisterende værdi fordi vi er interesserede i // at finde ud af hvordan depotet vil komme til at se ud efter handlen er lavet. if (isBuy) depositValue += currentTradeValue; var tradeShareInDeposit = 0m; var stockShareInDeposit = 0m; if (depositValue > 0) { tradeShareInDeposit = BeregnPctAndel(currentTradeValue, depositValue); stockShareInDeposit = BeregnPctAndel(stockValueInDeposit, depositValue); } return new TradeInfoDto( stockQuantity, originalTradeValue, currentTradeValue, stockValueInDeposit, tradeShareInDeposit, stockShareInDeposit); }
private DepositInfoDTO GetDepositInfo(Deposit deposit) { var stockPositions = _stockPositionCalculator.GetStockPositions(deposit).ToArray(); var sellableStockIds = stockPositions.Select(p => p.StockId).ToArray(); return new DepositInfoDTO(deposit, sellableStockIds, stockPositions); }
// TODO: Skal nok have andet navn. Kaldes når ny handel er oprettet og depotet skal opdateres pga. det. public DepositInfoDTO Refresh(Deposit deposit, Trade trade) { deposit.AddTrade(trade); return GetDepositInfo(deposit); }
private Tuple<decimal, decimal, decimal, List<YearlyReportDividendDTO>> GetReportDividendData(int year, Deposit deposit) { var reportDividends = new List<YearlyReportDividendDTO>(); decimal dividendDanishStocks = 0; decimal dividendForeignStocks = 0; decimal dividendInvestmentFonds = 0; var dividends = from dividend in deposit.Dividends where dividend.GeneralMeeting.MeetingDate.Year == year select dividend; foreach (var dividend in dividends) { var dividendPayment = dividend.DividendPayment; //TODO: Settings med skattesatser.. var taxPayment = dividendPayment * 0.27m; //TODO: Refactor conditional to polymorphism? string stockType = "??"; switch (dividend.GeneralMeeting.Stock.StockType) { case StockTypes.Aktie: stockType = "Dansk aktie"; dividendDanishStocks += dividendPayment; break; case StockTypes.UdenlandskAktie: stockType = "Udl. aktie"; dividendForeignStocks += dividendPayment; break; case StockTypes.Investeringsbevis: stockType = "Inv. bevis"; dividendInvestmentFonds += dividendPayment; break; case StockTypes.TegningsretAktie: stockType = "Tegningsret"; break; } var reportDividend = new YearlyReportDividendDTO ( date: dividend.GeneralMeeting.MeetingDate, description: dividend.GeneralMeeting.Stock.Name, stockType: stockType, quantity: dividend.Quantity, dividendRate: dividend.GeneralMeeting.DividendRate, grossDividendPayment: dividendPayment, taxPayment: taxPayment, netDividendPayment: dividendPayment - taxPayment ); reportDividends.Add(reportDividend); } return Tuple.Create(dividendDanishStocks, dividendForeignStocks, dividendInvestmentFonds, reportDividends); }
private IEnumerable<YearlyReportStockGroupDTO> GetReportStockGroups(int year, Deposit deposit) { if (!deposit.Trades.Any()) yield break; var tradeGroups = from trade in deposit.Trades where trade.TradeDate.Year <= year group trade by trade.Stock.Splitted into grp select new { StockName = grp.Key.Name, Trades = grp.OrderBy(p => p.TradeDate) }; foreach (var tradeGroup in tradeGroups) { var stockGroupItems = new List<YearlyReportStockGroupItemDTO>(); int buyQuantitySum = 0; decimal buyMarketvalueSum = 0; int taxFreeBuyQuantitySum = 0; decimal taxFreeBuyMarketvalueSum = 0; foreach (var trade in tradeGroup.Trades) { decimal marketvalue = trade.MarketvalueInclCommission; int quantity = trade.QuantitySplitted(); decimal buyValue = 0; decimal sellValue = 0; decimal tradeProfitLoss = 0; if (trade.IsBuy) { if (trade.TradeDate.Year < 2006) { // Aktier købt inden 2006 sælges skattefrit, så beregner den skattefrie beholdning. taxFreeBuyQuantitySum += quantity; taxFreeBuyMarketvalueSum += marketvalue; } else { buyQuantitySum += quantity; buyMarketvalueSum += marketvalue; } buyValue = marketvalue; } else { decimal salesPrice = marketvalue / quantity; Tuple<decimal, decimal> tradeProfitLossItem = null; // Skattefri gevinst bruges ikke til noget pt. // Den burde nok indgå i total indkomst, men ikke i skatteberegningerne. Tuple<decimal, decimal> tradeTaxFreeProfitLossItem = null; if (taxFreeBuyQuantitySum > 0) { int taxedQuantity = quantity - taxFreeBuyQuantitySum; if (taxedQuantity > 0) { // Delvist skattefrit salg. Har både en skattefri og en skattepligtig gevinst. tradeTaxFreeProfitLossItem = CalculateProfitLoss(ref taxFreeBuyMarketvalueSum, ref taxFreeBuyQuantitySum, taxFreeBuyQuantitySum, salesPrice); tradeProfitLossItem = CalculateProfitLoss(ref buyMarketvalueSum, ref buyQuantitySum, taxedQuantity, salesPrice); } else { // Fuldt skattefrit salg. Har kun en skattefri gevinst. tradeTaxFreeProfitLossItem = CalculateProfitLoss(ref taxFreeBuyMarketvalueSum, ref taxFreeBuyQuantitySum, quantity, salesPrice); } } else { // Fuldt skattepligtigt salg. tradeProfitLossItem = CalculateProfitLoss(ref buyMarketvalueSum, ref buyQuantitySum, quantity, salesPrice); } sellValue = marketvalue; buyValue = tradeProfitLossItem.Item1; tradeProfitLoss = tradeProfitLossItem.Item2; } if (trade.TradeDate.Year == year) { var stockGroupItem = new YearlyReportStockGroupItemDTO ( description: trade.IsBuy ? "Køb" : "Salg", date: trade.TradeDate.Date, quantity: trade.Quantity, sellValue: sellValue, buyValue: buyValue, profitLoss: tradeProfitLoss, isProfit: tradeProfitLoss >= 0, isSale: !trade.IsBuy ); stockGroupItems.Add(stockGroupItem); } } if (stockGroupItems.Any()) { var stockProfitLoss = stockGroupItems.Sum(p => p.ProfitLoss); var stockGroup = new YearlyReportStockGroupDTO( header: tradeGroup.StockName, profitLoss: stockProfitLoss, isProfit: stockProfitLoss >= 0, items: stockGroupItems); yield return stockGroup; } } }
public YearlyReportDTO GetYearlyReport(int year, bool isMarried, Deposit deposit) { // Gevinst og tab var reportStockGroups = GetReportStockGroups(year, deposit); Func<Func<YearlyReportStockGroupDTO, bool>, decimal> sumProfitLoss = p => reportStockGroups.Where(p).Sum(o => o.ProfitLoss); decimal totalProfit = sumProfitLoss(p => p.ProfitLoss > 0); decimal totalLoss = sumProfitLoss(p => p.ProfitLoss < 0); decimal totalProfitLoss = totalProfit + totalLoss; // Udbytte var reportDividendData = GetReportDividendData(year, deposit); decimal dividendDanishStocks = reportDividendData.Item1; decimal dividendForeignStocks = reportDividendData.Item2; decimal dividendInvestmentFonds = reportDividendData.Item3; IEnumerable<YearlyReportDividendDTO> reportDividends = reportDividendData.Item4; // Total decimal grossReturnNoLossDeduction = totalProfit + dividendDanishStocks + dividendForeignStocks; decimal grossReturn = grossReturnNoLossDeduction + totalLoss; string grossReturnDescription = $"{totalProfitLoss:N2} + {dividendDanishStocks:N2} + {dividendForeignStocks:N2}"; // Tax. If correct settings are not available don't calculate anything. decimal taxPayment = -1; string taxPaymentDescription = string.Empty; decimal netReturn = -1; decimal taxPaymentNoLossDeduction = -1; decimal lossDeduction = -1; bool isMissingTaxValueSettings = false; TaxValues taxValues = null; if (_settingsProvider.Instance.YearlyTaxValues?.TryGetValue(year, out taxValues) == true) { var taxInfo = CalculateTax(grossReturn, isMarried, taxValues); taxPayment = taxInfo.Item1; taxPaymentDescription = taxInfo.Item2; netReturn = grossReturn - taxPayment; taxPaymentNoLossDeduction = CalculateTax(grossReturnNoLossDeduction, isMarried, taxValues).Item1; lossDeduction = taxPaymentNoLossDeduction - taxPayment; } else { isMissingTaxValueSettings = true; } return new YearlyReportDTO ( profit: totalProfit, loss: totalLoss, profitLoss: totalProfitLoss, isProfit: totalProfitLoss >= 0, dividendDanishStocks: dividendDanishStocks, dividendForeignStocks: dividendForeignStocks, dividendInvestmentFonds: dividendInvestmentFonds, grossReturn: grossReturn, grossReturnDescription: grossReturnDescription, taxPayment: taxPayment, taxPaymentDescription: taxPaymentDescription, netReturn: netReturn, isPositiveReturn: netReturn >= 0, lossDeduction: lossDeduction, isMissingTaxValueSettings: isMissingTaxValueSettings, stockGroups: reportStockGroups, dividends: reportDividends ); }
public DepositInfoDTO(Deposit deposit, IEnumerable<int> sellableStockIds, IEnumerable<StockPosition> stockPositions) { Deposit = deposit; SellableStockIds = sellableStockIds; StockPositions = stockPositions; }
private DepositViewModel GetDepositViewModel(Deposit deposit) { return new DepositViewModel { Id = deposit.Id, Description = deposit.Description, IdentityNumber = deposit.IdentityNumber, DepositType = deposit.DepositType }; }