Cone8
 ( 6.30% )
- ago
Because the original "One Percent a Week" strategy needed Monday's opening price to execute a limit order on the same bar, the strategy was written to "peek" at the next bar's opening price. This sort of peeking is "legal" in the sense that you can actually trade such a strategy (see below), but the downside is that the backtest has to end one bar early to read the opening price of the next bar, which disables signaling (no alerts).

Recently we added One Percent a Week v2 to the Sample Strategies. The v2 version uses the ExecuteSessionOpen() so that you can get the entry limit signal a second after Monday's open. That part works.

The problem is that [Pre-Market] the strategy is not signaling to exit the position that it opened yesterday. That's not expected and I'm investigating the disconnect.

For this week, anyone trading the strategy just needs to place a GTC Sell at Limit 1.01 x entry price, which would be 41.33 x 1.01 = 41.74.
0
2,063
37 Replies

Reply

Bookmark

Sort
Cone8
 ( 6.30% )
- ago
#1
Everything is okay. The entire strategy is programmed in the ExecuteSessionOpen() method. That means it won't run that method "today" until the market's session opens at 09:30 ET.

Consequently, if you're Auto-Trading this strategy using the Strategy Monitor, this is the configuration -


While this is the best you can do to get the entry signal, it will incur a small delay to get the exit limit order placed - a second or two after the open. This delay could work for or against you.

Notes!
1. Trade this strategy at your own risk. We are not recommending this or any other strategy.
2. I'm not sure yet, but the v2 strategy may need to be modified to generate the MOC order when it runs on Friday's open (for an open position at the end of the week) . The way it's programmed in ExecuteSessionOpen, I think this will occur on Thursday - which would have been a good thing last week!
0
Cone8
 ( 6.30% )
- ago
#2
I think the following logic is correct to generate a signal for the MOC order on Friday - if a position is still open then. I'll verify it and report.

Important!
This is for the routine in ExecuteSessionOpen()
CODE:
            if (HasOpenPosition(bars, PositionType.Long))             {                double target = LastOpenPosition.EntryPrice;                if (LastOpenPosition.ProfitPctAsOf(idx) > -0.5)                   target = target * 1.01;                                Backtester.CancelationCode = 642;                PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, target);                             Backtester.CancelationCode = 642;                if (idx < bars.Count - 1)                {                   if (NextBarIsLastDayOfWeek)                      PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose);                }                else                {                   //last bar: if today is the last trading day of the week, signal MOC for the open position                   if (bars.TomorrowIsLastTradingDayOfWeek(idx - 1))                      PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose);                }             }
The premarket action isn't looking very promising for a favorable outcome this week - but the week is young!

Here's a tentative "v3" script in its entirety -

CODE:
   using WealthLab.Backtest;    using System;    using WealthLab.Core;    using WealthLab.Indicators;    using System.Collections.Generic;        namespace WealthScript123    {       public class OnePercentV3 : UserStrategyBase       {          public override void Initialize(BarHistory bars)          { }                 public override void Execute(BarHistory bars, int idx)          { }                    public override void ExecuteSessionOpen(BarHistory bars, int idx, double sessionOpenPrice)          {             bool NextBarIsStartOfWeek = _lastBarofWeek == idx;             bool NextBarIsLastDayOfWeek = bars.TomorrowIsLastTradingDayOfWeek(idx);             if (NextBarIsLastDayOfWeek)                _lastBarofWeek = idx + 1;             if (idx - 1 == _lastBarofWeek)                SetBackgroundColor(bars, idx, WLColor.Silver.SetAlpha(32));                          if (NextBarIsStartOfWeek)             {                mondayOpen = sessionOpenPrice;                tradedThisWeek = false;             }                          if (HasOpenPosition(bars, PositionType.Long))             {                double target = LastOpenPosition.EntryPrice;                if (LastOpenPosition.ProfitPctAsOf(idx) > -0.5)                   target = target * 1.01;                                Backtester.CancelationCode = 642;                    PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, target);                             Backtester.CancelationCode = 642;                if (idx < bars.Count - 1)                {                   if (NextBarIsLastDayOfWeek)                      PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose);                }                else                {                   //last bar: if today is the last trading day of the week, signal MOC for the open position                   if (bars.TomorrowIsLastTradingDayOfWeek(idx))                      PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose);                }             }             else             {                if (!Double.IsNaN(mondayOpen) && !tradedThisWeek)                {                   double mult = 0.99;                   PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, mondayOpen * mult);                   if (NextBarIsLastDayOfWeek)                   {                      Backtester.CancelationCode = 642;                      PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose);                   }                }             }          }             //same bar exit     public override void AssignAutoStopTargetPrices(Transaction t, double basisPrice, double executionPrice)          {             //t.AutoProfitTargetPrice = executionPrice * 1.01;             tradedThisWeek = true;          }              //declare private variables below          private double mondayOpen = Double.NaN;          bool tradedThisWeek = false;          int _lastBarofWeek = -1;       }    }
0
Cone8
 ( 6.30% )
- ago
#3
In case anyone copied the v3 code, I just fixed an index oversight above for for bars. TomorrowIsLastTradingDayOfWeek().

The v3 strategy above will be in the Sample Strategies for Build 42.
1
- ago
#4
Hi Cone.
I updated WL8 to build 42. I'm wondering why don't I see the One Percent a Week v2 under Sample Strategies. Any ideas?

Update: I just copied your code from above.

Thanks,
Mike
0
Cone8
 ( 6.30% )
- ago
#5
v2 didn't get copied because I forgot to add that to the project file.

In any case, use the v3 code to set it up in the S. Monitor for full automation, even for Friday's MOC order*.
Set for polling at 09:30:00 ET each day.
Don't forget to click Activate and enable Auto-Place

* UNCHECK "Use MOC... " in Preferences > Trading.
And use at least 30 seconds (can be less for IB, which is quick) before the close for the market order.

1
- ago
#6
Thank you.
Is there any reason why Live Positions isn’t checked?
0
Cone8
 ( 6.30% )
- ago
#7
Good eye! I hadn't looked to the left since I was concentrating on giving the setup on the right.

This is a development machine and I'm constantly changing my configuration to test different things. There are scenarios for which you might not want Live Positions, but trading this strategy isn't one of them - I'd check that.
1
- ago
#8
Whenever I run this, I keep getting this error...

"
Timestamp Source Message Exception
"8/21/2023 13:32:57:539" "Alpaca" "Error Replacing order: One or more errors occurred. (order is not open)" "One or more errors occurred. (order is not open) Inner Exception: order is not open"
"

I realize that it is supposed to be run around 8:30am, but I've tried to run it a few times and haven't gotten it to show a pending buy order on the Alpaca website. Any advice?
0
Cone8
 ( 6.30% )
- ago
#9
QUOTE:
Whenever I run this,
What's "this" exactly? And how are you running it?

QUOTE:
Timestamp Source Message Exception
"8/21/2023 13:32:57:539" "Alpaca" "Error Replacing order: One or more errors occurred. (order is not open)" "One or more errors occurred. (order is not open) Inner Exception: order is not open"
Pretty odd error since it's trying to replace an order. That shouldn't happen since we're talking about 1-order EOD script above. You're not trying to run "this" on intraday data, are you?
0
- ago
#10
"This" = The script from Post #2 on TQQQ.

I used the same settings that you described in Post #1.
0
Cone8
 ( 6.30% )
- ago
#11
... and you're right clicking and using "Run Strategy Now"?

In any case, I'm not seeing the problem placing this order with Alpaca. The question is how you got to the "Replace Order" method. Something else is going on there.

Have you placed orders successfully with your live account?
Try just placing an order manually from the Order Manager for 1 share TQQQ, Buy Limit 36.49. Does it go Active?
0
- ago
#12
Nevermind. I just cleared everything out of Order Manager and it looks like it's working just fine. Thanks for the help.
1
Cone8
 ( 6.30% )
- ago
#13
The market has a way of finding things you missed.
I tried to make the v3 script a complete "Auto-Trader" for Glitch's One Percent a Week, but it doesn't account for Auto-exiting if the entry occurs on the last day of the week - and one looks like it's going to happen tomorrow.

It's not a very frequent event, but if you check the backtest history, the last time it happened on the last day of the week was on 1/24/2020... and before that one 3/24/2016 and 4/4/2014! 3 times in the last 9 or 10 years.

Luckily we can make Auto-Trade profit target exit order when filled on the same bar, but tomorrow will be the first test for this modification - and there's a good chance it's going to happen.

CODE:
      public override void AssignAutoStopTargetPrices(Transaction t, double basisPrice, double executionPrice)       {          //revising       }

With Auto-Place enabled, you'll send in the Buy at Limit order for tomorrow's open. If filled, the Order Manager will automatically place a Sell At Limit order for a 1% profit. If the target hits, you're done for the week.

If it doesn't hit the target, you'll have to manually exit at the close. At this time, I don't see a way to create that MOC order when the trade is filled on "Friday".

I'll make a v4 script with the modification above. If you want, just open your v3 script, Clone it, and save it as v4 with this replacement for the AssignAutoStopTargetPrices() method. Good luck!
0
Cone8
 ( 6.30% )
- ago
#15
Revising code, testing.
0
Cone8
 ( 6.30% )
- ago
#16
We're going to refrain from a v4 script since the original strategy always held until the Close on "Friday" (an easy way to say the last trading day of the week). It's not possible to Auto-Place that MOC order with the same bar entry. So, in all cases, currently, you'll need to manually place that order if required.

Nonetheless, if you want to enable a same-bar profit target on "Friday", you can create your own v4 script and replace the AssignAutoStopTargetPrices method with this block of code -

CODE:
    //same bar exit for Friday fill     public override void AssignAutoStopTargetPrices(Transaction t, double basisPrice, double executionPrice)          {             tradedThisWeek = true;             //set same-bar limit orders if executed on the last day of the week                   DateTime dte = t.ExecutionDate == DateTime.MinValue ? t.Bars.Market.NowMarketTime : t.ExecutionDate.Date;             DateTime lastTradingDateThisWeek = dte.AddDays((int)DayOfWeek.Friday - (int)dte.DayOfWeek);             do             {                if (t.Bars.Market.IsTradingDay(lastTradingDateThisWeek))                   break;                else                   lastTradingDateThisWeek = lastTradingDateThisWeek.AddDays(-1);             }             while (lastTradingDateThisWeek.DayOfWeek != DayOfWeek.Friday);                 if (dte == lastTradingDateThisWeek)             {                Backtester.CancelationCode = 642;                t.AutoProfitTargetPrice = executionPrice * 1.01;             }          }
0
- ago
#17
for the MOC orders to work, can I open Wealth-Lab 5 minutes before close? I'm on a VM so running it all day would incur a lot of expenses.

Is there a 1 minute script I could enable to trigger a MOC order?
0
Cone8
 ( 6.30% )
- ago
#18
Sure, that's a good idea. You could launch an instance of WealthLab that opens a Workspace with a script that's scheduled to run at, say, 15:50. It will use LivePositions preference to load a position if it exists, and generate a MOC order for the shares.

This script will work for that -

CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Data; using WealthLab.Indicators; using System.Collections.Generic; namespace WealthScript31 { public class MarketCloseExit : UserStrategyBase { public override void Initialize(BarHistory bars) { } public override void Execute(BarHistory bars, int idx) {          Position foundPosition = FindOpenPosition(0);                    if (foundPosition != null)     if (idx == bars.Count - 1)     {                ClosePosition(foundPosition, OrderType.MarketClose);     } } } }


For this application, you can configure for a true broker MOC order. Once it runs, you're done.

Recommended Trading Prefs -



Note! If the broker doesn't support MOC (like C2), make sure to uncheck this and let WealthLab place the market order n seconds before the close.
0
Cone8
 ( 6.30% )
- ago
#19
And here's the "Cadillac" version. This one checks to make sure it's the last trading day of the week. That way you can even run it in the middle of the week and it won't sell the LivePosition.

CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Data; using WealthLab.Indicators; using System.Collections.Generic; namespace WealthScript5 { public class MarketCloseExit : UserStrategyBase { public override void Initialize(BarHistory bars) {          //set same-bar limit orders if executed on the last day of the week                DateTime dte = bars.Market.NowMarketTime;          DateTime lastTradingDateThisWeek = dte.AddDays((int)DayOfWeek.Friday - (int)dte.DayOfWeek);          do          {             if (bars.Market.IsTradingDay(lastTradingDateThisWeek))                break;             else                lastTradingDateThisWeek = lastTradingDateThisWeek.AddDays(-1);          }          while (lastTradingDateThisWeek.DayOfWeek != DayOfWeek.Friday);          _mocEnabled = dte == lastTradingDateThisWeek;        } public override void Execute(BarHistory bars, int idx) {          if (!_mocEnabled)             return;          if (idx == bars.Count - 1)          {             Position foundPosition = FindOpenPosition(0);             if (foundPosition != null)                ClosePosition(foundPosition, OrderType.MarketClose);          } }       bool _mocEnabled = false; } }
0
- ago
#20
This is stellar. Thanks Cone, appreciate the help.
1
- ago
#21
So the MarketClose script sent a signal correctly. However, my TDAmeritrade account rejected the order, so it will hold over the weekend. Did my TDAmeritrade account reject the MarketClose order because the Sell limit order is still open? If so, what can we implement in the MarketClose script to cancel the open limit order?

Here is Friay's order history found on my TDAmeritrade:
0
Cone8
 ( 6.30% )
- ago
#22
The strategy sets up the orders with the same CancelationCode, so the Limit order is canceled before the Market order is sent. One factor that may have contributed to this failure is that TDA is so damn slow canceling and activating orders. Anyway, we'll have to make sure we're getting the cancel confirmation before sending the other order.

fwiw, it worked on Alpaca, IB, and C2.

Note that you've got several hours to sell in the after market too - at even a better price this time -

1
Cone8
 ( 6.30% )
- ago
#23
Did anyone get a v3 trade trigger at 35.68 on the open today?
If you did, you can get out NOW for a tiny profit. The trade should not be "on" yet.
0
Cone8
 ( 6.30% )
- ago
#24
Now you can get out with a nice profit - one of the rare times that a mistake works in your favor.

Although Wealth-Data looks correct in our database, I'm getting a bad bar of data locally. (It might just be me.)
0
- ago
#25
So I understand most of the code in the script, except for this method at the bottom. If I understand correctly, the method is meant to allow same bar trade exits (as found on messages #13 and #16 of this discussion board). However, it looks like this version is just changing the "tradedThisWeek" boolean to true. The removal of this method creates a drastic change in the equity curve, so I was just wondering how it changes the backtest process.




0
Cone8
 ( 6.30% )
- ago
#26
The logic is to trade once per week.
If you change the logic, then you've got a different system.

QUOTE:
If I understand correctly, the method is meant to allow same bar trade exits
The method is only being used to indicate that a trade has occurred. The code that could be used for a same bar exit is commented out.
1
- ago
#27
Hi Cone / Glitch,

I have the V3 strategy set up in the strategy monitor. This is the first week letting it auto trade on Monday.

On the open yesterday, WL sent an order to IBKR for 1% below Friday's open, not Monday's open. It seems like it didn't have the current bar's data yet.

I can't get WL to see the current day's bar unless I use streaming data, but you say above to use polling.

Am I missing something? Is there something else I should configure to make sure WL has fresh data on the open?

Thanks!
0
Cone8
 ( 6.30% )
- ago
#28
We noticed that W-D was sluggish to respond the last couple days. We fixed that, but it's probably not good that ExecuteSessionOpen returns the last close if it's not able to get Monday's open. We'll have to review that.

I was out yesterday, but I usually this strategy running as a test every day (except yesterday) in the S. Monitor, polling, and trading an Alpaca test account. It usually works flawlessly!

Do you have Yahoo! backing up Wealth-Data?
In other words, is Yahoo! Finance checked in the list of Historical Data Providers, and below Wealth-Data?
0
- ago
#29
Yes, my Historical Providers go IBKR, then WealthData, then Yahoo.

However, when I type in TQQQ in the testbed on all 3 providers, they all show yesterday as the latest bar.

Screenshot from right now:

0
Cone8
 ( 6.30% )
- ago
#30
That's correct. You won't get an update for the Daily bar until the market close.

The GetSessionOpen() feature only gets the session opening price from a provider that will return it for the current date.

And, if the open price of the session were not available, the logic that I see won't allow it to create a trade because the price will be a "NaN".

What was the Signal's limit price you got yesterday?
1
- ago
#31
The limit price was 43.06 which is 1% down from Friday's TQQQ open of 43.49.
0
- ago
#32
This also shows in the strategy monitor log, in case it helps

0
Cone8
 ( 6.30% )
- ago
#33
This is correct.
It looks like you Activated the strategy today (about 3 hours late), and it loaded all the Daily bars up to yesterday.

When the Strategy Runs, it hits ExecuteSessionOpen, which creates another request to get today's opening price. That's not part of the log because it's something the strategy does.

It ran the strategy, and said there were 0 signals, but there should have been a limit sell today. However, if you're running with "Live Positions" and didn't have the TQQQ position, then this part would make sense.

After completing the run it scheduled the next run for 06:30 local, which means you're on PST.

It's not clear to me how or why you got the wrong price yesterday.
0
- ago
#34
Those screenshots are just from me testing today, sorry for the confusion. I was just trying to find some kind of log that would tell me what data WL is seeing.

The run on monday that generated the bad limit price was set for 9:30:00 EST in strategy monitor and ran on time.

Should ExecuteSessionOpen be able to get the opening price from IBKR? Or does that data only come from Wealth Data or Yahoo?

Is there anything I can add to the code to log what opening price the strategy is seeing? Might be helpful to debug for next week.
0
- ago
#35
I got the same wrong price on Monday.
0
Cone8
 ( 6.30% )
- ago
#36
The problem was IB's Daily bar - it's STILL returning an opening price of 43.94 - even higher than Friday's close! Data is everything.



Wealth-Data or IQFeed is a better choice for this feature.
I recommend that you stack the providers in this order - just ignore providers you don't have

Wealth-Data
IQFeed
Yahoo!
Q-Data (I'd normally put Q-Data ahead of Y!, but Q-Data will not return partial bar data.)
IB
1
- ago
#37
Thanks Cone! I made those changes.
1

Reply

Bookmark

Sort