diff --git a/internal/calc/calc.go b/internal/calc/calc.go index f590c26..2b8000f 100644 --- a/internal/calc/calc.go +++ b/internal/calc/calc.go @@ -6,7 +6,7 @@ import ( "strings" ) -type DebtRecyclingParameters struct { +type Parameters struct { Salary float64 InitialInvestment float64 AnnualInvestment float64 @@ -20,7 +20,7 @@ type DebtRecyclingParameters struct { ReinvestTaxRefunds bool } -type DebtRecyclingData struct { +type Data struct { DebtRecycled []float64 NonDeductibleInterest []float64 TaxDeductibleInterest []float64 @@ -122,8 +122,8 @@ func CAGR(initialValue, finalValue float64, numYears int) float64 { return math.Pow(finalValue/initialValue, 1/float64(numYears)) - 1 } -func DebtRecycling(params DebtRecyclingParameters) (*DebtRecyclingData, error) { - data := &DebtRecyclingData{} +func DebtRecycling(params Parameters) (*Data, error) { + data := &Data{} // Pre-allocate slices with the correct size data.DebtRecycled = make([]float64, params.NumYears) diff --git a/internal/calc/calc_test.go b/internal/calc/calc_test.go index 7874eb3..6151ea8 100644 --- a/internal/calc/calc_test.go +++ b/internal/calc/calc_test.go @@ -17,13 +17,13 @@ func Test_CAGR(t *testing.T) { func Test_DebtRecycling(t *testing.T) { type drTests struct { - params DebtRecyclingParameters - expected *DebtRecyclingData + params Parameters + expected *Data } cases := []drTests{ { - params: DebtRecyclingParameters{ + params: Parameters{ Salary: 150000, InitialInvestment: 100000, AnnualInvestment: 50000, @@ -36,7 +36,7 @@ func Test_DebtRecycling(t *testing.T) { ReinvestDividends: false, ReinvestTaxRefunds: false, }, - expected: &DebtRecyclingData{ + expected: &Data{ DebtRecycled: []float64{ 100000, 150000, @@ -151,7 +151,7 @@ func Test_DebtRecycling(t *testing.T) { }, }, { - params: DebtRecyclingParameters{ + params: Parameters{ Salary: 150000, InitialInvestment: 100000, AnnualInvestment: 50000, @@ -164,7 +164,7 @@ func Test_DebtRecycling(t *testing.T) { ReinvestDividends: true, ReinvestTaxRefunds: false, }, - expected: &DebtRecyclingData{ + expected: &Data{ PortfolioValue: []float64{ 100000, 164160, @@ -230,121 +230,247 @@ func Test_DebtRecycling(t *testing.T) { TotalValue: 1.3293381914617491e+06, }, }, - // { - // params: DebtRecyclingParameters{ - // Salary: 150000, - // InitialInvestment: 100000, - // AnnualInvestment: 50000, - // MortgageSize: 600000, - // MortgageInterestRate: 0.05, - // DividendReturnRate: 0.02, - // CapitalGrowthRate: 0.08, - // NumYears: 10, - // Country: "au", - // ReinvestDividends: false, - // ReinvestTaxRefunds: true, - // }, - // expected: &DebtRecyclingData{ - // PortfolioValue: []float64{ - // 100000, - // 166247.11052631578, - // 239907.86103614027, - // 321567.7011835439, - // 411857.9836250151, - // 511459.60838165897, - // 621106.9662296256, - // 741592.2063071347, - // 873769.8551086588, - // 1.0185618161322233e+06, - // 1.176962781658853e+06, - // }, - // DebtRecycled: []float64{ - // 104247.1105263158, - // 156360.9816677192, - // 208467.21126451236, - // 260564.86634678775, - // 312652.9860666426, - // 364730.58917743387, - // 416796.68277913897, - // 468850.27229695325, - // 520890.3726148719, - // 572916.0202360519, - // }, - // CumulativeDividends: []float64{ - // 2000, - // 5324.942210526316, - // 10123.09943124912, - // 16554.453454919996, - // 24791.6131274203, - // 35020.80529505348, - // 47442.94461964599, - // 62274.78874578868, - // 79750.18584796187, - // 100121.42217060634, - // }, - // CumulativeTaxRefunds: []float64{ - // 4247.11052631579, - // 10608.092194034987, - // 19075.30345854735, - // 29640.169805335096, - // 42293.15587197774, - // 57023.74504941159, - // 73820.42782855057, - // 92670.70012550385, - // 113561.07274037572, - // 136477.09297642764, - // }, - // NetPosition: []float64{ - // -498000, - // -428427.9472631579, - // -349969.0395326106, - // -261877.84536153614, - // -163350.40324756457, - // -53519.58632328757, - // 68549.91084927157, - // 203866.99505292345, - // 353520.0409566206, - // 518683.2383028297, - // }, - // TotalInvested: 600000, - // TotalValue: 2.0033068141620778e+06, - // }, - // }, - // { - // params: DebtRecyclingParameters{ - // Salary: 150000, - // InitialInvestment: 100000, - // AnnualInvestment: 50000, - // MortgageSize: 600000, - // MortgageInterestRate: 0.05, - // DividendReturnRate: 0.02, - // CapitalGrowthRate: 0.08, - // NumYears: 10, - // Country: "au", - // ReinvestDividends: true, - // ReinvestTaxRefunds: true, - // }, - // expected: &DebtRecyclingData{ - - // blah - //}, - // }, - // { - // params: DebtRecyclingParameters{ - // Salary: 150000, - // InitialInvestment: 100000, - // AnnualInvestment: 50000, - // MortgageSize: 600000, - // MortgageInterestRate: 0.05, - // DividendReturnRate: 0.02, - // CapitalGrowthRate: 0.08, - // NumYears: 10, - // Country: "nz", - // ReinvestDividends: false, - // ReinvestTaxRefunds: false, - // }, - // expected: &DebtRecyclingData{}, - // }, + { + params: Parameters{ + Salary: 150000, + InitialInvestment: 100000, + AnnualInvestment: 50000, + MortgageSize: 600000, + MortgageInterestRate: 0.05, + DividendReturnRate: 0.02, + CapitalGrowthRate: 0.08, + NumYears: 10, + Country: "au", + ReinvestDividends: false, + ReinvestTaxRefunds: true, + }, + expected: &Data{ + PortfolioValue: []float64{ + 100000, + 165402, + 237737.16, + 317560.1328000001, + 405469.9434240001, + 502113.53889792017, + 608189.6220097538, + 724452.7917705342, + 851718.015112177, + 990865.4563211512, + 1.1428456928268434e+06, + }, + DebtRecycled: []float64{ + 103150, + 154725, + 206300, + 257875, + 309450, + 361025, + 412600, + 464175, + 515750, + 567325, + }, + CumulativeDividends: []float64{ + 2000, + 5308.04, + 10062.7832, + 16413.985856, + 24523.38472448, + 34565.65550243841, + 46729.44794263349, + 61218.50377804417, + 78252.86408028772, + 98070.17320671075, + }, + CumulativeTaxRefunds: []float64{ + 3150, + 7875, + 14175, + 22050, + 31500, + 42525, + 55125, + 69300, + 85050, + 102375, + }, + NetPosition: []float64{ + -500000, + -434598, + -362262.83999999997, + -282439.8671999999, + -194530.0565759999, + -97886.46110207983, + 8189.622009753832, + 124452.79177053424, + 251718.015112177, + 390865.4563211512, + }, + TotalInvested: 600000, + TotalValue: 1.343290866033554e+06, + }, + }, + { + params: Parameters{ + Salary: 150000, + InitialInvestment: 100000, + AnnualInvestment: 50000, + MortgageSize: 600000, + MortgageInterestRate: 0.05, + DividendReturnRate: 0.02, + CapitalGrowthRate: 0.08, + NumYears: 10, + Country: "au", + ReinvestDividends: true, + ReinvestTaxRefunds: true, + }, + expected: &Data{ + PortfolioValue: []float64{ + 100000, + 167562, + 243689.2992, + 329252.13199872005, + 425209.14860979, + 532616.3981085448, + 652637.224156373, + 786553.1661306606, + 935775.9678095358, + 1.1018608061389846e+06, + 1.2865208640427056e+06, + }, + DebtRecycled: []float64{ + 105150, + 158076.24, + 211173.785984, + 264460.0426399744, + 317954.1829721958, + 371677.3279621709, + 425652.74448312743, + 479906.0633226132, + 534465.5193561907, + 589362.2161227796, + }, + CumulativeDividends: []float64{ + 2000, + 5351.24, + 10225.025984, + 16810.068623974403, + 25314.2515961702, + 35966.579558341095, + 49019.32404146856, + 64750.38736408177, + 83465.90672027248, + 105503.12284305217, + }, + CumulativeTaxRefunds: []float64{ + 3150, + 7875, + 14175, + 22050, + 31500, + 42525, + 55125, + 69300, + 85050, + 102375, + }, + NetPosition: []float64{ + -500000, + -432438, + -356310.7008, + -270747.86800127995, + -174790.85139020998, + -67383.60189145524, + 52637.22415637295, + 186553.16613066057, + 335775.9678095358, + 501860.8061389846, + }, + TotalInvested: 600000, + TotalValue: 1.494398986885758e+06, + }, + }, + { + params: Parameters{ + Salary: 150000, + InitialInvestment: 100000, + AnnualInvestment: 50000, + MortgageSize: 600000, + MortgageInterestRate: 0.05, + DividendReturnRate: 0.02, + CapitalGrowthRate: 0.08, + NumYears: 10, + Country: "nz", + ReinvestDividends: false, + ReinvestTaxRefunds: false, + }, + // HERE + expected: &Data{ + PortfolioValue: []float64{ + 100000, + 162000, + 228960.00000000003, + 301276.80000000005, + 379378.9440000001, + 463729.25952000014, + 554827.6002816001, + 653213.8083041282, + 759470.9129684585, + 874228.5860059352, + 998166.8728864101, + }, + DebtRecycled: []float64{ + 100000, + 150000, + 200000, + 250000, + 300000, + 350000, + 400000, + 450000, + 500000, + 550000, + }, + CumulativeDividends: []float64{ + 2000, + 5240, + 9819.2, + 15844.736, + 23432.31488, + 32706.900070400006, + 43803.45207603201, + 56867.72824211457, + 72057.14650148374, + 89541.71822160245, + }, + CumulativeTaxRefunds: []float64{ + 3349.9999999999995, + 8374.999999999998, + 15074.999999999996, + 23449.999999999996, + 33499.99999999999, + 45224.99999999999, + 58624.99999999999, + 73699.99999999999, + 90449.99999999999, + 108874.99999999999, + }, + NetPosition: []float64{ + -500000, + -438000, + -371040, + -298723.19999999995, + -220621.05599999992, + -136270.74047999986, + -45172.39971839986, + 53213.80830412824, + 159470.9129684585, + 274228.5860059352, + }, + TotalInvested: 600000, + TotalValue: 1.1965835911080125e+06, + }, + }, } for i, c := range cases { dr, err := DebtRecycling(c.params) @@ -422,7 +548,7 @@ func compareAllFloat64Values( got, want []float64, fieldName string, testIndex int, - params DebtRecyclingParameters, + params Parameters, ) { if len(got) != len(want) { t.Errorf( diff --git a/internal/charts/charts.go b/internal/charts/charts.go index bc8d33f..e358718 100644 --- a/internal/charts/charts.go +++ b/internal/charts/charts.go @@ -94,7 +94,7 @@ func ChartToTemplComponent(chart Renderable) templ.Component { } func Positions( - data *calc.DebtRecyclingData, + data *calc.Data, years int, ctx context.Context, ) (*echarts.Line, error) { @@ -184,7 +184,7 @@ func Positions( return line, nil } -func Income(data *calc.DebtRecyclingData, years int, ctx context.Context) (*echarts.Bar, error) { +func Income(data *calc.Data, years int, ctx context.Context) (*echarts.Bar, error) { bar := echarts.NewBar() styleNonce := middleware.GetInlineStyleNonce(ctx) @@ -259,7 +259,7 @@ func Income(data *calc.DebtRecyclingData, years int, ctx context.Context) (*echa return bar, nil } -func Interest(data *calc.DebtRecyclingData, years int, ctx context.Context) (*echarts.Bar, error) { +func Interest(data *calc.Data, years int, ctx context.Context) (*echarts.Bar, error) { bar := echarts.NewBar() styleNonce := middleware.GetInlineStyleNonce(ctx) diff --git a/internal/handlers/calc.go b/internal/handlers/calc.go index 3616b4f..0df331c 100644 --- a/internal/handlers/calc.go +++ b/internal/handlers/calc.go @@ -1,8 +1,8 @@ package handlers import ( + "fmt" "net/http" - "strconv" "debtrecyclingcalc.com/internal/calc" "debtrecyclingcalc.com/internal/charts" @@ -21,73 +21,12 @@ func CalcHandler(w http.ResponseWriter, r *http.Request) { return } - salary, err := strconv.ParseFloat(r.Form.Get("salary"), 64) + params, err := getFormParams(r) if err != nil { - http.Error(w, "error parsing salary", http.StatusBadRequest) + http.Error(w, err.Error(), http.StatusBadRequest) return } - inititalInvestmentAmount, err := strconv.ParseFloat(r.Form.Get("initial_investment"), 64) - if err != nil { - http.Error(w, "error parsing initial investment amount", http.StatusBadRequest) - return - } - - annualInvestmentAmount, err := strconv.ParseFloat(r.Form.Get("annual_investment"), 64) - if err != nil { - http.Error(w, "error parsing annual investment amount", http.StatusBadRequest) - return - } - - mortgageSize, err := strconv.ParseFloat(r.Form.Get("mortgage_size"), 64) - if err != nil { - http.Error(w, "error parsing mortgage size", http.StatusBadRequest) - return - } - - mortgageInterestRate, err := strconv.ParseFloat(r.Form.Get("mortgage_interest_rate"), 64) - if err != nil { - http.Error(w, "error parsing mortgage interest rate", http.StatusBadRequest) - return - } - - dividendReturnRate, err := strconv.ParseFloat(r.Form.Get("dividend_return_rate"), 64) - if err != nil { - http.Error(w, "error parsing dividend return rate", http.StatusBadRequest) - return - } - - capitalGrowthRate, err := strconv.ParseFloat(r.Form.Get("capital_growth_rate"), 64) - if err != nil { - http.Error(w, "error parsing capital growth rate", http.StatusBadRequest) - return - } - - years, err := strconv.Atoi(r.Form.Get("years")) - if err != nil { - http.Error(w, "error parsing years", http.StatusBadRequest) - return - } - - country := r.Form.Get("country") - - reinvestDividends := r.Form.Get("reinvest_dividends") == "on" - reinvestTaxRefunds := r.Form.Get("reinvest_tax_refunds") == "on" - - params := &calc.DebtRecyclingParameters{ - Salary: salary, - InitialInvestment: inititalInvestmentAmount, - AnnualInvestment: annualInvestmentAmount, - MortgageSize: mortgageSize, - MortgageInterestRate: mortgageInterestRate / 100, - DividendReturnRate: dividendReturnRate / 100, - CapitalGrowthRate: capitalGrowthRate / 100, - NumYears: years, - Country: country, - ReinvestDividends: reinvestDividends, - ReinvestTaxRefunds: reinvestTaxRefunds, - } - // if params is empty respond with error data, err := calc.DebtRecycling(*params) if err != nil { @@ -115,6 +54,31 @@ func CalcHandler(w http.ResponseWriter, r *http.Request) { results := templates.Results(data, params, positionsChart, incomeChart, interestChart) + w.Header(). + Set("HX-Push-Url", fmt.Sprintf("/?salary=%.2f"+ + "&initial_investment=%.2f"+ + "&annual_investment=%.2f"+ + "&mortgage_size=%.2f"+ + "&mortgage_interest_rate=%.2f"+ + "÷nd_return_rate=%.2f"+ + "&capital_growth_rate=%.2f"+ + "&years=%d"+ + "&country=%s"+ + "&reinvest_dividends=%t"+ + "&reinvest_tax_refunds=%t", + params.Salary, + params.InitialInvestment, + params.AnnualInvestment, + params.MortgageSize, + params.MortgageInterestRate*100, + params.DividendReturnRate*100, + params.CapitalGrowthRate*100, + params.NumYears, + params.Country, + params.ReinvestDividends, + params.ReinvestTaxRefunds, + )) + err = results.Render(r.Context(), w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/internal/handlers/index.go b/internal/handlers/index.go index 873c95a..11fc4c8 100644 --- a/internal/handlers/index.go +++ b/internal/handlers/index.go @@ -19,7 +19,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) c := templates.NotFound() - err := templates.Layout(c, "Not Fountempl.WithStatus(http.StatusNotFound)d", buildinfo.GitTag, buildinfo.BuildDate). + err := templates.Layout(c, "Not Found", buildinfo.GitTag, buildinfo.BuildDate). Render(r.Context(), w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -28,20 +28,30 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { return } - params := &calc.DebtRecyclingParameters{ + params := &calc.Parameters{ Salary: 150000, InitialInvestment: 100000, AnnualInvestment: 50000, MortgageSize: 600000, - MortgageInterestRate: 0.05, - DividendReturnRate: 0.02, - CapitalGrowthRate: 0.08, + MortgageInterestRate: 0.0500, + DividendReturnRate: 0.0200, + CapitalGrowthRate: 0.0800, NumYears: 10, Country: "au", ReinvestDividends: true, ReinvestTaxRefunds: true, } + query := r.URL.Query() + var err error + if len(query) != 0 { + params, err = getQueryParams(query) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + data, err := calc.DebtRecycling(*params) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -68,7 +78,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { index := templates.Index( templates.Hero(), - templates.Form(), + templates.Form(params), templates.Results(data, params, positionsChart, incomeChart, interestChart), ) diff --git a/internal/handlers/util.go b/internal/handlers/util.go new file mode 100644 index 0000000..a1e44fd --- /dev/null +++ b/internal/handlers/util.go @@ -0,0 +1,146 @@ +package handlers + +import ( + "fmt" + "net/http" + "net/url" + "strconv" + + "debtrecyclingcalc.com/internal/calc" +) + +func getFormParams(r *http.Request) (*calc.Parameters, error) { + parseFloat := func(key string) (float64, error) { + return strconv.ParseFloat(r.Form.Get(key), 64) + } + + parseInt := func(key string) (int, error) { + return strconv.Atoi(r.Form.Get(key)) + } + + salary, err := parseFloat("salary") + if err != nil { + return nil, fmt.Errorf("error parsing salary: %w", err) + } + + initialInvestmentAmount, err := parseFloat("initial_investment") + if err != nil { + return nil, fmt.Errorf("error parsing initial investment amount: %w", err) + } + + annualInvestmentAmount, err := parseFloat("annual_investment") + if err != nil { + return nil, fmt.Errorf("error parsing annual investment amount: %w", err) + } + + mortgageSize, err := parseFloat("mortgage_size") + if err != nil { + return nil, fmt.Errorf("error parsing mortgage size: %w", err) + } + + mortgageInterestRate, err := parseFloat("mortgage_interest_rate") + if err != nil { + return nil, fmt.Errorf("error parsing mortgage interest rate: %w", err) + } + + dividendReturnRate, err := parseFloat("dividend_return_rate") + if err != nil { + return nil, fmt.Errorf("error parsing dividend return rate: %w", err) + } + + capitalGrowthRate, err := parseFloat("capital_growth_rate") + if err != nil { + return nil, fmt.Errorf("error parsing capital growth rate: %w", err) + } + + years, err := parseInt("years") + if err != nil { + return nil, fmt.Errorf("error parsing years: %w", err) + } + + country := r.Form.Get("country") + reinvestDividends := r.Form.Get("reinvest_dividends") == "on" + reinvestTaxRefunds := r.Form.Get("reinvest_tax_refunds") == "on" + + return &calc.Parameters{ + Salary: salary, + InitialInvestment: initialInvestmentAmount, + AnnualInvestment: annualInvestmentAmount, + MortgageSize: mortgageSize, + MortgageInterestRate: mortgageInterestRate / 100, + DividendReturnRate: dividendReturnRate / 100, + CapitalGrowthRate: capitalGrowthRate / 100, + NumYears: years, + Country: country, + ReinvestDividends: reinvestDividends, + ReinvestTaxRefunds: reinvestTaxRefunds, + }, nil +} + +func getQueryParams(query url.Values) (*calc.Parameters, error) { + parseFloat := func(key string) (float64, error) { + return strconv.ParseFloat(query.Get(key), 64) + } + + parseInt := func(key string) (int, error) { + return strconv.Atoi(query.Get(key)) + } + + salary, err := parseFloat("salary") + if err != nil { + return nil, fmt.Errorf("error parsing salary: %w", err) + } + + initialInvestmentAmount, err := parseFloat("initial_investment") + if err != nil { + return nil, fmt.Errorf("error parsing initial investment amount: %w", err) + } + + annualInvestmentAmount, err := parseFloat("annual_investment") + if err != nil { + return nil, fmt.Errorf("error parsing annual investment amount: %w", err) + } + + mortgageSize, err := parseFloat("mortgage_size") + if err != nil { + return nil, fmt.Errorf("error parsing mortgage size: %w", err) + } + + mortgageInterestRate, err := parseFloat("mortgage_interest_rate") + if err != nil { + return nil, fmt.Errorf("error parsing mortgage interest rate: %w", err) + } + + dividendReturnRate, err := parseFloat("dividend_return_rate") + if err != nil { + return nil, fmt.Errorf("error parsing dividend return rate: %w", err) + } + + capitalGrowthRate, err := parseFloat("capital_growth_rate") + if err != nil { + return nil, fmt.Errorf("error parsing capital growth rate: %w", err) + } + + years, err := parseInt("years") + if err != nil { + return nil, fmt.Errorf("error parsing years: %w", err) + } + + country := query.Get("country") + reinvestDividends := query.Get("reinvest_dividends") == "true" + reinvestTaxRefunds := query.Get("reinvest_tax_refunds") == "true" + + return &calc.Parameters{ + Salary: salary, + InitialInvestment: initialInvestmentAmount, + AnnualInvestment: annualInvestmentAmount, + MortgageSize: mortgageSize, + MortgageInterestRate: mortgageInterestRate / 100, + DividendReturnRate: dividendReturnRate / 100, + CapitalGrowthRate: capitalGrowthRate / 100, + NumYears: years, + Country: country, + ReinvestDividends: reinvestDividends, + ReinvestTaxRefunds: reinvestTaxRefunds, + }, nil +} diff --git a/internal/templates/Hero_templ.go b/internal/templates/Hero_templ.go index 20af3f3..9dec81f 100644 --- a/internal/templates/Hero_templ.go +++ b/internal/templates/Hero_templ.go @@ -26,7 +26,7 @@ func Hero() templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Calculate your debt recycling scenario by entering your details below.
Debt recycling involves refinancing or redrawing funds from your offset account to invest in income producing assets. The idea is to turn your \"bad debt\" into tax-deductible \"good debt\".
The key requirement for the interest to qualify as deductible is that the funds must be used to earn income.
While this can be a very effective strategy for building wealth, it's important to consider the risks, such as the possibility of losing money if your investments underperform, or worse, go to zero.
Descriptions and examples for all fields are provided below.
Field | Description | Example |
---|---|---|
Salary | Your annual salary. This, along with any dividends income will be used to determine your bracket. | 150000 |
Initial Investment | The initial amount you will pay down, recycle and invest. This is the available cash you have now, e.g. in the bank or in your offset account. | 100000 |
Annual Investment | The annual amount you will pay down, recycle and invest. This should take into account regular payments as well as any additional payments you expect to make. | 50000 |
Mortgage | The total size of your mortgage loan. | 600000 |
Mortgage Interest Rate | The projected annual mortgage interest rate. | 5 |
Dividend Return Rate | The projected annual dividend return rate. | 2 |
Capital Growth Rate | The projected annual capital growth rate. | 8 |
Years | The number of years to project the scenario. | 10 |
Country | Select the country you live in. Used to determine your tax bracket alobng with your salary and dividend income. | Australia |
Reinvest Dividends | Whether to reinvest your dividend income via debt recycling. | ✔️ |
Reinvest Tax Refunds | Whether to reinvest your tax refunds via debt recycling. | ✔️ |
This calculator is for illustrative purposes only and is not to be misconstrued as financial advice.
It does not take into account your individual needs, goals and objectives.
The results are simply assumptions based on the information provided and are not guaranteed to be accurate.
In particular, it does not take into account any changes in the market, rates of return, interest rates, tax legislation or any extraordinary occurrences that may impact your results.
It also does not take into account franking credits, or the (hopefully) increasing value of your property and the subsequent increase in available equity.
It is better to be roughly right than precisely wrong.
Calculate your debt recycling scenario by entering your details below.
Debt recycling involves refinancing or redrawing funds from your offset account to invest in income producing assets. The idea is to turn your \"bad debt\" into tax-deductible \"good debt\".
The key requirement for the interest to qualify as deductible is that the funds must be used to earn income.
While this can be a very effective strategy for building wealth, it's important to consider the risks, such as the possibility of losing money if your investments underperform, or worse, go to zero.
Descriptions and examples for all fields are provided below.
Field | Description | Example |
---|---|---|
Salary | Your annual salary. This, along with any dividends income will be used to determine your bracket. | 150000 |
Initial Investment | The initial amount you will pay down, recycle and invest. This is the available cash you have now, e.g. in the bank or in your offset account. | 100000 |
Annual Investment | The annual amount you will pay down, recycle and invest. This should take into account regular payments as well as any additional payments you expect to make. | 50000 |
Mortgage | The total size of your mortgage loan. | 600000 |
Mortgage Interest Rate | The projected annual mortgage interest rate. | 5 |
Dividend Return Rate | The projected annual dividend return rate. | 2 |
Capital Growth Rate | The projected annual capital growth rate. | 8 |
Years | The number of years to project the scenario. | 10 |
Country | Select the country you live in. Used to determine your tax bracket according to your combined income from salary and dividends. | Australia |
Reinvest Dividends | Whether to reinvest your dividend income via debt recycling. | ✔️ |
Reinvest Tax Refunds | Whether to reinvest your tax refunds via debt recycling. | ✔️ |
This calculator is for illustrative purposes only and is not to be misconstrued as financial advice.
It does not take into account your individual needs, goals and objectives.
The results are simply assumptions based on the information provided and are not guaranteed to be accurate.
In particular, it does not take into account any changes in the market, rates of return, interest rates, tax legislation or any extraordinary occurrences that may impact your results.
It also does not take into account franking credits, or the (hopefully) increasing value of your property and the subsequent increase in available equity.
It is better to be roughly right than precisely wrong.