Intraday Pair Trading Strategy
Author: JWD
Creation Date: 9/25/2011 10:02 AM
profile picture

JWD

#1
Good morning,

I have zero programming experience, full time job and family so don't have ability to learn at this point in time. I have tried to find someone locally who can do this for me, but have had no luck so I am resorting to posting to the WL community in hopes that someone can help me out. I am looking for code to automate a strategy that I currently execute manually. Here are the rules:

1. My strategy always involves a pair of stocks, which change from time to time. So for these purposes I will call them "ABC" and "XYZ".

2. Totally intra-day; I execute a buy trade at some point after the session open and exit that same position at some point on the same day, never holding a position overnight.

3. I use one-minute streaming windows in Wealthlab 6.

4. Only one position can be entered per day, either ABC or XYZ. Which one to buy depends on which one hits the "buy in price trigger" first.

5. The "buy in price trigger" is a variable. It is a function of the session's open price for each of ABC and XYZ. In other words, if the buy in price trigger is set at 0.5%, then I buy whichever of ABC or XYZ hits 0.5% above its open price first. Again, cannot buy both in any single day, and cannot buy in more than once.

6. Occasionally the buy order triggers within the first 30 seconds of the session, so I need the strategy to begin working immediately on the open.

7. I sell the position at the first to occur of (a) hitting the "stop loss price", or (b) day's market close.
Like the "price trigger", the "stop loss price" is a variable, a function of the "buy in price trigger". So for example if the stop loss price is set at 0.5%, then a sell order is triggered if the position falls 0.5% from the buy in price trigger.

8. Position sizing is the most complicated thing here. The position size is the number of shares (rounded to the nearest hundred) that can be purchased with an amount of money defined as follows:

If account balance < $4000,
Then money used = account balance - $2000
Else money used = (account balance - $2000) + (account balance - $4000)^1.1

Would be very grateful for any direction on how this can be coded or any other thoughts or suggestions.

Thanks,

JWD

profile picture

Cone

#2
5. What happens if both stocks hit the 0.5% target on the same bar? I'm primarily speaking to the backtesting part of this, however, since Wealth-Lab doesn't provide a "one cancels all" type of order for secondary symbols, there isn't an automated way to handle the "on the same bar" scenario anyway.

Just thinking out loud, the best I think you could do for this scenario is to exit one of the positions on the next bar, or handle it some other way.

6. Getting the opening price before the first minute is also a problem if you're not there to run the script manually right at the 09:30 open; although there may be a work-around by scheduling the Strategy Monitor to run a "daily" strategy to enter the opening orders based on the opening price.

8. This can be done for backtesting in a PosSizer, but for live trading you'd simply enter a value for raw-profit sizing based on your account balance, which is not available to strategy scripts/logic.
profile picture

JWD

#3
Cone - Thanks, here are my responses.

5. Good question. I use one-cancels-the-other orders so it's not an issue for me right now in manual trading. I can see where this would be problematic in backtesting, but I don't really need this code for backtesting. But are you saying this is also an issue for automation? It happens VERY rarely (like probably once a year at MOST). I'm not betting the farm here so I think I can assume this risk away.

6. I would be interested in implementing the work-around you describe, as one of my goals here is not to have to be at my computer at 9:30 every morning. Do you mean that this can't be coded but possibly can be acheived through the settings in the strategy monitor? I'm a beginner with WL and don't even have a good grasp on the terminology here so sorry for being dense. In any event I want to automate this as much as possible even if it means I have to be at the computer every morning.

8. OK.

Thanks Again.

profile picture

Cone

#4
I've thought about this some more, and even though we can get those entry orders in for you using a Daily schedule in the S. Monitor, the problem remains canceling the opposite order when the other fills.

I think "keep it simple" applies here, which means waiting for that first minute to enter the orders. That way, the 1-minute strategy can work the orders per your design. If one of the symbols hypothetically hit the entry stop in the first minute, the strategy could compensate by attempting to enter at limit (which could result in a better fill). The only risk here is missing the trade, but I think it's a good tradeoff considering the complex work-arounds required.

You can even evaluate how this worked using a backtest... give me a few minutes to whip it up.
profile picture

Cone

#5
Given the constraints mentioned above, give this a try.

Notes:
1. You have to change Symbol2 from "FAZ" to your secondary symbol.
2. Set your Raw Profit sizing.
3. For a backtest load as many bars as you want (years). For streaming and training, load a minimum of 400 1-Minute bars (or at least 2 days).

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

JWD

#6
Awesome, thanks so much. I will try this when I get home from work. One quick question - if I want to change the buy in % and the stop loss %, how would I do that? Thanks
profile picture

Cone

#7
Use the Strategy Parameter slider at the below the Data Tree.
profile picture

Cone

#8
Please recopy and compile the script from above. There was at least 1 copy and paste error, and, I've made an adjustment in the limit order logic since Fidelity would actually cancel that order if price were already below the limit price. In this case, a market order is issued.

Also, for the backtest, the script detect the trades that were completely missed for the data and outputs those results to the debug window.
profile picture

JWD

#9
This is great, thanks again!!! Quick question - the buy in % and stop loss % are variables and are not always the same. For example, I could have a situation where the buy in % is equal to 0.5% from the open price and the stop loss price is 0.6% from the buy-in price. Does the code allow this? Thx
profile picture

Cone

#10
It's just a minor edit, completed above.
profile picture

JWD

#11
Cone - Awesome. I compared this against my own results daily going back several years and it's right on.
The only issues are:

(1) Minor losses due to having to wait until the next bar after an alert is triggered, I assume nothing can be done about this . . .

(2) I miss some key trades at the first bar as discussed above. If the trade can't be executed within the first minute, is there a way to avoid missing the trade completely by triggering the trade at the opening of the second bar if the high price of the first bar was at or above the "buy in trigger price"?

(3) In the backtest, occassionaly both stocks are purchased and this can result in a double loss. I don't know if this helps at all, but I have a segregated account to run this strategy so there is only one position open in the account at any time. Is there a way to cancel the second order based on the number of open positions in the account (because the first order has already triggered, creating the open position)?

(4) Just once in the backtest, the buy and stops occured in the same bar, triggering both trades. In the back test, this resulted in (i) a position in the first stock that remains open forever, and (ii) a position in the second stock that gets closed out. What would happen in live trading? Would prefer not to enter a trade at all in this situation.

Are there quick fixes to any of these? In any event, thanks so much again for all of your help this exceeds my expectations by a mile!
profile picture

Cone

#12
1) Give me an example, because there can be only [a few] missed opportunities for not getting a position during the first minute of the day, not "losses". These are displayed in the debug window.

2) Sure, you could enter at market at the open of the second bar, but, then the savings you get for entering those trades at limit will be canceled out. It's worth investigating to see the difference though.

3) I forgot to handle that case because I tested FAS/FAZ and they always move opposite of each other. Anyway, this was the other tradeoff. Either you manage both positions for the full day, or you sell one of them immediately on the next bar. Which should it be? You tell me.

4) that's symptom of previous problem - the situation is not handled yet.

Another way to handle it is to not use AtStop orders. If you always use "AtMarket" by waiting to see if the previous close had exceeded the "stop In" price, then you can selectively choose which trade to enter. This is the only way to avoid the possibility of both trades occurring on the same bar.
profile picture

JWD

#13
Thanks for helping me understand. I think all of the 4 issues are related somewhat. Could this work?:

At the open of the second bar,

(i) If just one symbol hits the buy in price trigger in the first bar, then place a market order for that symbol.

(ii) If both of the symbols hit the buy in price trigger in the first bar (very unsual), then place a market order for the primary symbol.

(iii) If neither symbol hits the buy in price trigger in the first bar, then place buy stop orders for the two symbols per the basic system rules.

In answer to #3, in any other bar after the first bar, if both symbols hit the buy in price trigger in the same bar, sell the secondary symbol at the next bar and keep the primary symbol.

profile picture

Cone

#14
I'll work on it tomorrow. That script isn't ready for prime time anyway. It needs compensation for short days, and the entry logic actually misses putting out the signals at the end of the first bar. I think that's what you were saying before, but I didn't catch it. Some rearrangement is required.
profile picture

Cone

#15
Easier than I thought.. just had to remove an "else".

The code is re-inserted above. I gave you a parameter choice between using the Market order and Limit order for first-bar trades so that you can test the difference. 0 = use the Limit strategy (and risk missing the trade), or 1 = enter at market. However, note that even the limit strategy will use a market order if the first bar's Close is below the entry trigger price (i.e., the High of the first bar had hit or exceeded the trigger price, but it closed lower).

Test it, and make sure you understand it.
profile picture

JWD

#16
This is great. And I love the ability to choose between the market and the limit order. You were right, the limit order usually is the way to go.
profile picture

JWD

#17
OK so I ran the strategy live this morning with auto-stage (but not auto-place) using the stocks you are using, FAS/FAZ. It staged 4 buy stop orders for the secondary symbol and none for the primary symbol. It then placed over a dozen sell stop orders for the secondary symbol (which triggered the buy order in the 2nd bar based on the entry parameter) over the next 10 minutes before I had to shut it down and go to work. It seems to be staging the orders in pairs (i.e., sometimes 2 at a time as opposed to always 4 at a time). I did not place any of these orders including the buy orders. Is this all just a result of staging but not placing the orders? Thanks!
profile picture

Cone

#18
QUOTE:
It staged 4 buy stop orders for the secondary symbol and none for the primary symbol.
Could it be because the secondary symbol hit the trigger on the first bar?

Re: multiple orders
If an order is "Staged", it's sitting in the Orders tool ready for YOU to place. These will just pile up in the queue if you don't do anything with them. Orders that are "Placed" are live in the market. Please see the User Guide about Staging and Placing orders, and other notes about Paper trading.
profile picture

JWD

#19
Sorry, I'm using my terms too loosely, let me be more precise. I should drink more coffee before trying to think or type.

I ran the strategy as a test only this morning with auto-stage, but I was not watching the orders window, only the strategy monitor window, so from now on I will just refer to the alerts that were generated as opposed to any orders that may or may not have been staged. I did not place any orders (on purpose - test only at this point).

I don't think either symbol hit the buy in trigger in the first bar (I used 1% from open). I think FAZ hit the buy in price in the 3rd bar (60.49.)
However, after the first bar, the strategy generated 4 buy stop alerts for FAZ and none for FAS. And then after FAZ did hit the trigger, the strategy began generating multiple sell stop alerts for FAZ.

My goal is to ultimately run this strategy with auto-trading so that the orders are automatically staged and placed. Based on the multiple alerts being generated, I'm just concerned that these will result in mutiple orders in the autotrading scenario. But since I am only talking about alerts being generated as opposed to orders being staged or placed, maybe there is no cause for concern?

Thanks

profile picture

Cone

#20
It's actually impossible for the Strategy to have generated more that 1 BuyAtStop order for each symbol when it is run once. Consequently, the strategy is designed to be run on ONE symbol - the primary "clicked" symbol. Do not select a DataSet, select only one symbol, the primary.
profile picture

JWD

#21
Hey Cone-

The above code works perfectly in the backtest. I have been running this strategy live for about six weeks and have identified a couple quirks and wanted to see if anything can be done about them.

1. When a BuyAtStop order is filled, the other BuyAtStop order is not automatically cancelled, but should be, if possible. Right now I have to do this manually.

2. Sometimes after the BuyLimit or BuyAtStop order is filled, the SellAtStop order is never placed. I don't know why it works sometimes and not others, I haven't been able to identify any sort of pattern here.

Your input is appreciated,

JWD
profile picture

Cone

#22
1. Of course you could do that for backtesting, but it would require an Orders tool enhancement for live trading; and even then there would be no guarantee that the opposite order could be canceled in time. The only way you could do it now is with a single Market order that is placed after some condition had triggered. (Mentioned previously on 9/27 post)

2. Are you talking about placing exit orders on the same bar as the entry after a fill, or something else?

Well, you said "never", so what that says to me is that the hypothetical trade is not being created by Wealth-Lab, consequently the exit logic isn't executed. That could happen for Limit orders if you've entered a non-zero slippage preference. For Stop orders, it's rare, but since the Fidelity back-end triggers on Bid/Ask and WLP triggers on actual trades, a real trade could be filled at a price inside the Stop trigger while WLP does not fill it hypothetically.

If that's not the case, then please be more specific about what is occurring.
profile picture

JWD

#23
Hey Cone-

Based on the above and other reasons, I was hoping you could revise the code so that instead of placing two buy-stop orders in the second bar (one for each symbol), every bar is instead treated like the first bar. In other words, there would be only one buy order, which is placed for the symbol that hits the price trigger first, in the next bar following the trigger. This could be a buy at market or buy at limit order depending on the parameter setting, which is already built in to the code. The stop loss and sell at close orders would not change.

I would have to test it of course to see if it's materially better or worse. But even if it is not as good in performance, I need to balance this against the fact that this will solve inability to cancel the second order automatically and, probably more importantly, the inability to use full buying power without OCO orders.

Your assistance would be greatly appreciated.

Happy holidays,

JWD
profile picture

Cone

#24
Missed your petition.. it must have been the holiday season!

Don't forget to change the assignment to symbol2 for your pair symbol.

CODE:
Please log in to see this code.
This website uses cookies to improve your experience. We'll assume you're ok with that, but you can opt-out if you wish (Read more).