Skip to main content

Trading Decisions of Your Stone Age Grandpa can Make You Money in FOREX

Why Ferrari or Rolex does not price their products at 149.999 or 12.999 but most of the items you see in your supermarket is priced like 4.99? Because they never like to be positioned as a bargain. Did you know that we tend to chose the price with less syllables even if the two prices have the same written lenght? These are some of the pricing strategies used by marketers.

This is a very interesting topic and you can even find yourself deep in neuroscience while reading about it. Check this site, it gives 42 pricing methods to influence your brain, crazy, https://www.nickkolenda.com/psychological-pricing-strategies/

We also deal with prices when trading and I believe there are some sub-concious forces in play. Knowing some articles on things like the effect of round numbers in trading, I see some more potential here and I find this worth digging more not as standalone trading strategies but more as filters. At the end of the day, no one knows how the price feed effects the primitive you sitting inside you.

Among the ones I have tested out of this 42 methods outlined at the above link, I want to share with one that can be applied as a filter.

In [30]:
#do the imports
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import bulk_tester as bt
%matplotlib inline
import datetime

Get price data for the Back Test period(BT) which is between 01.01.2009 and 01.01.2014 (M15 bars)

In [31]:
dftest = bt.get_data('EURUSD', 'BT', 'original')

Now, I want to short EURUSD for this period and I want to do this when the market is calm, lets say I believe that signal to noise ratio is better when the market is calm. I want to short when the spread is smaller than 1.1 and volume is smaller than 300. Here 300 is the volume provided by OANDA and it shows how many ticks make up a bar, an indication of fragmented activity.

Before coding the trading logic, I want to get a list of bars and use it as a control group before I apply any trading logic. I get the indexes of every 10th bar. For the backtesting period, we have 12807 such bars in hand.

In [32]:
index_of_entry = []
b = 0#this is the loop counter

#loop the frame
for index, row in dftest.iterrows():
    #this is mostly randomly selected entry points at every 10 bar, the control group
    if b % 10 == 0:
        index_of_entry.append(b)
               
    b += 1
In [33]:
len(index_of_entry)
Out[33]:
12807

Then I run a quick and dirty backtest to see the performance of this selected entry points. The trading rule here is this:

  • short at the index if spread < 1.1 and volume < 300 (these are optimized by hand for the BT period)
  • close after 50 bars (optimized by hand for BT period)
In [34]:
a = index_of_entry
pnl = []
lag = 50#periods to close the position

for b in range(0,len(a)):
    
    if a[b] + lag < len(dftest) and 10000*(dftest.Ask[a[b]] - dftest.Bid[a[b]]) < 1.1 and dftest.volume[a[b]] < 300:
        
        pips = 10000*(dftest.Bid[a[b]] - dftest.Ask[a[b]+lag])
        pnl.append(pips)
        
print("Profit in Pips: ", np.sum(pnl))
print("Number of Trades: ", len(pnl))
plt.plot(np.cumsum(pnl))
Profit in Pips:  861.9
Number of Trades:  710
Out[34]:
[<matplotlib.lines.Line2D at 0x11a34f198>]

Not good right. Now I apply my stoneage grandpa filter and re select the entry points. Here Bid price has a 5 at the end.

In [35]:
index_of_entry = []
b = 0#this is the loop counter

#loop the frame
for index, row in dftest.iterrows():
    #get bid
    bid = row["Bid"]
    #this is if there is number 5 at [6] of the bid
    if len(str(bid)) > 6 and int((str(bid))[6]) in (5,5):
        index_of_entry.append(b)
               
    b += 1
In [36]:
len(index_of_entry)
Out[36]:
12681

And apply the same trading logic. What an improvement.

In [37]:
a = index_of_entry
pnl = []
lag = 50#periods to close the position

for b in range(0,len(a)):
    
    if a[b] + lag < len(dftest) and 10000*(dftest.Ask[a[b]] - dftest.Bid[a[b]]) < 1.1 and dftest.volume[a[b]] < 300:
        
        pips = 10000*(dftest.Bid[a[b]] - dftest.Ask[a[b]+lag])
        pnl.append(pips)
        
print("Profit in Pips: ", np.sum(pnl))
print("Number of Trades: ", len(pnl))
plt.plot(np.cumsum(pnl))
Profit in Pips:  4920.6
Number of Trades:  689
Out[37]:
[<matplotlib.lines.Line2D at 0x11edfb160>]

And lets see what we have for all data covering unseen out of sample data(01.01.2009 to 05.05.2017) Here I simly copy pasted the same code above.

In [38]:
#get price data for the entire set: between 01.01.2009 and 05.05.2017, M15 bars
dftest = bt.get_data('EURUSD', 'ALL', 'original')
In [39]:
index_of_entry = []
b = 0#this is the loop counter

#loop the frame
for index, row in dftest.iterrows():
    #get bid
    bid = row["Bid"]
    #this is if there is number 5 at [6] of the bid
    if len(str(bid)) > 6 and int((str(bid))[6]) in (5,5):
        index_of_entry.append(b)
               
    b += 1
In [40]:
a = index_of_entry
pnl = []
lag = 50#periods to close the position

for b in range(0,len(a)):
    
    if a[b] + lag < len(dftest) and 10000*(dftest.Ask[a[b]] - dftest.Bid[a[b]]) < 1.1 and dftest.volume[a[b]] < 300:
        
        pips = 10000*(dftest.Bid[a[b]] - dftest.Ask[a[b]+lag])
        pnl.append(pips)
        
print("Profit in Pips: ", np.sum(pnl))
print("Number of Trades: ", len(pnl))
plt.plot(np.cumsum(pnl))
Profit in Pips:  7117.9
Number of Trades:  1005
Out[40]:
[<matplotlib.lines.Line2D at 0x11e60e278>]

I have to make a note here. When you backtest this properly, you will not see an equity curve like this, it will be still up but with flat periods of no activity. The reason is that we have are looking at the cumulative pips return graph with number of trades in the x axis. In an equity curve, you see how your capital changes in equally spaced time.

If you find this post usefull, please like this on Quantocracy!

Comments

  1. Really loving your blog. This was a real delight to read!

    Hey, if in period = x, the algo shorts, but in period = x+10, and the trading logic conditions still hold, the algo will short yet again?

    ReplyDelete
    Replies
    1. Hi Vinitrinh,

      Thank you for the nice words.

      You are correct, this algo holds multiple positions at time t. But to be honest, I did not engineer it for optimum performance that much, my point here is the effect of a single digit when used as a filter.

      If you test it with a single position at time t logic, let me know the outcome.

      Thanks,

      QTJ

      Delete
  2. Very good post, I've been an FX and Equities trader for a while and it completely makes sense from the trader's perspective: we tend to round up levels all the time to simplify our strategies.

    ReplyDelete
    Replies
    1. Thank you Esteban. I am using this one as a filter in my trading strategies and it works. Also, i encourage you to look at the other biases mentioned at the link in the post.

      Delete

Post a Comment

Popular posts from this blog

Two Strategies you can start trading tomorrow - Time of Day effects in FX continued

My latest post at http://quantsjourney.blogspot.co.uk/2017/09/time-of-day-effects-in-fx.html was on time of day effects in FX and I was claiming that you can actually make money with simple strategies depending on time of day.
Below you will find 2 very simple strategies you can play with and make some money. Do not forget sending my 20%, I know I can trust you.
I will now test these strategies with M15 bars with my quick and dirty backtester on 15 currency pairs including majors. Backtesting period is 01.01.2007 - 05.05.2017. The charts are profit in pips for the pairs where the strategy has positive P&L, these are not equity curves with equally spaced time, the horizontal axis is number of trades. But obviously equity curves will be similar with some gaps due to days with no trades.
I almost did not optimize these strategies, there is room for improvement.
Strategy one: Short at GMT 09:15 am, do this on Wednesday, Thursday and Friday, close after 5 hours. Check out EURUSD!

EDIT …

Struggling Quant Episode 1: How I lost USD 500.000 while figuring out the link between questions, math, stats, coding and trading

Say that you are 30 years old and you have a good 25 years to work hard. Instead of going down the easy way of working for someone else during the day and killing time in the evenings and weekends, you have chosen the hard path of quantitative trading and started the heavy work. How many profitable trading ideas you can find in 25 years. Lets say you can figure out and test ideas in 10 days, 36 ideas a year and 900 ideas in 25 years right. Since this is my post, I am rounding 900 to 1000. Assuming a hit rate of 5%, 50 profitable ideas in 25 years, 2 per year and lets also say a profitable idea makes 20% per year. Depending on your initial capital, you will make X USD in 25 years. Lets say this X is USD 12.500.000 for me. USD 500.000 per year in average. This is my potential if I start doing things right now, right? So if I do things wrong for a year, I lose USD 500.000, this is what I did last year. I could not figure out how to operationalize all the scientific literature, huge qua…