mjj38
- ago
So I've been working on porting a semi-automated strategy that utilizes zig zag's and volume profiles (support / resistance) along multiple timeframes. I would like the calculations (logic) to reside largely in indicators to make it reusable (can be used in multiple strategies or just display information in a given chart).

The problem I am running into is that indicators are essentially just a single TimeSeries from the PlotStyle's perspective. For example when plotting a zig zag I want to calculate each swings magnitude, length, etc. Also calculated projections and various statistics. I don't want to calculate these within the PlotStyle but rather the indicator. What would be the best way to approach this?

Here is the current ZigZag with my first attempt at a PlotStyle



CODE:
using System.Globalization; using System.Windows; using System.Windows.Media; using WealthLab.ChartWPF; namespace WealthLab.Custom.Wpf; public class ZigZagExtended : LineSeriesStyle { public override string Name { get; } = "ZigZagExt"; public override void Render(DrawingContext dc) { int start = -1, end = -1; for (int i = 0; i < Series.Count; i++) { if (!double.IsNaN(Series[i])) { if (start == -1) { start = i; } else { start = end; end = i; if (start >= Chart.StartIndex && end <= Chart.EndIndex) { Point startPoint = GetPoint(start, Series); Point endPoint = GetPoint(end, Series); dc.DrawLine(DefaultPen, startPoint, endPoint); double change = Series[end] - Series[start]; FormattedText txt = new($"{change:N2}", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Verdana"), 10, DefaultBrush); bool isUp = Series[end] > Series[start]; Point textPoint = new(endPoint.X - txt.WidthIncludingTrailingWhitespace / 2, endPoint.Y - (isUp ? txt.Height : 0)); dc.DrawText(txt, textPoint); } } } } } }
0
363
5 Replies

Reply

Bookmark

Sort
Glitch8
 ( 10.41% )
- ago
#1
Why do you want to calculate them in an indicator rather than a plot style?

It strikes that maybe some intermediate utility class (like PeakTroughCalculator is) might be the solution?
0
- ago
#2
QUOTE:
The problem ... is that indicators are essentially just a single TimeSeries

I agree, this is a limitation with indicators. So as Reply# 1 suggests, don't create an indicator in the first place. In my own Local.Components.dll library, I've created many classes, which create there own objects. Some objects contain two TimeSeries. All contain Doubles and Integers. Many custom classes have internal field (state) variables for encapsulating (hiding) their operations.

It might also be useful to create a new PlotStyle as well.
1
mjj38
- ago
#3
Thank you guys for your suggestions. A helper classes would definitely solve the code reuse issue.

What I am struggling with is figuring out how to save the calculations in the cache so optimizations are much quicker. I'm using relatively expensive processing time logic (largely zigzag's, pattern matching and volume profiles across multiple time frames).

I was thinking of going the indicator route because it's simple to save the state of indicators in the barseries cache. Or should I try to preprocess these in another step in the process like backtest begin?

Do you have any suggestions based on your experience what is the best approach?
0
- ago
#4
QUOTE:
I was thinking of going the indicator route because it's simple to save the state of indicators in the Bars series cache. Or should I try to preprocess these in another step in the process like backtest begin?

There are many options. Remember the BarHistory cache can be used by any code that makes a reference to a BarHistory object. And all BarHistory objects are ticker symbol specific. There many be an issue with clearing the cached object if you want it's time-of-life to be shorter than the strategy or WL session. If that's the case, then I wouldn't store it in the BarHistory cache in the first place (although you could use the Clear method as a destructor).

When I create my own classes, I typically call its constructor in the MyStrategy() constructor itself. That allows me to create class objects for the entire Strategy run (without using the global cache) that are unique to that strategy instance. (Recall the optimizer is threading multiple strategy instances.) If that time-of-life is too long, then block your code to a more local scope. For example, create a "local block" that only includes Initialize and Execute, but nothing else. Then when you call your class' constructor in Initialize, those objects will only be available in Execute, but nowhere else.

Let the constructor for your custom class build whatever objects it needs (no need to cache them unless you need them across independent WL runs). And let the methods for your custom class modify those objects as needed. I have one class that does significant calculations (including two sorts) in its constructor. That work is saved by its constructor (in a field variable; e.g. this.TimeSeries), then employed by other class methods later on.

And, of course, your custom class should be encapsulated (have "closure") such that high-level strategy code can't corrupt its internal objects. If the strategy code wants to change something affecting a class object, it has to do so via a class method. In other words, follow good software engineering design practices to prevent latent "side effects".
0
mjj38
- ago
#5
Super helpful SuperTicker (no pun intended ;-)

I believe that answers all of my questions. Thank you again!
0

Reply

Bookmark

Sort