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

//@version=5

// @description Provides functions for calculating risk metrics
library("RiskTools", true)

// @function Calculates what is the percentage drop from a reference price
// @param start Starting price before the drop occurred
// @param result Resulting price to which the percentage drop occurred
// @returns Percentage drop from "start" to "result"
export pctDrop(float start, float result) =>
    100 * (1 - (result / start))

// @function Calculates a starting price given a resulting price and a percentage drop to that price
// @param pctdrop Percentage drop
// @param result Resulting price to which the percentage drop occurred
// @returns The starting price from which a percentage drop "pctDrop" gave a "result"
export priceBeforeDrop(float pctdrop, float result) =>
    result / (1 - pctdrop/100)

// @function Calculates drop zone as an integer representing some multiple of the "zoning size"
// @param price The current price from which you want to calculate the drop zone
// @param masource The source series used in the SMA calculation from which the floor price is determined
// @param malength_days The length (in days) used in the SMA calculation from which the floor price is determined
// @param window_days The lookback period (in days) from which to calculate the floor price
// @param zonesize The increment by which each new zone is larger than the next
// @param minuszone The zone number to use if price is below the floor price. If ommitted it defaults to zero (0)
// @param firstzone The first valid zone number. If ommitted it defaults to the first zonesize increment
// @param lastzone The last zone number where price is above the final zone.
// @returns The zone identifier as a multiple of the zone size in string format
export dropzone(float price, float masource=close, simple int malength_days=50, simple int window_days=800, simple int zonesize=20, simple int minuszone=0, simple int firstzone=0, simple int lastzone=80, simple int overzone=80) =>
    _timemult     = 86400 / timeframe.in_seconds(timeframe.period)
	var float[] _afloorWindow = array.new_float()
	if array.size(_afloorWindow) >= int(_timemult*window_days)
	    array.pop(_afloorWindow)
	array.unshift(_afloorWindow, ta.sma(masource, int(_timemult*malength_days)))
	_floor = array.min(_afloorWindow)
    _dropDistance = pctDrop(start=price, result=_floor)
    _zone =  _dropDistance == 0 ? zonesize : math.ceil(_dropDistance/zonesize)*zonesize
    _zone := switch
    	firstzone >=0 and _dropDistance <= 0 => minuszone
    	firstzone >=0 and _zone < firstzone  => firstzone
    	overzone   >0 and _zone > lastzone   => lastzone
    	=> _zone
    str.tostring(_zone)
