Note

This post is kept for historical reasons. The indicator and sample have been updated in the sources and `PivotPoint` can now auto-couple itself, removing boilerplate for the user code.

A new post will be written referencing this one. Meanwhile, please check the updated sample in the sources.

# Pivot Point and Cross Plotting

An interesting request came across:

• PivotPoint

It is interesting because of how the indicator is defined. Literature can be found at PivotPoint at StockCharts. PivotPoints use the `close`, `high` and `low` prices of a past timeframe. For example for the daily timeframe:

• Pivot Points for daily charts use the prior month’s data

This may seem troublesome because for each timeframe there needs to be a definition of what other timeframe has to be used. Looking at the formulas opens up another question:

```Pivot Point (P) = (High + Low + Close)/3
Support 1 (S1) = (P x 2) - High
Support 2 (S2) = P  -  (High  -  Low)
Resistance 1 (R1) = (P x 2) - Low
Resistance 2 (R2) = P + (High  -  Low)
```

Even if the text is full of references to prior period and past … the formulas seem to reference the current point in time. Let’s follow the advice of the text and use previous in our first attempt at the PivotPoint. But first let’s tackle the problem of the different timeframes by doing this:

• The indicator will not tackle the problem

Although this may seem confusing, one has to take into account that indicators have to remain as stupid as possible and be made up of actual formulas. The problem will be solved as follows:

```data = btfeeds.ADataFeed(..., timeframe=bt.TimeFrame.Days)
cerebro.resampledata(data, timeframe=bt.TimeFrame.Months)
```

And later in the Strategy:

```class MyStrategy(bt.Strategy):
def __init__(self):
self.pp = PivotPoint(self.data1)  # the resampled data
```

Now it’s clear. The system will have the data, plus an extra input resampled to the needed timeframe. And the `PivotPoint` indicator will work with the resampled data, which is already in the needed monthly timeframe for the original data timeframe which is daily.

The indicator can be developed. Let’s start by following the text indications and not the formulas and look back 1 period.

```class PivotPoint1(bt.Indicator):
lines = ('p', 's1', 's2', 'r1', 'r2',)

def __init__(self):
h = self.data.high(-1)  # previous high
l = self.data.low(-1)  # previous low
c = self.data.close(-1)  # previous close

self.lines.p = p = (h + l + c) / 3.0

p2 = p * 2.0
self.lines.s1 = p2 - h  # (p x 2) - high
self.lines.r1 = p2 - l  # (p x 2) - low

hilo = h - l
self.lines.s2 = p - hilo  # p - (high - low)
self.lines.r2 = p + hilo  # p + (high - low)
```

The strategy will look at parameter `usepp1` to use this `PivotPoint1`

```    def __init__(self):
if self.p.usepp1:
self.pp = PivotPoint1(self.data1)
else:
self.pp = PivotPoint(self.data1)
```

And the output is controlled by a simple `next` method

```    def next(self):
txt = ','.join(
['%04d' % len(self),
'%04d' % len(self.data0),
'%04d' % len(self.data1),
self.data.datetime.date(0).isoformat(),
'%.2f' % self.pp[0]])

print(txt)
```

Let’s execute:

```./ppsample --usepp1
```

And the output:

```0041,0041,0002,2005-02-28,2962.79
0042,0042,0002,2005-03-01,2962.79
...
```

Immediately something is clear: index 41 already belongs to the 2nd month. That means we have skipped calculation of the indicator by 1 month. It is now clear why the text in StockCharts is always mentioning that the calculation happens with the previous month but the formulas seem to reference the current moment.

• The developers probably faced the same design decisions with multiple datas with multiple timeframes.

At the current daily point, only the closed bar from the previous month can be delivered.

That’s why the `next` method is looking at index `[0]`. All this has a very easy fix and that’s writing the formulas just like StockCharts documents them.

```class PivotPoint(bt.Indicator):
lines = ('p', 's1', 's2', 'r1', 'r2',)
plotinfo = dict(subplot=False)

def __init__(self):
h = self.data.high  # current high
l = self.data.low  # current high
c = self.data.close  # current high

self.lines.p = p = (h + l + c) / 3.0

p2 = p * 2.0
self.lines.s1 = p2 - h  # (p x 2) - high
self.lines.r1 = p2 - l  # (p x 2) - low

hilo = h - l
self.lines.s2 = p - hilo  # p - (high - low)
self.lines.r2 = p + hilo  # p + (high - low)
```

An execution without `usepp1`:

```./ppsample
```

And the new output is:

```0021,0021,0001,2005-01-31,2962.79
0022,0022,0001,2005-02-01,2962.79
...
```

Et voilá! The 1st month had `20` trading days and once complete the indicator has calculated the values and can be delivered. The only printed line is `p` and if the value is the same in the 2 lines is because the value remains fixed for the entire next month. Quoting StockCharts:

```Once Pivot Points are set, they do not change and remain in play throughout ...
```

The indicator can already be used. Let’s go for plotting. A plotting parameter has already been set

```    plotinfo = dict(subplot=False)
```

The calculated values are in line with the data scale and just like a Moving Average it can be plotted along the data (hence `subplot=False`)

An execution with `--plot`:

```./ppsample --plot
```

The blistering barnacles are attacking again. The indicator has been plotted on the monthly data (its source), which gives no visual indication on the daily chart, where it would be really helpful.

But `backtrader` supports cross-plotting from one data to another. Although a small addition in `1.2.8.88` was needed to support cross-plotting to a data of a different timeframe.

This is achieved by having `plotmaster` say which the plot target is, by adding it to the `plotinfo` attribute of the indicator:

```./ppsample --plot --plot-on-daily
```

The visual feedback is now useful to understand what `PivotPoint` is offering.

## Script Code and Usage

Available as sample in the sources of `backtrader`:

```\$ ./ppsample.py --help
usage: ppsample.py [-h] [--data DATA] [--usepp1] [--plot] [--plot-on-daily]

Sample for pivot point and cross plotting

optional arguments:
-h, --help       show this help message and exit
--data DATA      Data to be read in (default:
../../datas/2005-2006-day-001.txt)
--usepp1         Have PivotPoint look 1 period backwards (default: False)
--plot           Plot the result (default: False)
--plot-on-daily  Plot the indicator on the daily data (default: False)
```

The code for `PivotPoint`

```from __future__ import (absolute_import, division, print_function,)
#                        unicode_literals)

class PivotPoint1(bt.Indicator):
lines = ('p', 's1', 's2', 'r1', 'r2',)

def __init__(self):
h = self.data.high(-1)  # previous high
l = self.data.low(-1)  # previous low
c = self.data.close(-1)  # previous close

self.lines.p = p = (h + l + c) / 3.0

p2 = p * 2.0
self.lines.s1 = p2 - h  # (p x 2) - high
self.lines.r1 = p2 - l  # (p x 2) - low

hilo = h - l
self.lines.s2 = p - hilo  # p - (high - low)
self.lines.r2 = p + hilo  # p + (high - low)

class PivotPoint(bt.Indicator):
lines = ('p', 's1', 's2', 'r1', 'r2',)
plotinfo = dict(subplot=False)

def __init__(self):
h = self.data.high  # current high
l = self.data.low  # current high
c = self.data.close  # current high

self.lines.p = p = (h + l + c) / 3.0

p2 = p * 2.0
self.lines.s1 = p2 - h  # (p x 2) - high
self.lines.r1 = p2 - l  # (p x 2) - low

hilo = h - l
self.lines.s2 = p - hilo  # p - (high - low)
self.lines.r2 = p + hilo  # p + (high - low)
```

The code for the script.

```from __future__ import (absolute_import, division, print_function,
unicode_literals)

import argparse

from pivotpoint import PivotPoint, PivotPoint1

class St(bt.Strategy):
params = (('usepp1', False),
('plot_on_daily', False))

def __init__(self):
if self.p.usepp1:
self.pp = PivotPoint1(self.data1)
else:
self.pp = PivotPoint(self.data1)

if self.p.plot_on_daily:
self.pp.plotinfo.plotmaster = self.data0

def next(self):
txt = ','.join(
['%04d' % len(self),
'%04d' % len(self.data0),
'%04d' % len(self.data1),
self.data.datetime.date(0).isoformat(),
'%.2f' % self.pp[0]])

print(txt)

def runstrat():
args = parse_args()

cerebro = bt.Cerebro()
cerebro.resampledata(data, timeframe=bt.TimeFrame.Months)

usepp1=args.usepp1,
plot_on_daily=args.plot_on_daily)
cerebro.run()
if args.plot:
cerebro.plot(style='bar')

def parse_args():
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description='Sample for pivot point and cross plotting')

default='../../datas/2005-2006-day-001.txt',

help='Have PivotPoint look 1 period backwards')