public override ConsequenceResult Calculate(ConsequenceRequest request) { if (request.TriggerMode != TriggerType.OneTime || request.InitialAmount < LowerThreshold || request.InitialAmount > UpperThreshold) return null; try { Money localizedCost = ExchangeRates.CurrentRates.ConvertToGiven(this.Cost, request.InitialAmount.CurrencyCode); decimal units = (request.InitialAmount / localizedCost).Value; return new ConsequenceResult (this, request, new Units (units), FormatCaption (Caption, new Dictionary<string,string> { {"Cost", Cost.ToString()} } ), this.Image, (units >= LowerResultLimit && units <= UpperResultLimit) ); } catch (Exception ex) { Console.WriteLine ("{0} thrown when calculating units for price: {1}", ex.GetType().Name, ex.Message); return new ConsequenceResult (this, request, null, "Oops, something went wrong in this calculator", this.Image, false); } }
public override ConsequenceResult Calculate(ConsequenceRequest request) { if (Cost == 0m) return null; if (request.TriggerMode == TriggerType.OneTime) return null; try { Money localizedCost = ExchangeRates.CurrentRates.ConvertToGiven(this.Cost, request.InitialAmount.CurrencyCode); Money perDay = (request.InitialAmount / request.DaysPerPeriod); decimal unitsPerDay = (perDay / localizedCost).Value; decimal unitsPerPeriod = unitsPerDay * ConsequenceRequest.DaysPerUnit [Period]; return new ConsequenceResult (this, request, new Units (unitsPerPeriod), FormatCaption (this.Caption, new Dictionary<string,string> { {"Cost", this.Cost.ToString ()} } ), this.Image, (unitsPerPeriod >= LowerResultLimit && unitsPerPeriod <= UpperResultLimit)); } catch (Exception ex) { Console.WriteLine ("{0} thrown when computing Units per Period: {1}", ex.GetType().Name, ex.Message); return new ConsequenceResult (this, request, null, "Oops, something went wrong in this calculator", this.Image, false); } }
public void ComputeConsequences (ConsequenceRequest request) { if (computeWorker != null && computeWorker.IsBusy) computeWorker.CancelAsync (); computeWorker = new BackgroundWorker (); computeWorker.DoWork += HandleDoWork; computeWorker.RunWorkerCompleted += HandleRunWorkerCompleted; computeWorker.WorkerSupportsCancellation = true; computeWorker.RunWorkerAsync (request); }
public override ConsequenceResult Calculate(ConsequenceRequest request) { if (Cost == 0m) return null; if (request.TriggerMode != TriggerType.OneTime) return null; try { Money localizedCost = ExchangeRates.CurrentRates.ConvertToGiven(this.Cost, request.InitialAmount.CurrencyCode); decimal serviceUnits = (request.InitialAmount / localizedCost).Value; decimal serviceSeconds = ConsequenceRequest.SecondsPerUnit [UnitForCost] * serviceUnits; if (UnitsPerDay > 0) { // If the service is not provided 24 hours/day, then spread the time over whole days. // This is mainly used to calculate how long it would take to earn $n, given an 8-hour day decimal maxSecondsPerDay = UnitsPerDay * ConsequenceRequest.SecondsPerUnit[UnitForCost]; decimal wholeDays = Math.Floor(serviceSeconds / maxSecondsPerDay); decimal wholeDaysInSeconds = wholeDays * ConsequenceRequest.SecondsPerUnit[TimeUnit.Day]; serviceSeconds = wholeDaysInSeconds + (serviceSeconds - (wholeDays * maxSecondsPerDay)); } if (serviceSeconds > (decimal)TimeSpan.MaxValue.TotalSeconds) return new ConsequenceResult (this, request, new OverflowMessage (), "Try reducing the spending amount", this.Image, false); else return new ConsequenceResult (this, request, new Time (TimeSpan.FromSeconds((double)serviceSeconds)), this.FormatCaption (this.Caption, new Dictionary<string,string> { {"Unit", this.UnitForCost.ToString ()}, {"Cost", this.Cost.ToString ()} }), this.Image, (serviceSeconds <= (decimal)TimeSpan.MaxValue.TotalSeconds && serviceUnits >= LowerResultLimit && serviceUnits <= UpperResultLimit)); } catch (Exception ex) { Console.WriteLine ("{0} thrown when calculating Time Of Service: {1}", ex.GetType().Name, ex.Message); return new ConsequenceResult (this, request, null, "Oops, something went wrong in this calculator", this.Image, false); } }
public override ConsequenceResult Calculate(ConsequenceRequest request) { Money total = request.TriggerMode == TriggerType.OneTime ? request.InitialAmount : request.AmountAfter(TimeSpan.FromDays((Double)ConsequenceRequest.DaysPerUnit[TimeUnit.Year] * this.Years)); return new ConsequenceResult(this, request, total * PercentAsDecimal(this.Amount), FormatCaption(this.Caption, new Dictionary<string,string> { {"Percentage", String.Format("{0}%", this.Amount)}, {"Years", Years.ToString()} }), this.Image, true); }
public ConsequenceResult(ACalculator calculator, ConsequenceRequest request, object computedValue, string formattedCaption, Image image, bool recommended) { this.Calculator = calculator; this.Request = request; this.ComputedValue = computedValue; this.FormattedCaption = formattedCaption; this.Image = image; this.Recommended = recommended; }
public override ConsequenceResult Calculate(ConsequenceRequest request) { if (request.TriggerMode != TriggerType.OneTime) return null; Money convertedAmount = ExchangeRates.CurrentRates.ConvertToGiven(request.InitialAmount, this.CurrencyCode); return new ConsequenceResult(this, request, convertedAmount, this.FormatCaption (this.Caption, new Dictionary<string,string> { {"CurrencyCode", this.CurrencyCode}, {"CurrencyName", ExchangeRates.GetCurrencyName(this.CurrencyCode)} }), this.Image, true); }
public override ConsequenceResult Calculate(ConsequenceRequest request) { if (Cost == null || Cost == 0m) return null; if (request.TriggerMode == TriggerType.OneTime) return null; if (this.Cost < request.InitialAmount) return null; try { Money localizedCost = ExchangeRates.CurrentRates.ConvertToGiven(this.Cost, request.InitialAmount.CurrencyCode); decimal givenUnitsUntil = (localizedCost / request.InitialAmount).Value; if (givenUnitsUntil * request.DaysPerPeriod < MaxDays) { TimeSpan timeUntil = TimeSpan.FromDays((double)(givenUnitsUntil * request.DaysPerPeriod)); return new ConsequenceResult (this, request, new Time (timeUntil), this.FormatCaption (this.Caption, new Dictionary<string,string> { {"Cost", this.Cost.ToString ()} } ), this.Image, (timeUntil.TotalDays >= (double)LowerResultLimit && timeUntil.TotalDays <= (double)UpperResultLimit)); } else { return new ConsequenceResult(this, request, new OverflowMessage(), "Try reducing the target price", this.Image, false); } } catch (Exception ex) { Console.WriteLine("{0} thrown when computing Time Until: {1}", ex.GetType().Name, ex.Message); return new ConsequenceResult (this, request, null, "Oops, something went wrong in this calculator", this.Image, false); } }
public override ConsequenceResult Calculate(ConsequenceRequest request) { if (Cost == 0m) return null; if (request.TriggerMode == TriggerType.OneTime) return null; decimal perYear = request.AmountAfter (oneYear); decimal units = perYear / Cost; if (units >= LowerResultLimit && units <= UpperResultLimit) return new ConsequenceResult (this, request, new Units(units), FormatCaption (this.Caption, new Dictionary<string,string> { {"Cost", this.Cost.ToString ()} }), this.ImageName); else return null; }
public static IEnumerable<InvestmentInstallment> InvestmentSchedule(ConsequenceRequest request, Investment calculator) { decimal annualRate = ACalculator.PercentAsDecimal (calculator.Rate); decimal compoundingsPerYear = ACalculator.CompoundingsPerYear (calculator.Compounding); decimal periodRate = annualRate / compoundingsPerYear; Money balance = 0m; if (request.TriggerMode != TriggerType.OneTime) { Money periodicInvestment = request.InitialAmount; decimal totalInvestments = request.InvestmentsPerYear * calculator.Years; decimal compoundingsPerInvestment = compoundingsPerYear / request.InvestmentsPerYear; var baseSchedule = from p in Enumerable.Range (1, (int)Math.Floor (totalInvestments)) let newBalance = balance += periodicInvestment let earnings = ((balance * periodRate) * compoundingsPerInvestment) select new InvestmentInstallment { Installment = p, Investment = periodicInvestment, Earnings = earnings, Balance = balance += earnings }; return baseSchedule; } else { decimal totalCompoundings = compoundingsPerYear * calculator.Years; balance = request.InitialAmount; var baseSchedule = from p in Enumerable.Range (1, (int)Math.Floor (totalCompoundings)) let earnings = balance * periodRate select new InvestmentInstallment { Installment = p, Investment = p == 1 ? request.InitialAmount : 0, Earnings = earnings, Balance = balance += earnings }; return baseSchedule; } }
public override ConsequenceResult Calculate(ConsequenceRequest request) { if (request.InitialAmount <= 0 || request.TriggerMode == TriggerType.OneTime) return null; try { int annualPayments = CompoundingsPerYear (PaymentFrequency); decimal ratePerPeriod = PercentAsDecimal (Rate) / annualPayments; Money paymentPerInstallment = (request.InitialAmount * (decimal)(InvestmentsPerYear (request.TriggerMode))) / annualPayments; Money totalPayment = paymentPerInstallment * Installments; Money maxLoanAmount = null; if (Rate > 0) maxLoanAmount = ((paymentPerInstallment / ratePerPeriod) * (1 - (1 / Financials.Pow ((1 + ratePerPeriod), Installments)))); else maxLoanAmount = totalPayment; return new ConsequenceResult (this, request, maxLoanAmount, new TabularResult (request.Summary, string.Format ("Amortization of a {0} loan at {1}%", maxLoanAmount, Rate), this), FormatCaption (this.Caption, new Dictionary<string,string> { {"Installments", Installments.ToString ()}, {"TotalPayment", totalPayment.ToString ()}, {"TotalInterest", (totalPayment - maxLoanAmount).ToString()} } ), this.Image, (maxLoanAmount >= LowerResultLimit && maxLoanAmount <= UpperResultLimit)); } catch (Exception ex) { Console.WriteLine("{0} thrown when computing spending power: {1}", ex.GetType().Name, ex.Message); return new ConsequenceResult (this, request, null, "Oops, something went wrong in this calculator", this.Image, false); } }
public override ConsequenceResult Calculate(ConsequenceRequest request) { if (Threshold == 0m) return null; decimal upper = Threshold + (Threshold * 0.10m); decimal lower = Threshold - (Threshold * 0.10m); if (request.InitialAmount >= lower && request.InitialAmount <= upper) return new ConsequenceResult (this, Threshold, this.Caption, this.ImageName); for (int i = 1; i <= Limit; i++) { decimal accumilated = request.AmountAfter (TimeSpan.FromDays (30 * i)); if (accumilated >= lower && accumilated <= upper) return new ConsequenceResult (this, Threshold, this.FormatCaption (this.Caption, new Dictionary<string,string> { {"Months", i.ToString ()}, {"Threshold", this.Threshold.ToString ()} }), this.ImageName); } return null; }
public abstract ConsequenceResult Calculate(ConsequenceRequest request);
public override ConsequenceResult Calculate(ConsequenceRequest request) { // For some of the scenarios below we have to sacrifice a bit of accuracy in order to produce results // within a few milliseconds. EG: A savings account may compound monthly but calculate interest daily, but // that would require more CPU time than we can afford. if (request.InitialAmount == 0m) return null; int annualCompoundings = CompoundingsPerYear (Compounding); int compoundingPeriods = Years * annualCompoundings; decimal ratePerPeriod = PercentAsDecimal (Rate) / annualCompoundings; decimal investmentsPerYear = InvestmentsPerYear (request.TriggerMode); Money result = null; try { if (request.TriggerMode == TriggerType.OneTime) { // A one-time investment means we can use the compound interest formula result = request.InitialAmount * Financials.Pow (1 + ratePerPeriod, compoundingPeriods); } else if (annualCompoundings == investmentsPerYear) { // Compounding frequency is the same as the investment frequency, so we can // use a mathematical series. // Described here: http://mathdude.quickanddirtytips.com/math-dude-web-bonus.aspx decimal seriesSum = Enumerable.Range (1, compoundingPeriods).Sum (x => Financials.Pow (1 + ratePerPeriod, x)); result = request.InitialAmount.Value * seriesSum; } else if (annualCompoundings > investmentsPerYear) { // We need to take the sum of sub-periods and apply the standard compound interest formula to each decimal investmentPeriods = Years * investmentsPerYear; int compoundingsPerInvestment = ((int)Math.Floor (annualCompoundings / investmentsPerYear)); Money sum = request.InitialAmount * Financials.Pow (1 + ratePerPeriod, compoundingsPerInvestment); for (int i = 1; i <= investmentPeriods - 1; i++) sum = (sum + (request.InitialAmount * Financials.Pow (1 + ratePerPeriod, compoundingsPerInvestment))); result = sum; } else if (investmentsPerYear > annualCompoundings) { // Apply the interest to the midpoint of the previous + next balance for each compounding period decimal investmentsPerPeriod = (decimal)(investmentsPerYear / annualCompoundings); decimal amountPerPeriod = (investmentsPerPeriod * request.InitialAmount.Value); decimal sum = 0; for (int i = 1; i <= compoundingPeriods; i++) { sum = sum + amountPerPeriod + ((sum + amountPerPeriod / 2) * (decimal)ratePerPeriod); } result = sum; } } catch (OverflowException oex) { Console.WriteLine (String.Format ("Number overflow in investment calculator: {0}", oex.Message)); return new ConsequenceResult (this, request, new OverflowMessage (), "Try reducing the interest rate or years", this.Image, false ); } catch (Exception ex) { Console.WriteLine (String.Format ("{0} thrown in investment calculator: {1}", ex.GetType ().Name, ex.Message)); return new ConsequenceResult (this, request, null, "Oops, something went wrong in this calculation", this.Image, false ); } return new ConsequenceResult (this, request, result, new TabularResult(request.Summary, string.Format ("{0} invested at {1:0.00}%", request.Summary, Rate), this), FormatMyCaption (), this.Image, (result >= this.LowerResultLimit && result <= this.UpperResultLimit)); }
private void CreateNewConsequence(TriggerType mode, Template template) { if (template == null) return; SubProfile userProfile = Profile.GetSubProfile("user"); if (userProfile == null) { userProfile = SubProfile.Create("user"); Profile.AddSubProfile("user", userProfile); } XElement unwrappedTemplate = template.GetUsableTemplateDefinition(); if (unwrappedTemplate != null) { ACalculator calculator = userProfile.AddConsequenceFromDefinition(unwrappedTemplate); calculator.SortOrder = -1; // Switch to a compatible mode if (!calculator.TriggersOn.Contains(this.CurrentMode)) if (calculator.TriggersOn.Contains(TriggerType.Repeating)) this.CurrentMode = TriggerType.PerDay; else if (calculator.TriggersOn.Contains(TriggerType.OneTime)) this.CurrentMode = TriggerType.OneTime; // Create a prototype request in order to load the Details View in edit mode Money prototypeAmount = this.CurrentAmount; if (prototypeAmount == null) prototypeAmount = 100; ConsequenceRequest prototypeRequest = new ConsequenceRequest(prototypeAmount, this.CurrentMode); ConsequenceResult prototypeResult = calculator.Calculate(prototypeRequest); DisplayConsequenceDetails(prototypeResult); } }
public override ConsequenceResult Calculate(ConsequenceRequest request) { if (request.InitialAmount == 0 || request.InitialAmount < LowerThreshold || request.InitialAmount > UpperThreshold) return null; try { Money localizedMinPayment = ExchangeRates.CurrentRates.ConvertToGiven(MinimumPayment, request.InitialAmount.CurrencyCode); var amortization = Financials.Amortization (request.InitialAmount, CompoundingsPerYear (Compounding), PercentAsDecimal (Rate), PercentAsDecimal (MinPayPercent), localizedMinPayment, PayoffMode); int installments = 0; Money totalInterest = new Money(0, request.InitialAmount.CurrencyCode); foreach (var i in amortization) { installments++; totalInterest += i.Interest; } Money payoff = request.InitialAmount + totalInterest; return new ConsequenceResult (this, request, payoff, new TabularResult ( request.Summary, String.Format ("Amortization of a {0} loan at {1}%", request.InitialAmount, Rate), this), FormatCaption (this.Caption, new Dictionary<string,string> { {"Periods", installments.ToString()}, {"Interest", totalInterest.ToString()} } ), this.Image, (payoff >= LowerResultLimit && payoff <= UpperResultLimit)); } catch (Exception ex) { Console.WriteLine("{0} thrown when computing loan payoff: {1}", ex.GetType().Name, ex.Message); return new ConsequenceResult(this, request, null, "Oops, something went wrong in this calculator", this.Image, false); } }