- ago
Hi,
I'm trying to create a dividend-adjustment factor series which can then be multiplied into any Price series (e.g. Close) to get dividend-adjusted prices.
CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; using System.Linq; namespace WealthScript1 {    public class divAdj : TimeSeries    {       TimeSeries ts;       UserStrategyBase usb;       BarHistory bars;       public divAdj(UserStrategyBase usb, TimeSeries ts) : base(ts.DateTimes, false)       {          this.ts = ts;          this.usb = usb;          List<EventDataPoint> fList;          fList = bars.EventDataPoints.Where(i => i.Value > 0 && i.Name == "Dividend" && i.ProviderName == "Morningstar").ToList(); //Morningstar Fndmntl Data          // build a dividend adjustment factor (daf) TimeSeries          TimeSeries daf = new TimeSeries(bars.DateTimes, 1);          try          {             //don't process a symbol with no dividends             if (fList.Count == 0)                return;             int bar = bars.Count - 1;             double _daf = 1d;             double _div = 0d;             for (int n = fList.Count - 1; n >= 0; n--)             {                var fi = fList[n];                int fbar = daf.IndexOf(fi.Date.Date);                if (fbar == -1)                   continue;                while (bar >= fbar)                {                   if (bar < 1) break;                   daf[bar] = _daf;                   bar--;                }                if (fList.Count > 1                   && n < fList.Count - 1                   && fList[n].Date == fList[n + 1].Date) // there's > 1 div on this bar                   _div += fi.Value;                else                   _div = fi.Value;                _daf = daf[bar + 1] * (1 - _div / bars.Close[bar]);             }             while (bar > -1)             {                daf[bar] = _daf;                bar--;             }             TimeSeries result = new TimeSeries();             result = ts * daf;             for (bar = 0; bar < bars.Count; bar++)             {                base[bar] = result[bar];             }          }          catch (Exception e)          {             usb.WriteToDebugLog(bars.Symbol + "; divAdj series exception: " + e.Message);          }       }       public static divAdj Series(UserStrategyBase usb, TimeSeries ts)       {          divAdj _da = new divAdj(usb, ts);          return _da;       }    }    public class MyStrategy : UserStrategyBase    {       //create indicators and other objects here, this is executed prior to the main trading loop       public override void Initialize(BarHistory bars)       {          TimeSeries divAdjC = divAdj.Series(this, bars.Close);          PlotTimeSeriesLine(divAdjC, "div-adj Close", "pTest", Color.Fuchsia);       }       //execute the strategy rules here, this is executed once for each bar in the backtest history       public override void Execute(BarHistory bars, int idx)       {       }       //declare private variables below    } }


It compiles w/o errors but when I run it I get this error:
Initialize Exception (SPY) Line 23 - Object reference not set to an instance of an object.
at WealthScript1.divAdj..ctor(UserStrategyBase usb, TimeSeries ts) in :line 23
at WealthScript1.MyStrategy.Initialize(BarHistory bars) in :line 86
at WealthLab.Backtest.UserStrategyExecutor.CountProducer(List`1 symbols)

How do I fix this & get it working?
0
1,152
Solved
20 Replies

Reply

Bookmark

Sort
- ago
#1
Reason: BarHistory bars is null (not initialized).

CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; using System.Linq; namespace WealthScript2 {    public class divAdj : TimeSeries    {       public divAdj(BarHistory bars) : base(bars.DateTimes, false)       {          List<EventDataPoint> fList;          fList = bars.EventDataPoints.Where(i => i.Value > 0 && i.Name == "Dividend" && i.ProviderName == "Morningstar").ToList(); //Morningstar Fndmntl Data                                                                          // build a dividend adjustment factor (daf) TimeSeries          TimeSeries daf = new TimeSeries(bars.DateTimes, 1);          try          {             //don't process a symbol with no dividends             if (fList.Count == 0)                return;             int bar = bars.Count - 1;             double _daf = 1d;             double _div = 0d;             for (int n = fList.Count - 1; n >= 0; n--)             {                var fi = fList[n];                int fbar = daf.IndexOf(fi.Date.Date);                if (fbar == -1)                   continue;                while (bar >= fbar)                {                   if (bar < 1) break;                   daf[bar] = _daf;                   bar--;                }                if (fList.Count > 1                 && n < fList.Count - 1                 && fList[n].Date == fList[n + 1].Date) // there's > 1 div on this bar                   _div += fi.Value;                else                   _div = fi.Value;                _daf = daf[bar + 1] * (1 - _div / bars.Close[bar]);             }             while (bar > -1)             {                daf[bar] = _daf;                bar--;             }             TimeSeries result = new TimeSeries(bars.DateTimes, 0);             result.DateTimes = bars.DateTimes;             result = bars.Close * daf;             this.Values = result.Values;          }          catch (Exception e)          {          }       } }    public class MyStrategy : UserStrategyBase    {       //create indicators and other objects here, this is executed prior to the main trading loop       public override void Initialize(BarHistory bars)       {          TimeSeries divAdjC = new divAdj(bars);          PlotTimeSeriesLine(divAdjC, "div-adj Close", "pTest", Color.Fuchsia);       }       //execute the strategy rules here, this is executed once for each bar in the backtest history       public override void Execute(BarHistory bars, int idx)       {       }       //declare private variables below    } }
1
Best Answer
- ago
#2
Thank you so much!!

Last Q: In previous WL versions I could add a description to the custom series, is there a way to do so in WL7?
0
Cone8
 ( 6.17% )
- ago
#3
Exactly the same way -
CODE:
            TimeSeries result = something;             result.Description = "My Description of the series";


Specifically -
CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; using System.Linq; namespace WealthScript2 {    public class divAdj : TimeSeries    {       public divAdj(BarHistory bars) : base(bars.DateTimes, false)       {          List<EventDataPoint> fList;          fList = bars.EventDataPoints.Where(i => i.Value > 0 && i.Name == "Dividend" && i.ProviderName == "Morningstar").ToList(); //Morningstar Fndmntl Data                                                                                                     // build a dividend adjustment factor (daf) TimeSeries          TimeSeries daf = new TimeSeries(bars.DateTimes, 1);          try          {             //don't process a symbol with no dividends             if (fList.Count == 0)                return;             int bar = bars.Count - 1;             double _daf = 1d;             double _div = 0d;             for (int n = fList.Count - 1; n >= 0; n--)             {                var fi = fList[n];                int fbar = daf.IndexOf(fi.Date.Date);                if (fbar == -1)                   continue;                while (bar >= fbar)                {                   if (bar < 1) break;                   daf[bar] = _daf;                   bar--;                }                if (fList.Count > 1                 && n < fList.Count - 1                 && fList[n].Date == fList[n + 1].Date) // there's > 1 div on this bar                   _div += fi.Value;                else                   _div = fi.Value;                _daf = daf[bar + 1] * (1 - _div / bars.Close[bar]);             }             while (bar > -1)             {                daf[bar] = _daf;                bar--;             }             TimeSeries result = new TimeSeries(bars.DateTimes, 0);             result.DateTimes = bars.DateTimes;             result = bars.Close * daf;             this.Values = result.Values;             this.Description = "My Adjusted Series";          }          catch (Exception e)          {          }       }    }    public class MyStrategy : UserStrategyBase    {       //create indicators and other objects here, this is executed prior to the main trading loop       public override void Initialize(BarHistory bars)       {          // not good style to use exactly the class name as a variable          // TimeSeries divAdjC = new divAdj(bars);                    //at least give it different case          TimeSeries divadjc = new divAdj(bars);          PlotTimeSeriesLine(divadjc, divadjc.Description, "pTest", Color.Fuchsia);       }       //execute the strategy rules here, this is executed once for each bar in the backtest history       public override void Execute(BarHistory bars, int idx)       {       }       //declare private variables below    } }
1
- ago
#4
Not sure why it doesn't work but if you wrap the TimeSeries up in an Indicator, it should.
0
Cone8
 ( 6.17% )
- ago
#5
It works - post edited - it's just that the first time I ran the script with "Morningstar", WL7 hung in some sort of loop since I didn't have Morningstar dividend data. We'll need to look into that!
0
- ago
#6
Thank you, works like a charm!
0
- ago
#7
Heads-up: the Dividend item by Morningstar is broken, they have decommissioned the endpoint it was requested from.
0
- ago
#8
In the lines below, why is result being initialized once by the constructor, and then again on the line below? Am I missing something?
CODE:
TimeSeries result = new TimeSeries(bars.DateTimes, 0); result.DateTimes = bars.DateTimes;
Remember, this class is inheriting from TimeSeries, which can do it's own initialization in its constructor. In contrast, the constructor for IndicatorBase does not take a parameter, so it can't initialize. (It depends on Populate() for that instead.)
0
Glitch8
 ( 12.53% )
- ago
#9
The reason is that the class calls this base constructor:

CODE:
public TimeSeries(List<DateTime> dateTimes, bool fillNaN = true)


... with a parameter of false. This leaves it winding up with a populated DateTimes property, but a Values property with zero items. This leads to big problems when trying to plot it.

The assignment to the DateTimes property also initializes the Values property. So it's only one possible way to fix the issue here.
0
- ago
#10
QUOTE:
... with a parameter of false. This leaves it winding up with a populated DateTimes property, but a Values property with zero items.

So the constructor "breaks" the DataType with certain parameter values? I would either fix that bug in the constructor (perhaps populate .Value with all zeros) or just have the plotting routines plot all zeros like in WL6.

Either way, the current behavior is totally unexpected. :(
Thanks for the clarification.
0
Glitch8
 ( 12.53% )
- ago
#11
It's not a bug, it's working as designed and documented. There are legitimate cases where you want to pass false in that constructor. This isn't one of them.
0
Glitch8
 ( 12.53% )
- ago
#12
0
- ago
#13
But in this example, zero is passed as a parameter in the TimeSeries constructor.
CODE:
TimeSeries result = new TimeSeries(bars.DateTimes, 0);
But you're saying that zero parameter isn't setting all .Value's to zero as documented. I'm confused.
0
Glitch8
 ( 12.53% )
- ago
#14
That’s a different TimeSeries, it’s the variable “result”.

The problem Is that the “this” TimeSeries was not initialized correctly. Hope that clears up the confusion.
0
- ago
#15
Okay, I got it. I was looking at the TimeSeries in Reply# 8 (My post.) where "result" was being created, then immediately reinitialized on the following line (for "result").

But you're talking about the "this" TimeSeries in Reply# 1 instead where "false" is passed into the base TimeSeries constructor. Yes, having nothing for this.Value's could be a problem. The intent of the "false" is that the DivAdj constructor must fill all this.Value's with something under this circumstance. I agree.

Thanks again for the clarification and your time.
0
Glitch8
 ( 12.53% )
- ago
#16
Exactly! It’s kind of a tangled web the way it’s all coded, but I think we’re all clear now 🙂
1
- ago
#17
Hi, I think I have a similar problem, but I don't understand where I stumbled.
A fairly simple code for the intersection of averages, but I get an error all the time.
To get the history, I connected the finance adapter binance
Error:
Execute Exception (BTCUSDT,0) Line 46 - Object reference not set to an instance of an object.
at WealthScript7.MyStrategy.Execute(BarHistory bars, Int32 idx) in :line 46
at WealthLab.Backtest.UserStrategyExecutor.StopTests(List`1 lst, DateTime dt)
CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; namespace WealthScript7 { public class MyStrategy : UserStrategyBase {       TimeSeries _smaFast, _smaSlow;        public override void Initialize(BarHistory bars) {                              SMA _smaFast = SMA.Series(bars.Close, 30);          SMA _smaSlow = SMA.Series(bars.Close, 120);          PlotIndicatorLine(_smaFast);          PlotIndicatorLine(_smaSlow, Color.Red);       } //execute the strategy rules here, this is executed once for each bar in the backtest history public override void Execute(BarHistory bars, int idx) { if (HasOpenPosition(bars,PositionType.Long) || HasOpenPosition(bars,PositionType.Short)) {             Position p = LastPosition;             if (p.PositionType == PositionType.Long)             {                if(_smaFast.CrossesUnder(_smaSlow, idx))                   ClosePosition(p, OrderType.Market,0, "ExitLong");             }             else             {                if (_smaFast.CrossesOver(_smaSlow, idx))                   ClosePosition(p, OrderType.Market, 0, "ExitShort");             } } else {             if(_smaFast.CrossesUnder(_smaFast, idx))                PlaceTrade(bars, TransactionType.Short, OrderType.Market,0, "Go Short");             if(_smaFast.CrossesOver(_smaFast, idx))                PlaceTrade(bars, TransactionType.Buy, OrderType.Market,0,"Go Long"); } }        } }
0
- ago
#18
In the code below, you are declaring two entirely separate variables. Can you see that? From your declarations, TimeSeries _smaFast is a different variable from SMA _smaFast. Is that what you want to do?

CODE:
public class MyStrategy : UserStrategyBase { TimeSeries _smaFast, _smaSlow; public override void Initialize(BarHistory bars) { SMA _smaFast = SMA.Series(bars.Close, 30); SMA _smaSlow = SMA.Series(bars.Close, 120);

Let's change it to
CODE:
SMA _smaFast, _smaSlow; //corrected declaration //TimeSeries _smaFast, _smaSlow; //delete this duplicate public override void Initialize(BarHistory bars) { _smaFast = SMA.Series(bars.Close, 30); //REMOVE the "SMA" declaration here since it's ... _smaSlow = SMA.Series(bars.Close, 120); //... already declared in the MyStrategy block above

If this is confusing, check out the chapter in your C# textbook on controlling scoping with declarations. That should clarify it best.
1
- ago
#19
QUOTE:
In the code below, you are declaring two entirely separate variables. Can you see that? From your declarations, TimeSeries _smaFast is a different variable from SMA _smaFast. Is that what you want to do?

Thank you, it was really visual. I was always a little confused in the scopes, but in the message above you already gave an example, I just didn't know how to apply it to myself)
0
- ago
#20
All WL indicators inherit from the TimeSeries class, so it is possible to declare
CODE:
TimeSeries sma = new SMA(Close,10);
like you tried to do originally in the MyStrategy block. But in doing so, you only can employ TimeSeries methods to this new sma instance. Since .Series is not a TimeSeries method, this above approach won't work for you. So you're required to declare
CODE:
SMA sma = SMA.Series(Close,10);
so you include the .Series (and .Value) methods (which are particular to some indicators) in your indicator instance; otherwise, those two methods will not be available.

You can read up on the consequences of inheritance in your C# textbook for more explanation about this behavior as well. All object-oriented programming languages (OOPS) work this way. It's not hard once you understand the design of the language and how the methods are scoped.

QUOTE:
I was always a little confused in the scopes,

There's a WL blog article that also touches on scoping for WL7. But read over the scoping chapter in your C# book first before attempting the blog article, which is kind of advanced. https://www.wealth-lab.com/blog/anatomy-of-a-wl7-strategy
Happy computing.
0

Reply

Bookmark

Sort