Max open positions PosSizer
Author: Panache
Creation Date: 4/27/2014 2:23 PM
profile picture

Panache

#1
I'd like to incorporate Max open positions from Position Options into a custom position sizer. Before I reinvent the wheel, is the code for that available somewhere? If not, would you be willing to share it?
profile picture

Eugene

#2
The code is not available, but it's so simple that it doesn't justify the "don't code what you can beg, borrow or steal" motto for someone familiar with Visual Studio like you. ;) Just compare ActivePositions.Count with the number of max open positions allowed. That's it.
profile picture

Panache

#3
That's what I assumed. What I haven't been able to figure out is how to select only the highest priority alerts within the position sizer:

1. Is there a way to sort the Alerts Object directly, or do I have to create a list from it and sort the list?

2. Is there a way to do something like list.RemoveAt(x) with the Alerts Object, or what do I need to do to get down to the required number of Alerts?
profile picture

Eugene

#4
There is no such thing as "Alerts Object". To pick only the highest priority, you compare the currentPos.Priority against a given priority threshold.
profile picture

Panache

#5
When I try to resize Alerts, ie. BuyAtMarket(Bars.Count) using currentPos,Priority, I get a quantity of zero. It works as expected on Trades, ie. BuyAtMarket(Bars.Count -1).

Is this the problem of

(68057) "Max Entries per Day" PosSizer is sizing Alerts incorrectly (zero size) Backtested values are correct and/or

(62853) In PosSizers, the Candidates list is empty for Alert sizing. The collection functions as advertised for historical backtests, but for sizing Alerts at Bars.Count it is not being populated.

If so, so that I don't keep beating a dead horse, have you been able to determine if the problem is that Priority is not being passed to the Position Sizer?

Second, I see you've done a lot of work on extensions in the last couple of days. Can you give me a feel for how high these issues are on your priority list, ie. should I be recoding my strategies to work around this, or is this something that might get fixed with the next update.
profile picture

Eugene

#6
1 - I have no idea how you resize Alerts so this could be anything: either live bug 62853 (only if your PosSizer uses Candidates) or an error in your code.

2 - These issues have never been on my todo list. I develop extensions and these are Wealth-Lab thick client's issues, and MS123 is not involved in its development which is performed by Fidelity. And unfortunately, neither 68057 nor 62853 will get fixed in the upcoming update.
profile picture

Panache

#7
CurrentPos.Priority causes the problem as well. Perhaps you can tell me what I'm doing wrong.

To reproduce the issue, I cut and pasted the code for MaxEntriesPerDay from Creating a PosSizer in Wealth-Lab Pro® and added the necessary references to the .dll's in my Program Files directory. I then commented out the code limiting the max entries and added 3 additional lines below calculate position size. (I didn't include the entire PosSizer below.)

CODE:
Please log in to see this code.


To reproduce the problem easily, I also wrote a simple strategy to generate 10 buy signals, each with a different priority of 1 through 10.

CODE:
Please log in to see this code.


If I change the strategy to buy on Bars.Count - 1, the PosSizer works as expected, with the trades with priorities 6-10 being limited to 100 shares. When I run the strategy as written with my additional 3 lines of code in the PosSizer commented out, it also works as expected, with all Alerts being for the same quantity. However, when my 3 additional lines of code in the PosSizer are active, all of the Alerts have a quantity of 0.
profile picture

Eugene

#8
Now try the following:
CODE:
Please log in to see this code.

You see that PrintDebug did not trigger. Why? Because each one of these Positions is null yet: you're sizing an Alert. Consequently, currentPos is null in the PosSizer too. When the PosSizer is called to size Alerts, there is no Position so approach above will not work.

The good news is that this has been answered in the FAQ for years...

How to pass a value from Strategy to PosSizer for Alert sizing?

...and in the QuickRef, too. See Bars.Tag property there.
profile picture

Panache

#9
Thank you!

For those who might read this thread in the future, I believe adding Bars.Tag passes a single value for the bar, so changing LastActivePosition.Priority to Bars.Tag in my simple strategy wouldn't achieve the intended result, because only the last priority (10) would be passed to the PosSizer. You would have to assign each different priority to a different symbol (which is the way I would assume most strategies operate).

Getting back to my original task of picking only those Alerts with the highest priority, I assume that because of the bug relating to Candidates, a PosSizer can only select Alerts with a priority (Tag value) higher than some value set in the PosSizer. However, a PosSizer can't sort multiple Alerts generated by a strategy to pick the one with the highest priority.
profile picture

Panache

#10
I thought I would share my strategy work around based on a number of things which have previously been published, which solves several issues:

1. "Extra" alerts aren't generated -- every Alert generated is traded to keep the strategy at the number of positions specified in the strategy parameter NumberOfPositions. Run this strategy on only one of the symbols in your DataSets, not the entire DataSet.

2. The strategy contains it's own position sizer, so that the number of shares in each Alert is the number to be traded. Make sure you set the Position Size to "Override in script".

3. The strategy parameter StartDate lets the user pick the date when the strategy starts trading -- the Date Range is only used to determine the amount of data loaded. Therefore, assuming enough data is loaded, when viewing Performance, etc., there is no "ramp up" while any SMA or other series is filled. (The downside of doing it this way, however, is the the Benchmark Buy & Hold is not a true comparison, since it starts on the first day of the Date Range, not the StartDate.)

If you see any ways to improve upon this, please don't hesitate to say so.

CODE:
Please log in to see this code.
profile picture

Eugene

#11
Thanks for sharing. BTW, to speed up looping through Positions you can change this:
CODE:
Please log in to see this code.
This way:
CODE:
Please log in to see this code.

profile picture

Panache

#12
What I use your improvement, I get the following error:

Runtime error: Collection was modified; enumeration operation may not execute
at WealthLab.Strategies.MyStrategy.Execute()
at System.Collections.Generic.List 1.Enumerator.MoveNextRare()

CODE:
Please log in to see this code.


Any ideas?
profile picture

Eugene

#13
Sorry. You need to use for on ActivePositions - not foreach. Code above edited.
profile picture

Panache

#14
Here's another head-scratcher:

When I use your code, some of the positions are not closing at the correct time. I decreased the numberOfPositions to 4, to make it easier to show.

With:

CODE:
Please log in to see this code.


I get 8 Alerts and 4 open positions, which is the way it should be. (See Positions.jpg)

With:

CODE:
Please log in to see this code.


I get 11 Alerts and 7 open positions. (See Active Positions.jpg)
profile picture

Panache

#15
The problem is starting from ActivePosition[0] and increasing to ActivePositions.Count. I'm guessing that when you process the first position, it is no longer active, and ActivePositions.Count decreases by 1. Therefore, the way it is written, the for loop ends before all of the ActivePositions are sold. If you start selling at ActivePositions.Count - 1 and decrease to ActivePositions[0] it works.

CODE:
Please log in to see this code.
profile picture

Panache

#16
When I run this strategy against a symbol in a large DataSet (ie, NYSE stocks) even over a relatively short period of time (ie. startDate 2014), I get no trades or alerts. I realize this results in something on the order of half a million potential trades. Is there a memory constraint that is preventing the strategy from working as expected?
profile picture

Eugene

#17
You'll instantly know by experiencing a System.OutOfMemoryException. You're not running 32-bit WLP, are you?
profile picture

Panache

#18
No, I'm running the 64 bit version with 8GB of memory, and it's not throwing any errors. It is just that when I run it against a big DataSet, I don't get any trades.

To confirm that the problem is the number of symbols, I created a DataSet which is a subset of the first x symbols in all stocks listed on the NYSE and ran the strategy against AA every time. If there are 130 symbols in the DataSet everything works fine. If the DataSet is increased to 149 symbols, instead of making 4 trades per day, it only makes 3 and give me 6 Alerts. If I increase the number of symbols to 268, I'm down to only 2 trades per day and 4 Alerts. With 300 symbols, I'm down to 1 trade per day and 2 Alerts. At 375, I don't get any trades or alerts. (I didn't test it by adding one symbol at a time, so these are not the exact cut-off's.) That's what why I thought it might be a memory issue.

It's not that the strategy is changing because of the symbols being added. At 275 symbols, the 2 trades per day are always ACT, ADS and/or AMG. Adding 25 symbols starting with B, thereby increasing the size of the DataSet to 300 symbols, results in only one trade per day, which was ADS every time.

Any ideas?
profile picture

Eugene

#19
Can not imagine how 1 year of data for 300 symbols could present any problem even on 32-bit version with say 3Gb RAM. There may be a subtle logic/code error (I didn't anayze your code) that you can catch using this technique:

How can I debug my trading strategies in Wealth-Lab?
profile picture

Panache

#20
Eugene's instincts were correct -- the problem was neither with the code nor Wealth-Lab.

The problem was two arbitrary defaults that don't work well together. The strategy set the Priority as Close[bar], ie. the highest priced stocks. It also rounds the shares down to the nearest 100. Therefore, with the Capital set at $100,000, when the DataSet includes stocks like Berkshire Hathaway Class A ($190,000 per share), the Share Size becomes 0, resulting in no trades!

For anyone who may be interested, here's the strategy with all the changes, which can be used as a framework to trade your strategies. WARNING -- This can take a very long time to run on large DataSets with big Data Ranges -- remember, this is a work-around.

CODE:
Please log in to see this code.
profile picture

Eugene

#21
QUOTE:
This can take a very long time to run on large DataSets with big Data Ranges

You might try to squeeze a couple milliseconds by removing the try/catch block around SetContext if you're sure about the data. Try/catch has some performance overhead.