I want to count how many signals are triggered across the Universe and see it visually.
50-200 SMA Crossover in my code example.
The chart is not rendering when I try to plot the signal TimeSeries. Maybe I'm not feeding data to the TimeSeries object properly.
50-200 SMA Crossover in my code example.
The chart is not rendering when I try to plot the signal TimeSeries. Maybe I'm not feeding data to the TimeSeries object properly.
CODE:
namespace WealthScript2 { public class Signal : UserStrategyBase { private TimeSeries signals; private SMA sma1, sma2; public override void Initialize(BarHistory bars) { sma1 = new SMA(bars.Close, 50); sma2 = new SMA(bars.Close, 200); PlotIndicatorLine(sma1); PlotIndicatorLine(sma2); signals = new TimeSeries(bars.DateTimes); PlotTimeSeries(signals, "Signals", "OP", WLColor.Green, PlotStyle.Histogram); } public override void Execute(BarHistory bars, int idx) { if (sma1.CrossesOver(sma2, idx)) { signals[idx] += 1; } } } }
Rename
It's a bit complicated because each BarHistory in the DataSet might have a varying number of elements (bars) so you'll need do some of this in Pre and PostExecute. I create a static counter variable, initialize it to zero in PreExecute. In Execute, I increment the counter if there's a signal. In PostExecute I go through all the BarHistories again, get their current index, get their own copy of their plottable "Signals" TimeSeries, and assign the value there.
CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Collections.Generic; namespace WealthScript2 { public class Signal : UserStrategyBase { private TimeSeries signals; private SMA sma1, sma2; public override void Initialize(BarHistory bars) { sma1 = new SMA(bars.Close, 50); sma2 = new SMA(bars.Close, 200); PlotIndicatorLine(sma1); PlotIndicatorLine(sma2); signals = new TimeSeries(bars.DateTimes, 0); bars.Cache["Signals"] = signals; PlotTimeSeries(signals, "Signals", "OP", WLColor.Green, PlotStyle.Histogram); } public override void Execute(BarHistory bars, int idx) { if (sma1.CrossesOver(sma2, idx)) { counter++; } } public override void PreExecute(DateTime dt, List<BarHistory> participants) { counter = 0; } public override void PostExecute(DateTime dt, List<BarHistory> participants) { foreach(BarHistory bars in participants) { int idx = GetCurrentIndex(bars); TimeSeries sig = (TimeSeries)bars.Cache["Signals"]; sig[idx] = counter; } } private static int counter; } }
Hey Glitch, thanks taking the time to help me out while you're in Cairo.
I tried to do it your way but I'm still getting an empty plot on the "signals" TimeSeries.
I want to be able to plot a signal count of all signals across the Universe. My interpretation of this code is a TimeSeries sig is assigned the static counter varible value in PostExecute at its own particular index but its outside the scope of the TimeSeries signals?
I tried to do it your way but I'm still getting an empty plot on the "signals" TimeSeries.
I want to be able to plot a signal count of all signals across the Universe. My interpretation of this code is a TimeSeries sig is assigned the static counter varible value in PostExecute at its own particular index but its outside the scope of the TimeSeries signals?
I’m not sure what is happening at your end, but the code above is producing histogram bars when i run it on the Dow 30, for example.
I'm not sure what's going on either. I just straight copied and pasted your code.
What build number are you running? You might need to upgrade to Build 8 because we did incorporate some fixes in TimeSeries plotting that could explain why you're not seeing the plot.
I upgraded to Build 8 and it works. thank you
I have a couple questions concerning the Reply# 2 solution.
1) How many signal TimeSeries are we creating? To me, it looks like we are creating a separate signal TimeSeries for each BarHistory object. Is this correct or am I reading this wrong?
2) Why is the plot of "signal" being done in Initialize when it's only zeros at that point? Plotting it in Cleanup, after it's populated with values, would make more sense to me. Are all the plots defined in Initialize actually executed in BacktestComplete?
1) How many signal TimeSeries are we creating? To me, it looks like we are creating a separate signal TimeSeries for each BarHistory object. Is this correct or am I reading this wrong?
2) Why is the plot of "signal" being done in Initialize when it's only zeros at that point? Plotting it in Cleanup, after it's populated with values, would make more sense to me. Are all the plots defined in Initialize actually executed in BacktestComplete?
1) Glitch said why in the first sentence - if you knew each BarHistory would have exactly the same number of Bars, then you could probably use just one copy and plot it without error. But that would be a bad assumption, and, you don't know which symbol will be plotted, so you need a synchronized TimeSeries to plot for each symbol.
2) Just answered.. you need a synchronized series to plot with the primary bars. I think it doesn't matter when you call a Plot function - it's housekeeping Chart rendering chore.
2) Just answered.. you need a synchronized series to plot with the primary bars. I think it doesn't matter when you call a Plot function - it's housekeeping Chart rendering chore.
QUOTE:
1) ... you don't know which symbol will be plotted, so you need a synchronized TimeSeries to plot for each symbol.
Interesting. So you have to create separate signal plots for each stock in the dataset since each stock may have different missing bars (and DateTimes). Seems like you're creating many "redundant" signal vectors, which are nearly identical. But I can't think of another way to do it unless the plotting function automatically handled the synchronization operations and filled in for the missing bars. (If it did that, that would save a great deal of memory.)
QUOTE:
2) ... it doesn't matter when you call a Plot function
So the Plot requests are queued and executed in base.BacktestComplete. I didn't know that.
Thanks for all the clarifications. Someone needs to publish a blog article on how all this plumbing works eventually. Hiding the particulars is thought to be a plus in OOPS design, but it can also be a minus; that is, "information hiding" can be a double-edged sword sometimes.
One great thing about programming functions and indirection is that you don't have to know the "particulars" or internals of a function, only the result.
On the other hand, if there's something you must know to use the function, that's different, but in the case of Plot you can [probably] put them anywhere and everywhere. Wealth-Lab will cache and adapt so as not to plot the same thing once for on every bar, for example, if you happen to put a Plot statement in Execute().
Just like in WL6.9 (and every version of Wealth-Lab before and after), you ALWAYS needed to synchronize a series to plot it with the charted symbol. If you don't, you'd get an error. Usually, you don't have to consider that unless you're using TimeSeriesSynchronizer functions, but this was a special case that 99% of users are unlikely to encounter.
On the other hand, if there's something you must know to use the function, that's different, but in the case of Plot you can [probably] put them anywhere and everywhere. Wealth-Lab will cache and adapt so as not to plot the same thing once for on every bar, for example, if you happen to put a Plot statement in Execute().
Just like in WL6.9 (and every version of Wealth-Lab before and after), you ALWAYS needed to synchronize a series to plot it with the charted symbol. If you don't, you'd get an error. Usually, you don't have to consider that unless you're using TimeSeriesSynchronizer functions, but this was a special case that 99% of users are unlikely to encounter.
Your Response
Post
Edit Post
Login is required