Rebalance Oscillator
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © hugodanielcom

//@version=5
import hugodanielcom/TradingPortfolio/1 as portfolio 
import hugodanielcom/PureRebalance/3    as rebalancer

indicator("Rebalance Locations")
float percentage = input.float(defval = 60,           title = "Rebalance Token %", minval = 0.01, maxval = 100.0, tooltip="Percentage of Token / Money.\n60 means the targeted balance is 60% token and 40% money")
float capital    = input.float(defval = 10000,        title = "Initial capital",   minval = 1, tooltip="The total portfolio value to start with. A higher value makes the rebalancer more sensitive to peaks/bottoms and act more often")
float precision  = input.float(defval = 10,           title = "Action Threshold",  tooltip="Bigger values make the rebalance hold more and become less sensitive to peaks/bottoms and act less often.")
int   from_month = input.int(defval = 10,             title = "From Month",        minval = 1, maxval = 12)
int   from_day   = input.int(defval = 1,              title = "From Day",          minval = 1, maxval = 31)
int   from_year  = input.int(defval = 2021,           title = "From Year",         minval = 2000)
bool  show_date  = input.bool(defval = true, title = "Show Date Range")

// Backtest start window
start     = timestamp(from_year, from_month, from_day)
// Function returns true when the current bar time is
// "within the window of time" for this indicator
window()  => time >= start

var float[] portfolio = portfolio.init()

// Initialization starts by doing a rebalance at the first bar within
// the window
var bool init1 = true
if window() and init1
    // Only do this once, for the first bar, set init1 to false to disallow
    // this block from running again
    init1 := false
    first_rebalance_amount = rebalancer.rebalance(close, 0.0, capital, percentage / 100.0)
    portfolio.set_balance(portfolio, first_rebalance_amount, capital - (first_rebalance_amount * close))

// This is where the rebalance gets done
float amount = rebalancer.rebalance(close, portfolio.crypto(portfolio), portfolio.fiat(portfolio), percentage / 100)
// Adjust the precision to the current token value,
// This way the amount to rebalance is adjusted to be meaningful in very high
// value tokens, or very low value tokens
float token_adjustment = 2000 / (capital / close)

bool is_rebalance_buying = amount * token_adjustment > precision
bool is_rebalance_selling = amount * token_adjustment < -precision

// Update the rebalance portfolio, this will allow it to work properly in
// the next bar
if window()
    if is_rebalance_buying
        portfolio.buy(portfolio, amount, close)
        
    if is_rebalance_selling
        portfolio.sell(portfolio, math.abs(amount), close)
    
// Paint the bg color if this bar is within the window and the amount to
// rebalance fits within the precision
color rebalance_buy_color = color.new(color.blue, 70)
color rebalance_sell_color = color.new(color.red, 70)
bgcolor(window() and is_rebalance_buying ? rebalance_buy_color : na, title="Buy Bar")
bgcolor(window() and is_rebalance_selling ? rebalance_sell_color : na, title="Sell Bar")

// Plot the signal
plot(-amount * token_adjustment, "Signal", color=color.teal)
// Plot the precision bands
band1 = hline(precision, "Upper Band", color=#787B86)
bandm = hline(0, "Middle Band", color=color.new(#787B86, 50))
band0 = hline(-precision, "Lower Band", color=#787B86)
fill(band1, band0, color=color.rgb(126, 87, 194, 90), title="Background")
