WORDEN STOCHASTIC
An indicator and strategy by mashumeFor trading with set stops and multiple targets
INTRODUCTION
I wanted to create an indicator that would pick lows and highs, but not designed to require me to be in a trade at all times after reading Trading in the Zone by Mark Douglas.
In the closing strategy portion of that book, he describes a system where you place a trade for three contracts, with stops and targets for each of the contracts set from the start of the trade. This is not an 'always in' trading strategy. If this doesn't make sense, read his book and then come back for this one.
Use it at your own risk and play with the settings. There are lots of options, and ways to tailor the indicator to your instrument of choice.
Note that the success percentages are not based on trades making targets, they are based on whether the trade is above (for long) the opening price a set number of bars after the trade is placed. It is beyond the coding presented here to check more fully than that. It is not a ToS Strategy, it is an indicator.
Happy Trading.

Code:
##############################################################
#
# Worden Stochastic
#
# A Strategy
# with stops and targets on chart
# and lots and lots of variations
#
# (c) 2023 mashume (Seth Urion)
# Released under GPLv3 to the UseThinkScript community
#
# This software is released as is and with no warranty
# of appropriateness or suitability for any task.
#
##############################################################
declare upper;
input upper = 77.5;
input lower = 22.5;
input kvo_length = 13;
input win_length = 15;
input k = 10;
input d = 10;
def rsi_value = RSI();
def rsi_value_u = RSI("over bought" = upper, "over sold" = lower).UpSignal;
input rsi_lookback = 3;
input Worden_length = 30;
input alerts = no;
script Worden {
input length = 12;
input k = 3;
input d = 5;
input OS = 20;
input OB = 80;
def rank = fold i = 0 to length with rankcounter = 0 do if close > GetValue(close, i) then rankcounter + 1 else rankcounter;
def w = ( 100 / ( length – 1 ) ) * Rank;
plot Worden = SimpleMovingAvg(w, k);
plot signal = SimpleMovingAvg(Worden, d);
};
script klingerAlt {
input MALength = 13;
def DM = high - low;
def Trend = if hlc3 > hlc3[1] then 1 else -1;
def CM = DM + if Trend == Trend[1] then CM[1] else DM[1];
def modVol = VOLUME / tick_count();
def VForce = if CM != 0 then Trend * 100 * modVol * AbsValue(2 * DM / CM - 1) else VForce[1];
plot KVOsc = ExpAverage(VForce, 34) - ExpAverage(VForce, 55);
plot TriggerLine = Average(KVOsc, MALength);
};
input kvo_method = {default standard, alternate};
def kvosc;
switch (kvo_method) {
case standard:
kvosc = KlingerOscillator(MALength = kvo_length).KVOsc;
case alternate:
kvosc = klingerAlt(MALength = kvo_length).KVOsc;
}
def triggerLine = KlingerOscillator(MALength = kvo_length).TriggerLine;
def slowD = StochasticSlow("k period" = k, "d period" = d).SlowD;
def slowK = StochasticSlow("k period" = k, "d period" = d).SlowK;
input trade_loss = 50.0;
DefineGlobalColor(color = Color.RED, name = "pl_neg");
DefineGlobalColor(color = Color.WHITE, name = "pl_pos");
DefineGlobalColor(color = Color.YELLOW, name = "stopLoss");
input t1 = 1.0;
input t2 = 2.0;
input t3 = 4.0;
input Method = {default "kvo + sto", "kvo + trigger dt", "kvo + trigger dt no zero", "kvo + sto + trigger dt", "kvo + sto no zero", "kvo + sto no cx", "kvo + rsi", "kvo Worden"};
def kosc_buy;
def kosc_sell;
def stoch_buy;
def stoch_sell;
switch (Method) {
case "kvo Worden":
kosc_buy = if kvosc crosses above triggerLine
and triggerLine < 0
then 1 else 0;
kosc_sell = if kvosc crosses below triggerLine
and triggerLine > 0
then 1 else 0;
stoch_buy =
if Worden(length = Worden_length).signal < lower
# and Worden(length = Worden_length) < lower
and Worden(length = Worden_length) > Worden(length = Worden_length).signal
then 1 else 0;
stoch_sell =
if Worden(length = Worden_length).signal > upper
# and Worden() > upper
and Worden(length = Worden_length) < Worden(length = Worden_length).signal
then 1 else 0;
case "kvo + sto":
kosc_buy = if kvosc crosses above triggerLine
and triggerLine < 0
then 1 else 0;
kosc_sell = if kvosc crosses below triggerLine
and triggerLine > 0
then 1 else 0;
stoch_buy = if slowD < lower and slowK < lower and (slowD < SlowK
or (slowK crosses above lower and slowD < lower) )
# or slowD crosses above lower
then 1 else 0;
stoch_sell = if slowD > upper and slowK > upper and (slowD > SlowK
or (slowK crosses below upper and slowD > upper ))
# or slowD crosses below upper
then 1 else 0;
case "kvo + sto no cx":
kosc_buy = if kvosc crosses above triggerLine
and triggerLine < 0
then 1 else 0;
kosc_sell = if kvosc crosses below triggerLine
and triggerLine > 0
then 1 else 0;
stoch_buy = if (slowD < lower) # and slowK < lower)
# or slowK crosses above lower
# or slowD crosses above lower
then 1 else 0;
stoch_sell = if slowD > upper # and slowK > upper
# or slowK crosses below upper
# or slowD crosses below upper
then 1 else 0;
case "kvo + sto no zero":
kosc_buy = if kvosc crosses above triggerLine
then 1 else 0;
kosc_sell = if kvosc crosses below triggerLine
then 1 else 0;
stoch_buy = if slowD < lower or slowK < lower then 1 else 0;
stoch_sell = if slowD > upper or slowK > upper then 1 else 0;
case "kvo + sto + trigger dt":
kosc_buy = if kvosc crosses above triggerLine
and triggerLine - triggerLine[1] >= 0
and triggerLine < 0
then 1 else 0;
kosc_sell = if kvosc crosses below triggerLine
and triggerLine - triggerLine[1] <= 0
and triggerLine > 0
then 1 else 0;
stoch_buy = if slowD < lower or slowK < lower then 1 else 0;
stoch_sell = if slowD > upper or slowK > upper then 1 else 0;
case "kvo + trigger dt":
kosc_buy = if kvosc crosses above triggerLine
and triggerLine - triggerLine[1] >= 0
and triggerLine < 0
then 1 else 0;
kosc_sell = if kvosc crosses below triggerLine
and triggerLine - triggerLine[1] <= 0
and triggerLine > 0
then 1 else 0;
stoch_buy = 1;
stoch_sell = 1;
case "kvo + trigger dt no zero":
kosc_buy = if kvosc crosses above triggerLine
and triggerLine - triggerLine[1] >= 0
then 1 else 0;
kosc_sell = if kvosc crosses below triggerLine
and triggerLine - triggerLine[1] <= 0
then 1 else 0;
stoch_buy = 1;
stoch_sell = 1;
case "kvo + rsi":
kosc_buy = if kvosc crosses above triggerLine
and kvosc <= 0
then 1 else 0;
kosc_sell = if kvosc crosses below triggerLine
and kvosc >= 0
then 1 else 0;
stoch_buy = if rsi_value < lower within rsi_lookback bars then 1 else 0;
stoch_sell = if rsi_value > upper within rsi_lookback bars then 1 else 0;
}
plot long = if kosc_buy == 1 and stoch_buy == 1 then low else Double.NaN;
plot short = if kosc_sell == 1 and stoch_sell == 1 then high else Double.NaN;
def trade_length = if !IsNaN(long[1]) then 1 else trade_length[1] + 1;
def short_trade_length = if !IsNaN(short[1]) then 1 else short_trade_length[1] + 1;
def TradePrice = if !IsNaN(long[1]) then open else TradePrice[1];
# AddLabel(yes, "Last Trade Price = " + TradePrice, color.black);
def sl = if !IsNaN(long[1]) then open - (( trade_loss * TickSize() ) / TickValue()) else if trade_length <= 15 then sl[1] else Double.NaN;
plot Stop_loss = sl;
def tg = if !IsNaN(long[1]) then open + ((t1 * trade_loss * TickSize() ) / TickValue()) else if trade_length <= 30 then tg[1] else Double.NaN;
plot target = tg;
def tg2 = if !IsNaN(long[1]) then open + (( t2 * trade_loss * TickSize() ) / TickValue()) else if trade_length <= 45 then tg2[1] else Double.NaN;
plot target2 = tg2;
def tg3 = if !IsNaN(long[1]) then open + ((t3 * trade_loss * TickSize() ) / TickValue()) else if trade_length <= 60 then tg3[1] else Double.NaN;
plot target3 = tg3;
def sls = if !IsNaN(short[1]) then open + ((1 * trade_loss * TickSize() ) / TickValue()) else if short_trade_length <= 15 then sls[1] else Double.NaN;
plot short_Stop_loss = sls;
def tgs = if !IsNaN(short[1]) then open - ((t1 * trade_loss * TickSize() ) / TickValue()) else if short_trade_length <= 30 then tgs[1] else Double.NaN;
plot short_target = tgs;
def tgs2 = if !IsNaN(short[1]) then open - (( t2 * trade_loss * TickSize() ) / TickValue()) else if short_trade_length <= 45 then tgs2[1] else Double.NaN;
plot short_target2 = tgs2;
def tgs3 = if !IsNaN(short[1]) then open - ((t3 * trade_loss * TickSize() ) / TickValue()) else if short_trade_length <= 60 then tgs3[1] else Double.NaN;
plot short_target3 = tgs3;
# AddChartBubble("time condition" = !IsNaN(sl) and IsNaN(sl[1]), text = "SL " + (open - ((trade_loss * TickSize() ) / TickValue())), color = Color.YELLOW, up = no, "price location" = sl);
AddChartBubble("time condition" = (!IsNaN(sl) and IsNaN(sl[1]))
# or sl[1] != sl
, text = "SL " + sl, color = GlobalColor("stopLoss"), up = no, "price location" = sl);
AddChartBubble("time condition" = (!IsNaN(sls) and IsNaN(sls[1]))
# or sl[1] != sl
, text = "SL " + sls, color = GlobalColor("stopLoss"), up = yes, "price location" = sls);
stop_loss.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
stop_loss.SetLineWeight(2);
stop_loss.SetDefaultColor(Color.black);
target.SetPaintingStrategy(PaintingStrategy.DASHES);
target.SetLineWeight(1);
target.SetDefaultColor(Color.black);
target2.SetPaintingStrategy(PaintingStrategy.DASHES);
target2.SetLineWeight(1);
target2.SetDefaultColor(Color.black);
target3.SetPaintingStrategy(PaintingStrategy.DASHES);
target3.SetLineWeight(1);
target3.SetDefaultColor(Color.black);
short_stop_loss.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
short_stop_loss.SetLineWeight(2);
short_stop_loss.SetDefaultColor(Color.PLUM);
short_target.SetPaintingStrategy(PaintingStrategy.DASHES);
short_target.SetLineWeight(1);
short_target.SetDefaultColor(Color.PLUM);
short_target2.SetPaintingStrategy(PaintingStrategy.DASHES);
short_target2.SetLineWeight(1);
short_target2.SetDefaultColor(Color.PLUM);
short_target3.SetPaintingStrategy(PaintingStrategy.DASHES);
short_target3.SetLineWeight(1);
short_target3.SetDefaultColor(Color.PLUM);
short.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
short.SetLineWeight(4);
short.SetDefaultColor(Color.PLUM);
long.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
long.SetLineWeight(4);
long.SetDefaultColor(Color.Dark_Green);
Alert(condition = long and alerts == yes, text = "Enter Long", sound = Sound.Ring);
Alert(condition = short and alerts == yes, text = "Enter Long", sound = Sound.Ring);
AddLabel( (slowD <= lower and slowK <= lower and slowD < SlowK ), "Stochastic Ready LONG", Color.DARK_GREEN);
AddLabel( (slowD >= upper or slowK >= upper and slowD > SlowK ), "Stochastic Ready SHORT", Color.DARK_RED);
AddLabel( ((slowD < upper and slowK < upper) and (slowD > lower and slowK > lower) ), "Stochastic Neutral", Color.GRAY);
AddLabel( (triggerLine <= 0 and kvosc < triggerLine), "Klinger Ready Long", Color.DARK_GREEN);
AddLabel( (triggerLine <= 0 and kvosc[1] < triggerLine[1]
and kvosc > triggerLine), "Klinger Crossover Long!", Color.BLACK);
# AddLabel( triggerLine - triggerLine[1] <= 0, "KVO T dt Negative", Color.DARK_RED);
# AddLabel( triggerLine - triggerLine[1] > 0, "KVO T dt Positive", Color.DARK_GREEN);
def trades = if !IsNaN(long) then if IsNaN(trades[1]) then 1 else trades[1] + 1 else trades[1];
def wins = if !IsNaN(long[win_length]) and close[win_length - 1] < close then if IsNaN(wins[1]) then 1 else wins[1] + 1 else wins[1];
def win_rate = wins / Max(trades, 1);
# addChartBubble(long, high + (8 * tickSize()), text = wins + " / " + trades, if win_rate > 0.666 then color.green else if win_rate < 0.333 then color.red else if win_rate > 0.5 then color.cyan else color.orange);
# AddChartBubble(long, high + (15 * TickSize()), text = "L: " + Round(win_rate * 100, 0) + "%"
# + " :: " + wins + " / " + max((trades) - 1, 1)
# , if win_rate > 0.666 then Color.GREEN else if win_rate < 0.333 then Color.RED else if win_rate > 0.5 then Color.CYAN else Color.ORANGE);
AddLabel(yes, "LONG Rate: " + AsPercent(win_rate), if win_rate > 0.666 then Color.DARK_GREEN else if win_rate < 0.333 then Color.DARK_RED else if win_rate > 0.5 then Color.BLUE else Color.DARK_ORANGE);
def short_trades = if !IsNaN(short) then if IsNaN(short_trades[1]) then 1 else short_trades[1] + 1 else short_trades[1];
def short_wins = if !IsNaN(short[win_length]) and close[win_length - 1] > close then if IsNaN(short_wins[1]) then 1 else short_wins[1] + 1 else short_wins[1];
def short_win_rate = short_wins / Max(short_trades, 1);
AddLabel(yes, "SHORT Rate: " + AsPercent(short_win_rate), if short_win_rate > 0.666 then Color.DARK_GREEN else if short_win_rate < 0.333 then Color.DARK_RED else if short_win_rate > 0.5 then Color.BLUE else Color.DARK_ORANGE);
# addChartBubble(short, low - (8 * tickSize()), text = short_wins + " / " + short_trades, if short_win_rate > 0.666 then color.green else if short_win_rate < 0.333 then color.red else if short_win_rate > 0.5 then color.cyan else color.orange, up = No);
# AddChartBubble("time condition" = short, "price location" = high + (4 * TickSize()), text = "S: " + Round(short_win_rate * 100, 0) + "%", color = if short_win_rate > 0.666 then Color.GREEN else if short_win_rate < 0.333 then Color.RED else if short_win_rate > 0.5 then Color.CYAN else Color.ORANGE);
def profit_loss = if !IsNaN(long[win_length]) then close[-1] - open[win_length - 1] else Double.NaN;
def pl_dollars = (profit_loss / TickSize()) * TickValue();
def time_to_pl = if !IsNaN(long[win_length]) then ( GetTime() - GetTime()[win_length]) / 60000 else Double.NaN;
# AddChartBubble("time condition" = !IsNaN(profit_loss), text = "P/L $" + AbsValue(pl_dollars) + "\n" + time_to_pl + " min", "price location" = low - 4 * TickSize(), up = no, color = if pl_dollars > 0 then GlobalColor("pl_pos") else GlobalColor("pl_neg"));
Alert(condition = alerts == yes and long[1], text = "Long Entry", "alert type" = Alert.BAR, sound = Sound.Bell);
Alert(condition = alerts == yes and short[1], text = "Short Entry", "alert type" = Alert.BAR, sound = Sound.Bell);
def in_trade = if !IsNaN(EntryPrice()) then 1 else 0;
Alert(condition = alerts == yes and high crosses above target and in_trade == 1, text = "Target 1", "alert type" = Alert.BAR, sound = Sound.Ding);
Alert(condition = alerts == yes and high crosses above target2 and in_trade == 1, text = "Target 2", "alert type" = Alert.BAR, sound = Sound.Ring);
# AddLabel(yes, "Tick Stop Loss: " + (( trade_loss * tickSize() )/ tickValue()), color.gray);
AddLabel(yes, " SL/T: " + AsDollars(trade_loss) + " R/Rs : " + t1 + ", " + t2 + " ", Color.DARK_GRAY);
# AddChartBubble("time condition" = long[win_length], "price location" = SL, text = "LOSS = " + asDollars( (open[win_length] - lowest(LOW, win_length)) / ticksize() * tickValue()), up = No, color = GlobalColor("pl_neg"));
plot stoSell = stoch_sell;
def long_trail_stop =
if !IsNaN(long[1])
then open - (( trade_loss * TickSize() ) / TickValue())
else if trade_length <= 60
then
if high - (( trade_loss * TickSize() ) / TickValue()) > long_trail_stop[1]
then high - (( trade_loss * TickSize() ) / TickValue())
else
long_trail_stop[1]
else
Double.NaN;
def short_trail_stop = if !IsNaN(short[1]) then open + (( trade_loss * TickSize() ) / TickValue()) else if trade_length <= 60 then if low + (( trade_loss * TickSize() ) / TickValue()) < short_trail_stop[1] then low + (( trade_loss * TickSize() ) / TickValue()) else short_trail_stop[1] else Double.NaN;
plot short_trail = short_trail_stop;
plot long_trail = long_trail_stop;
# def long_open = if !IsNaN(long[1]) or !IsNaN(short[1]) then open else if trade_length <= 60 then long_open[1] else Double.NaN;
# plot trade_open = long_open;
# addCloud(trade_open, long_trail, color.red, color.green);
# addCloud(trade_open, short_trail, color.green, color.red);
P.S. There is a bunch of code that's commented out in there, and I think I got all the proprietary stuff removed. (I have another version that uses the BuyTheDip indicator in conjunction with those herein). I can't remember what all of the variations of this script were designed to do. Things like "no cx" means it doesn't need to cross (presumably the previous one needed a crossover to trigger). I think the default settings work fairly well and you can probably safely ignore everything but the stop loss value and the R:R settings.
This will be my last big post for a while. I think.
-mashume