StochRSI in Amibroker to Match TOS StochRSI

I have had Amibroker since 2017 but because of my 24/7 job I had I could not find time to learn it. I also had this major issue. I have used Ameritrade's TOS for trading and backtesting for 15 years. My exit is TOS's StochRSI. I have not been able to find an StochRSI for Amibroker that incorporates all the parameters that are used in TOS. This causes my exit signals to be correct sometimes in Amibroker and very far off in other instances. I am new to AFL so please excuse anything I might say that's not correct. All of the StochRSI's I have found or attempted in Amibroker do not work and I believe that is because they do not have all the parameters TOS uses. Can someone show me how to create an Amibroker StochRSI that uses all of the paraments TOS uses? Here is TOS StchRSI:

TD Ameritrade IP Company, Inc. (c) 2008-2022

declare lower;
input RSI_length = 14;
input over_bought = 80;
input over_sold = 20;
input RSI_average_type = AverageType.WILDERS;
input RSI_price = close;
input KPeriod = 14;
input DPeriod = 3;
input slowing_period = 1;
input averageType = AverageType.SIMPLE;
input showBreakoutSignals = {default "No", "On FullK", "On FullD", "On FullK &
FullD"};
def RSI = RSI(price = RSI_price, length = RSI_length, averageType =
RSI_average_type);
plot FullK = StochasticFull(over_bought, over_sold, KPeriod, DPeriod, RSI, RSI, RSI,
slowing_period, averageType).FullK;
plot FullD = StochasticFull(over_bought, over_sold, KPeriod, DPeriod, RSI, RSI, RSI,
slowing_period, averageType).FullD;
plot OverBought = over_bought;
plot OverSold = over_sold;
def upK = FullK crosses above OverSold;
def upD = FullD crosses above OverSold;
def downK = FullK crosses below OverBought;
def downD = FullD crosses below OverBought;
plot UpSignal;
plot DownSignal;
switch (showBreakoutSignals) {
case "No":
UpSignal = Double.NaN;
DownSignal = Double.NaN;
case "On FullK":
UpSignal = if upK then OverSold else Double.NaN;
DownSignal = if downK then OverBought else Double.NaN;
case "On FullD":
UpSignal = if upD then OverSold else Double.NaN;
DownSignal = if downD then OverBought else Double.NaN;
case "On FullK & FullD":
UpSignal = if upK or upD then OverSold else Double.NaN;
DownSignal = if downK or downD then OverBought else Double.NaN;
}

Hi @Astroidor
It seems the exact formula for StochasticFull() is not public but it should be close to the following:

_SECTION_BEGIN("StochRSI");

RSI_length = Param("RSI length", 14, 2, 100, 1);
over_bought = Param("overbought level", 80, 5, 95, 5);
over_sold = Param("oversold level", 20, 5, 95, 5);
RSI_average_type = ParamList("RSI Avg Type", "SMA|EMA|WMA|Wilders|DEMA", 3);
RSI_price = ParamField("RSI price");
KPeriod = Param("K period", 14, 2, 100);
DPeriod = Param("P period", 3, 1, 100);
slowing_period = Param("slowing period", 1, 1, 20);
averageType = ParamList("average type", "SMA|EMA", 0);
showBreakoutSignals = ParamList("show breakout signals", "No|On FullK|On FullD|On FullK FullD");

function RSIx(price, length, type) {
	local D, Up, Dn, MUp, MDn;
	
	D = price - Ref(price,-1);
	Up = IIf(D > 0, D, 0); 
	Dn = IIf(D < 0, -D, 0);
	switch(type) {
		case "SMA": MUp = MA(Up, length); MDn = MA(Dn, length); break;
		case "EMA": MUp = EMA(Up, length); MDn = EMA(Dn, length);  break;
		case "WMA": MUp = WMA(Up, length); MDn = WMA(Dn, length);  break;
		case "Wilders": MUp = Wilders(Up, length); MDn = Wilders(Dn, length);  break;
		case "DEMA": MUp = WMA(Up, length); MDn = WMA(Dn, length);  break;
		default: MUp = MDn = 0;
	}
	return 100*SafeDivide(MUp, (MUp+MDn), 0.5);
}

// https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/R-S/StochasticFull
// NB: in AFL only few params are used in each function. You could use profiles:
// function StochasticFullK(k, R) and function StochasticFullD(k, d, R) and function StochasticFullSlow(k, d, R, slow, type) 
// Even better, pass StochasticFullK result as a param to StochasticFullD to fasten computation:
// function StochasticFullK(k, R), function StochasticFullD(d, StochFK), function StochasticFullSlow(StochFD, slow, type) 
// I've kept the same profile to help AFL beginners make the translation from TOS and study AFL.
function StochasticFullK(P, ob, os, k, d, R, slow, type) {
	local Lk, Hk;
	Lk = LLV(R, k); Hk = HHV(R, k);
	return 100*SafeDivide((R - Lk), (Hk - Lk), 0.5);
}

function StochasticFullD(P, ob, os, k, d, R, slow, type) {
	local SK, SD;
	
	SK = StochasticFullK(P, ob, os, k, d, R, slow, type);
	SD = MA(SK, d);

	return SD;
}

function StochasticFullSlow(P, ob, os, k, d, R, slow, type) {
	local S, SD;
	
	SD = StochasticFullD(P, ob, os, k, d, R, slow, type);
	switch(type) {
		case "SMA": S = MA(SD, slow); break;
		case "EMA": S = EMA(SD, slow); break;
		default: S = MA(SD, slow); break;
	}
	
	return S;
}

// Indicators
RSIb = RSIx(RSI_price, RSI_length, RSI_average_type);
FullK = StochasticFullK(RSI_price, over_bought, over_sold, KPeriod, DPeriod, RSIb, slowing_period, averageType);
FullD = StochasticFullD(RSI_price, over_bought, over_sold, KPeriod, DPeriod, RSIb, slowing_period, averageType);

Plot(RSIb, "RSI", colorYellow);
PlotGrid(over_bought, colorGreen); PlotGrid(over_sold, colorRed);
Plot(FullK, "FullK", colorlightBlue, styleThick);
Plot(FullD, "FullD", colorOrange, styleThick);

// System
upK = Cross(FullK, over_sold);
upD = Cross(FullD, over_sold);
downK = Cross(over_bought, FullK);
downD = Cross(over_bought, FullD);

switch (showBreakoutSignals) {
	case "No":	Buy = Short = 0; break;
	case "On FullK": Buy = upK; Short = downK; break;
	case "On FullD": Buy = upD; Short = downD; break;
	case "On FullK FullD": 
		Buy = upK OR upD; Short = downK OR downD; break;
	default: Buy = Short = 0; break;
}
Sell = Short; Cover = Buy;

shape = Buy * shapeUpArrow + Short * shapeDownArrow;
PlotShapes( shape, IIf( Buy, colorGreen, colorRed ), 0, IIf( Buy, over_sold, over_bought ) );

_SECTION_END();
/*
TOS Code:

declare lower;
input RSI_length = 14;
input over_bought = 80;
input over_sold = 20;
input RSI_average_type = AverageType.WILDERS;
input RSI_price = close;
input KPeriod = 14;
input DPeriod = 3;
input slowing_period = 1;
input averageType = AverageType.SIMPLE;
input showBreakoutSignals = {default "No", "On FullK", "On FullD", "On FullK &
FullD"};
def RSI = RSI(price = RSI_price, length = RSI_length, averageType =
RSI_average_type);
plot FullK = StochasticFull(over_bought, over_sold, KPeriod, DPeriod, RSI, RSI, RSI,
slowing_period, averageType).FullK;
plot FullD = StochasticFull(over_bought, over_sold, KPeriod, DPeriod, RSI, RSI, RSI,
slowing_period, averageType).FullD;
plot OverBought = over_bought;
plot OverSold = over_sold;
def upK = FullK crosses above OverSold;
def upD = FullD crosses above OverSold;
def downK = FullK crosses below OverBought;
def downD = FullD crosses below OverBought;
plot UpSignal;
plot DownSignal;
switch (showBreakoutSignals) {
case "No":
UpSignal = Double.NaN;
DownSignal = Double.NaN;
case "On FullK":
UpSignal = if upK then OverSold else Double.NaN;
DownSignal = if downK then OverBought else Double.NaN;
case "On FullD":
UpSignal = if upD then OverSold else Double.NaN;
DownSignal = if downD then OverBought else Double.NaN;
case "On FullK & FullD":
UpSignal = if upK or upD then OverSold else Double.NaN;
DownSignal = if downK or downD then OverBought else Double.NaN;
}

*/

You can learn AFL by comparing both formulas.
Merry christmas & Happy new year

4 Likes

I very much appreciate your response and support for this alligator. I have entered this but it still does not come close to the TOS chart. Also, the Slow parameter has no effect on the chart, nor does the Avg Type. Shouldn't these have an effect on the chart when changed? In TOS they both have drastic effects on the chart. I have another StochRSI I was using that comes close to TOS but only with the values way different. I have my TOS parameters at 8, EXP, Close, 8,3,2,Sim. The StochRSI that comes close (sometimes) has to be set at 9,6,2,3. Here is the one that I was trying to use.
_SECTION_BEGIN("StochRSI");
RSIperiods = Param( "RSIperiods", 8, 1, 100, 1 );
Stochperiods = Param( "Stochperiods", 7, 1, 100, 1 );
sep = ParamList( "Separator Interval", "Day|Week|Month|Year", 0 );
Kperiods = Param( "Kperiods", 2, 1, 50, 1 );
Dperiods = Param( "Dperiods", 3, 1, 50, 1 );
OBthreshold = Param( "OBthreshold", 85, 55, 100, 5 );
OSthreshold = Param( "OSthreshold", 20, 0, 50, 5 );

switch( sep )
{
case "Day":
    dn = Day();
    dn1 = inDaily;
    break;

case "Week":
    dw = DayOfWeek();
    newWeek = dw < Ref( dw, -1 );;
    newYear = Year() != Ref( Year(), -1 );
    dn = weekNumber = Sum( newWeek, BarsSince( newYear ) + 1 );
    dn1 = inWeekly;
    break;

case "Month":
    dn = Month();
    dn1 = inMonthly;
    break;

case "Year":
    dn = Year();
    dn1 = inYearly;
    break;
}
 
separator = dn != Ref( dn, -1 );
Plot( separator, "", colorDarkBlue, styleHistogram | styleOwnScale | styleNoLabel | styleNoRescale, 0, 1, 0, -2, 5 );

TheRSI = RSI( RSIperiods );
llRSI = LLV( TheRSI, Stochperiods );
hhRSI = HHV( TheRSI, Stochperiods );

StochasticRSI = 100 * ( ( TheRSI - llRSI ) / ( hhRSI - llRSI + 0.00001 ) );

StocK_rsi = MA( StochasticRSI, Kperiods );
StocD_rsi = MA( StocK_rsi, Dperiods );

Plot( StocK_rsi, "StochK(" + Kperiods + ") of RSI(" + RSIperiods + ")", colorYellow, styleLine | styleThick );
Plot( StocD_rsi, "StochD(" + Dperiods + ") of RSI(" + RSIperiods + ")", colorRed, styleLine | styleThick );
PlotGrid( OBthreshold, colorRed, 2 );
PlotGrid( OSthreshold, colorGreen, 2 );

_SECTION_END();

When posting the formula, please make sure that you use Code Tags (using </> code button) as explained here: How to use this site.

Using code button

Code tags are required so formulas can be properly displayed and copied without errors.

1 Like

Did you read the script ? There's a StochasticFullSlow() function. About the 'slow' thing, slowing_period is 1 by default so it has no impact by default. The main difference between my script and the second one you posted is in the computation of StochK: it's averaged. But I don't see any parameter in the TOS functions script to perform that...Maybe they reuse the Kperiod parameter. In that case, replace StochasticFullK() by:

function StochasticFullK(P, ob, os, k, d, R, slow, type) {
	local Lk, Hk;
	Lk = LLV(R, k); Hk = HHV(R, k);
        Sk = 100*SafeDivide((R - Lk), (Hk - Lk), 0.5);
	return MA(Sk, k);
}

Possibly another type of moving average... (try EMA instead of MA). Also you could try to use d or slow in the return instead of k (try return MA(Sk, slow) with slowing_period = 3 just in case...). Without the original formula I don't see any other approach than trial and error.
Good luck.

3 Likes

OK, thank you for your reply and help!