Function to provide S&P500 index

User avatar
fbertram
Posts: 166
Joined: 16 Oct 2014
Location: Seattle, USA
Has thanked: 36 times
Been thanked: 76 times
Contact:

Function to provide S&P500 index

Postby fbertram » 12 Apr 2018

this is a little function that I use to have access to the S&P500 index from my strategies - without the need to add a secondary data series. It uses MultiCharts' DataLoader to load the data into memory.

I find this quite useful with Portfolio Trader, and I hope you do too.

Best regards, Felix

Code: Select all

//==============================================================================
// Name: __FUB_Benchmark
// Description: Function to return benchmark index
// History: 2017x17, FUB, created
//==============================================================================
// Copyright: 2017-2018, Bertram Solutions LLC
// http://www.bertram.solutions
// License: this code is licensed under GPL v3.0
//==============================================================================

//------------------------------------------------------------------------------
// usage:
//
// public class MyFancySignal : PortfolioSignalObject
// {
// private __FUB_Benchmark Benchmark;
// protected override void Create()
// {
// Benchmark = new __FUB_Benchmark(this);
// }
// protected override void StartCalc()
// {
// Benchmark.BenchmarkTicker = "^GSPC"; // optional assignment of symbol
// }
// protected override void CalcBar()
// {
// double benchmark = Benchmark[0];
// Output.WriteLine("{0:MM/dd/yyyy}: S&P 500 = {1} (2:F2}x",
// Bars.Time[0], benchmark, benchmark / Benchmark.InitialValue);
// }
// }
//
//------------------------------------------------------------------------------
// requirements:
// * your benchmark symbol should be added to QuoteManager as an index
// we use ^GSPC from Free Quotes, but any other index should work
// * your strategy should run on daily bars
//------------------------------------------------------------------------------

#define CLEAR_ON_STARTCALC

using System;
using System.Drawing;
using System.Linq;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using ATCenterProxy.interop;

namespace PowerLanguage
{
namespace Function
{
public sealed class __FUB_Benchmark : FunctionSeries<System.Double>
{
[Input] public string BenchmarkTicker {get; set;}

public __FUB_Benchmark(CStudyControl _master) : base(_master) { }
public __FUB_Benchmark(CStudyControl _master, int _ds) : base(_master, _ds) { }

private static Dictionary<string, double> BenchmarkData = BenchmarkData = new Dictionary<string, double>();
private static ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
private double? _InitialValue;
public double InitialValue {get {return _InitialValue != null ? (double)_InitialValue : 0.0;}}

private double GetBenchmarkValue()
{
string key = String.Format("{0:MM/dd/yyyy}", Bars.Time[0]);
string key1 = String.Format("{0:MM/dd/yyyy}", Bars.Time[1]);

bool hasKey;
try
{
Lock.EnterReadLock();
hasKey = BenchmarkData.ContainsKey(key);
if (hasKey && Bars.Time.Value < DateTime.Now.Date)
return BenchmarkData[key];
}
finally
{
Lock.ExitReadLock();
}

// when we get here, one of 2 things has happened
// 1) the date is not available in the cache
// 2) the date equals today, and we should update the cache

// find our index symbol in the database
var benchmarkInfo = SymbolStorage.GetDataFeeds()
.SelectMany(it =>
SymbolStorage.GetSymbols(it, ESymbolCategory.Index)
.Select(it2 => new {
feed = it,
syminfo = it2}))
.Where(it => it.syminfo.SymbolName == BenchmarkTicker)
.First();

// populate data request structure
// see https://www.multicharts.com/trading-software/index.php/4.7.4_Receiving_the_data_for_any_symbol_and_any_resolution._DataLoader
InstrumentDataRequest Req = Bars.Request;
Req.From = Bars.Time[2];
Req.To = Bars.Time[0] + TimeSpan.FromDays(365);
// alternative way of populating data range:
// DataRequest _DataRequest = new DataRequest();
// _DataRequest.RequestType = DataRequestType.BarsBack;
// _DataRequest.Count = Bars.FullSymbolData.Count;
// Req.Range = _DataRequest;
//Req.Resolution = ... // FIXME: should force this to daily bars
Req.Category = (ESymbolCategory)benchmarkInfo.syminfo.SymbolCategory; // can we cast MCPA_MCSymbolCategories => ESymbolCategory ???
Req.Symbol = benchmarkInfo.syminfo.SymbolName;
Req.DataFeed = benchmarkInfo.feed;
Req.Exchange = benchmarkInfo.syminfo.SymbolExchange;
Req.RTSymbol = Req.Symbol;
Req.RTDataFeed = Req.DataFeed;
Req.RTExchange = Req.Exchange;

AutoResetEvent autoEvent = new AutoResetEvent(false);

IDataLoaderResult iRes = DataLoader.BeginLoadData( Req, Result =>
{ // public void OnData(IDataLoaderResult Result){ }
if (Result.IsCompleted)
{
try
{
Lock.EnterWriteLock();
foreach (var quote in Result.Data)
{
string k = String.Format("{0:MM/dd/yyyy}", quote.Time);
BenchmarkData[k] = quote.Close;
}
}
finally
{
Lock.ExitWriteLock();
}
}

DataLoader.EndLoadData( Result );
autoEvent.Set();
},
null);

autoEvent.WaitOne();

try
{
Lock.EnterReadLock();
hasKey = BenchmarkData.ContainsKey(key);
bool hasKey1 = BenchmarkData.ContainsKey(key1);

// we might not be able to load today's value
// if that happens, we revert to yesterday's value

return hasKey
? BenchmarkData[key]
: (hasKey1 ? BenchmarkData[key1] : 0.0);
}
finally
{
Lock.ExitReadLock();
}
}

protected override void Create()
{
BenchmarkTicker = "^GSPC";
}

protected override void StartCalc()
{
#if CLEAR_ON_STARTCALC
BenchmarkData.Clear();
#endif
}

protected override System.Double CalcBar()
{
double benchmark = GetBenchmarkValue();

if (_InitialValue == null)
_InitialValue = benchmark;

return benchmark;
}
}
}
}

//==============================================================================
// end of file
Attachments
__FUB_Benchmark.pln
(2.73 KiB) Downloaded 502 times

darob
Posts: 207
Joined: 20 Nov 2014
Has thanked: 57 times
Been thanked: 32 times

Re: Function to provide S&P500 index

Postby darob » 04 May 2018

Hi Felix, thanks so much for sharing this. I just need something like this to work in a 5 min timeframe. Are you willing to offer an opinion on that possibility? Of course I understand if not.

Alternatively, if MC added secondary data series functionality to the scanner that would be awesome.

User avatar
fbertram
Posts: 166
Joined: 16 Oct 2014
Location: Seattle, USA
Has thanked: 36 times
Been thanked: 76 times
Contact:

Re: Function to provide S&P500 index

Postby fbertram » 04 May 2018

Hi Darob,

that shouldn't be hard to do. There are a few things that would need to be changed:
  • * the timeframe should be taken from the 'master' instrument
    * the key used to cache the benchmark data should be changed to the epoch of the bar

Sounds like a great activity for a rainy afternoon. PM me, if you'd like to discuss.


Cheers, Felix


Return to “User Contributed Studies”