- ago
An indicator I like to use a lot and one that works very nicely with a rotation strategy in wealth-lab is the relative strength (not to be confused with RSI). This indicator measures a symbols "strength" against another symbol typically an ETF to see if the symbol is performing better or worse than the ETF and can be a good signal. A value below 0 means the stock is weak compared to the other symbol and a value above 0 indicates it is strong compared to the other symbol. I noticed wealth-lab did not have this indicator so I created it. Feel free to use the code below or offer suggestions to improve. This particular indicator also factors in ATR to get a true measure of the stocks strength against an ETF.



CODE:
using WealthLab.Core; using System; using System.Drawing; using WealthLab.Indicators; using WealthLab.ChartWPF; using WealthLab.Backtest; namespace WealthLab.MyIndicators { public class RealRelativeStrength : IndicatorBase { //parameterless constructor public RealRelativeStrength() : base() { } //for code based construction public RealRelativeStrength(BarHistory source, String compareSymbol, Int32 period) : base() {          Parameters[0].Value = source;          Parameters[1].Value = compareSymbol;          Parameters[2].Value = period; Populate(); }       //static Series method       public static RealRelativeStrength Series(BarHistory source, String compareSymbol, Int32 period)       {          return new RealRelativeStrength(source, compareSymbol, period);       } //name public override string Name { get { return "RealRelativeStrength"; } } //abbreviation public override string Abbreviation { get { return "RRS"; } } //description public override string HelpDescription { get { return "Real Relative Strength measured against a compared symbol or sector etf"; } } //price pane public override string PaneTag { get { return "rrs"; } }       //default color       public override WLColor DefaultColor       {          get          {             return WLColor.FromArgb(255,50,205,50);          }       }       //default plot style       public override PlotStyle DefaultPlotStyle       {          get          {             return PlotStyle.Oscillator;          }       } //populate public override void Populate() {          BarHistory source = Parameters[0].AsBarHistory;          String compareSymbol = Parameters[1].AsString;          Int32 period = Parameters[2].AsInt; DateTimes = source.DateTimes;          //modify the code below to implement your own indicator calculation          BarHistory compSource = WLHost.Instance.GetHistory(compareSymbol, source.Scale, DateTime.MinValue, DateTime.MaxValue, source.Count, null);          ATR compAtr = ATR.Series(compSource, period);          if(source != null && source.Count > 0)          {             ATR symAtr = ATR.Series(source, period);             TimeSeries compAtrSynced = TimeSeriesSynchronizer.Synchronize(compAtr, source);             for (int n = 0; n < compSource.Count; n++)             {                if (n > period)                {                   double compSymRollingMove = compSource.Close[n] - compSource.Close[n - period];                   double symRollingMove = source.Close[n] - source.Close[n - period];                   var powerIndex = compSymRollingMove / compAtr.Values[n];                   var rrs = (symRollingMove - powerIndex * symAtr.Values[n]) / symAtr.Values[n];                   Values[n] = rrs;                }             }          } } //generate parameters protected override void GenerateParameters() {          AddParameter("Source", ParameterType.BarHistory, null);          AddParameter("Compare Symbol", ParameterType.String, "SPY");          AddParameter("Period", ParameterType.Int32, 12); } } }
2
205
8 Answers

Reply

Bookmark

Sort
- ago
#1
Maybe we should add this to our WL8 Community Extension Github repository?
3
- ago
#2
I don't think you need the Synchronize statement because GetHistory automatically synchronizes for you unless you use the GetHistoryUnsynched(...) version, which you aren't doing.

I would write "int" instead of "Int32", but either way, with the default compiler switches, both ways should generate the same intermediate code (IC).

I would include a FirstValueIndex setting in your indicator definition.

Your Series method should do caching. That's the typical behavior.

If you make the code public on GitHub, some of us could try to commit some of these changes. As author, however, you (or someone) should approve them. (We don't want the wild west.)

The article I'm looking at https://www.reddit.com/r/RealDayTrading/comments/rpi75s/real_relative_strength_indicator/ definitely shows RRS trending with the stock behavior, but the example cited here (above) doesn't seem to. Are we talking about the same calculation? The link (or link of the link) states RS = ROC(stock) / ROC(index), and then it scales that relative to ATR. But the indicator code above isn't doing that. Perhaps we are talking about an entirely different calculation altogether.
0
- ago
#3
Thank you for the suggestions superticker, really helps with learning the standards and implementations of these classes, I will try to get to these and push them up to a repo. I did notice that without the synchronize the two series were only synchronized if both have the same bar count so I just included it to be safe and I think Glitch recommended it at one point but I could have interpreted incorrectly. Thanks for the inputs.

Edit: Couple of questions

1. The default indicator creator sets it as Int32 when creating an int param, I dont know if that is on purpose but like you said either would work.

2. Can you point me to an example of FirstValidIndex? Is the idea that you can only calculate the indicator once you have a certain amount of data? If so does the condition if (n > period) suffice or is there a better approach? Also I don't think BarHistory has a FirstValidIndex property right?

3. By caching the time series do you mean creating and instantiating the variable outside of Populate as a class variable? Or is there an example you can point me too? Which variable are you referring to here?
0
- ago
#4
QUOTE:
I did notice that without the synchronize the two series were only synchronized if both have the same bar count ...

But you want the two series to have the same bar count in all cases. You need to fix that. Why not use ... instead?
CODE:
BarHistory observedIndex = IndicatorFactory.Instance.GetHistory(bars, indexSymbol, bars.Scale);
There's an optional fourth parameter for the DataSet directory if the indexSymbol isn't in the source directory. See the IntelliSense annotations.

2) Well the ATR isn't valid until there are "period" bars. Why not code
CODE:
FirstValidIndex = period + 1;

And then correct the FOR loop, and you don't need the IF statement that follows.
CODE:
for (int n = period+1; n < compSource.Count; n++)

3) No, I mean caching the series in the WL global cache for the source BarHistory. See https://www.wealth-lab.com/Support/ExtensionApi/IndicatorLibrary under Static Series Method for an example. Each BarHistory has its own "named cache" where you can save stuff under a [key] index.
1
- ago
#5
Just to clarify:

UserStrategyBase has two methods, GetHistory and GetHistoryUnsynched.

But this is an Indicator, not a Strategy, so those methods are not available.

So Adrian used the IHost.GetHistory which does not (and cannot) synchronize, so his code here is correct.
0
- ago
#6
The RRS has been added to the WL8 extension demo library (of course, with caching):

https://github.com/LucidDion/WL8ExtensionDemos

Kudos to @adrianbegi
1
- ago
#7
Thanks, Eugene!
1
- ago
#8
QUOTE:
the IHost.GetHistory which does not (and cannot) synchronize, so his code here is correct.

Thanks for clarifying that. Why can't it synchronize? I see, it's not being passed a source to synchronize by. I never noticed that before. (I'm trying to decide if it should be overloaded to include an optional source for synchronization? Would overloading cause more confusion?)

Will the call below synchronize? That's what I've been using in all my indicators without any problems.
CODE:
BarHistory observedIndex = IndicatorFactory.Instance.GetHistory(bars, indexSymbol, bars.Scale);

It would be nice if the indicator code also included a URL for the method used. However, I don't think the URL below is the one being used in this case because their calculation is different. I think the URL below outlines a method that follows the stock behavior better. https://www.reddit.com/r/RealDayTrading/comments/rp5rmx/a_new_measure_of_relative_strength/
0

Reply

Bookmark

Sort