Traders' Tip text
Wealth-Lab’s strategy code includes Mr. Vervoort’s indicator plotting template and has success at producing similar performance results using only the trading rules employing the SVE_Stoch_IFT indicator, which now lives comfortably in Wealth-Lab’s TASCIndicator Library. In addition, the money management rule for not sharing profit or loss between symbols inspired a new MS123.PosSizer to be created for that purpose. The Equity Curve for the Portfolio Simulation with $100,000 starting equity is shown in Figure 1. As a reminder, Wealth-Lab users should install the CBOE Provider extension from Wealth-Lab.com to easily access and update Put/Call Ratio data directly from the CBOE website.
Figure 1. The strategy was able to avoid Buy & Hold’s large drawdown in 2008 (blue) by trading from the short side (red), which compensated the drawdown in long positions (black).
Note: The indicator was added to the TASCIndicators library, version 2011.11.0.0
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
using TASCIndicators;
namespace WealthLab.Strategies
{
public class PutCallRatioIndicator : WealthScript
{
StrategyParameter _rsiPeriod;
StrategyParameter _wmaPeriod;
StrategyParameter _devs;
StrategyParameter _slowRainbowPeriod;
StrategyParameter _wmaSlow;
StrategyParameter _rsiPer;
StrategyParameter _stoPer;
StrategyParameter _stoSmooth;
public PutCallRatioIndicator()
{
_rsiPeriod = CreateParameter("Fast RSI Period", 5, 2, 30, 1);
_wmaPeriod = CreateParameter("Fast WMA Period", 5, 2, 10, 1);
_devs = CreateParameter("SD Multiple", 1.3, 1, 2, 0.1);
_slowRainbowPeriod = CreateParameter("Slow Rbw Period", 4, 1, 10, 1);
_wmaSlow = CreateParameter("Slow Smooth Period", 2, 1, 10, 1);
_rsiPer = CreateParameter("Slow IF RSI Period", 8, 1, 20, 1);
_stoPer = CreateParameter("Stoch Period", 30, 5, 500, 1);
_stoSmooth = CreateParameter("Sto Smooth Perio", 5, 2, 10, 1);
}
// Transforms a series for unscaled plotting (translated from WL4 Code Library, Dexter Brunet)
public DataSeries TransformSeries( DataSeries ds, double a1, double a2, double b1, double b2)
{
string sName = ds.Description + "(No Scale)";
if (Math.Abs(a1 - a2) > 0.00001)
{
double a = (b1 - b2)/(a1 - a2);
double b = (b2 * a1 - a2 * b1)/(a1 - a2);
DataSeries s = ds * a;
s += b;
s.Description = sName;
return s;
}
else
return ds;
}
protected override void Execute()
{
// Access the raw P/C Ratio data
DataSeries pcrEQ = GetExternalSymbol("CBOE", "EQUITYPC", true).Close;
/* Create and plot indicators */
HideVolume();
// Moving Averages
DataSeries ma50 = SMA.Series(Close, 50);
DataSeries ma100 = SMA.Series(Close, 100);
DataSeries ma200 = SMA.Series(Close, 200);
DataSeries ma20 = SMA.Series(Close, 20);
DataSeries bbU = BBandUpper.Series(Close, 20, 2);
DataSeries bbL = BBandLower.Series(Close, 20, 2);
PlotSeries(PricePane, ma50, Color.Blue, LineStyle.Solid, 1);
PlotSeries(PricePane, ma100, Color.Red, LineStyle.Dashed, 1);
PlotSeries(PricePane, ma200, Color.Red, LineStyle.Solid, 1);
PlotSeries(PricePane, ma20, Color.Green, LineStyle.Solid, 1);
PlotSeriesFillBand(PricePane, bbU, bbL, Color.Transparent, Color.FromArgb(30, Color.Green), LineStyle.Solid, 1);
DataSeries fastPCRI = PCRiFast.Series(pcrEQ, _rsiPeriod.ValueInt, _wmaPeriod.ValueInt);
DataSeries uBand = BBandUpper.Series(fastPCRI, 200, _devs.Value);
DataSeries lBand = BBandLower.Series(fastPCRI, 200, _devs.Value);
DataSeries slowPCRI = PCRiSlow.Series(pcrEQ, _slowRainbowPeriod.ValueInt, _wmaSlow.ValueInt);
ChartPane rsiPane = CreatePane(40, false, true);
PlotSeries(rsiPane, fastPCRI, Color.Black, LineStyle.Solid, 2);
PlotSeries(rsiPane, uBand, Color.Blue, LineStyle.Dashed, 1);
PlotSeries(rsiPane, lBand, Color.Blue, LineStyle.Dashed, 1);
// Transform slowPCRI for plotting in same pane as fastPCRI
int n = Bars.Count - 1;
DataSeries slowT = TransformSeries(slowPCRI, Lowest.Value(n, slowPCRI, n - 30), Highest.Value(n, slowPCRI, n - 30), 0, 100);
ChartPane sloPane = CreatePane(40, false, true);
PlotSeries(sloPane, slowT, Color.Red, LineStyle.Solid, 2);
DataSeries pcri_IF = PCRiSlowIFT.Series(pcrEQ, _slowRainbowPeriod.ValueInt, _wmaSlow.ValueInt, _rsiPer.ValueInt);
PlotSeries(sloPane, pcri_IF, Color.Blue, LineStyle.Solid, 2);
DataSeries iftSto = InverseFisherStoch.Series(Close, _stoPer.ValueInt, _stoSmooth.ValueInt);
DataSeries stoD = StochD.Series(Bars, _stoPer.ValueInt, _stoSmooth.ValueInt);
ChartPane stoPane = CreatePane(40, false, true);
PlotSeries(stoPane, iftSto, Color.Red, LineStyle.Solid, 2);
PlotSeries(stoPane, stoD, Color.Blue, LineStyle.Solid, 1);
for (int bar = GetTradingLoopStartBar(165); bar < Bars.Count; bar++)
{
if (IsLastPositionActive)
{
Position p = LastPosition;
if (p.PositionType == PositionType.Long)
{
if (CrossUnder(bar, iftSto, 60))
ExitAtMarket(bar + 1, p);
}
else if (CrossOver(bar, iftSto, 30))
ExitAtMarket(bar + 1, p);
}
else
{
if (CrossOver(bar, iftSto, 30))
BuyAtMarket(bar + 1);
else if (CrossUnder(bar, iftSto, 60) && Close[bar] < SMA.Series(Close, 165)[bar])
ShortAtMarket(bar + 1);
}
}
}
}
}