A number of sites provide all sorts of helpful data in plain ASCII files. To utilize it, you can go the obvious way: download, save the file on your local hard disk and create a static DataSet with the help of the ASCII adapter. But when new data becomes available, you have to download the file again.
This example illustrates how, using the power of .NET classes, you could access remote sources of information on-the-fly in your trading strategy. After the raw data for the total Put/Call Ratio is downloaded from the CBOE website and becomes available as a DataSeries, the strategy is able to make its trading decisions on Bollinger Bands derived from the Put/Call Ratio.
The code also demonstrates how to cache the resulting Bars object in the global memory (GOP) to speed up Multi-Symbol Backtests. Without caching, the strategy would have to generate redundant data requests to the server on each symbol in a DataSet. As soon the data is in place, access to the Internet is no longer required.
Note: CBOE stopped updating this ASCII file on their website in October 2019. For the actual data see
CBOE data provider
using System;
using System.Globalization;
using System.Net;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
namespace WealthLab.Strategies
{
public class PutCallRatio : WealthScript
{
protected override void Execute()
{
/* Location of the Put/Call Ratio, year 2003 to present */
Bars pcratio = new Bars( "PCRATIO", BarScale.Daily, 0 );
IFormatProvider format = new CultureInfo("en-US");
string URI = @"http://www.cboe.com/publish/ScheduledTask/MktData/datahouse/indexPC.csv";
if ( GetGlobal("onDemand") == null )
{
/* Download the file and split into lines */
WebClient client = new WebClient();
string text = client.DownloadString(URI);
string[] lines = text.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
/* Parse the Put/Call Ratio data file starting from line 2 */
for ( int i = 3; i < lines.Length; i++ )
{
try
{
string[] t = lines[i].Split(',');
DateTime d = DateTime.ParseExact( t[0], new string[] { "M/d/yyyy" }, format, DateTimeStyles.None );
double ratio = Convert.ToDouble( t[4], format );
pcratio.Add( d,ratio,ratio,ratio,ratio,1 );
}
catch
{
PrintDebug( lines[i] );
}
}
SetGlobal( "onDemand", pcratio );
}
pcratio = Synchronize( (Bars)GetGlobal("onDemand") );
ChartPane PCRatioPane = CreatePane( 30, true, true );
PlotSeries( PCRatioPane, pcratio.Close, Color.Blue, WealthLab.LineStyle.Solid, 2 );
DataSeries bbL = BBandLower.Series( pcratio.Close, 20, 2 ); bbL.Description = "Put/Call Ratio: Lower BB";
DataSeries bbU = BBandUpper.Series( pcratio.Close, 20, 2 ); bbU.Description = "Put/Call Ratio: Upper BB";
PlotSeriesDualFillBand( PCRatioPane, bbU, bbL, Color.Empty, Color.Empty, Color.Brown, WealthLab.LineStyle.Solid, 1 );
for(int bar = 50; bar < Bars.Count; bar++)
{
if (IsLastPositionActive)
{
Position p = LastPosition;
if ( p.PositionType == PositionType.Long )
SellAtStop( bar+1, p, p.EntryPrice * 0.9, "Stop Loss" ); else
CoverAtStop( bar+1, p, p.EntryPrice * 1.1, "Stop Loss" );
ExitAtAutoTrailingStop( bar+1, p, 5, 25, "AutoStop" );
}
else
{
if( CrossUnder( bar, pcratio.Close, bbL ) )
BuyAtMarket( bar+1 );
if( CrossOver( bar, pcratio.Close, bbU ) )
ShortAtMarket( bar+1 );
}
}
}
}
}