| Overall Statistics |
|
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset |
namespace QuantConnect.Indicators {
using System.Numerics;
public class FourierSeries {
private readonly int _period;
private readonly double _frequency;
private readonly int _depth;
private double _leading;
private double _value;
private GuassianQuadrature _gauss;
public int WarmUpPeriod { get; }
public bool IsReady => CosineCoefficients.Count() == _depth && SineCoefficients.Count() == _depth;
public double Value => _value;
public double LeadingCoefficient => _leading;
public List<double> CosineCoefficients { get; }
public List<double> SineCoefficients { get; }
public void Reset()
{
CosineCoefficients.Clear();
SineCoefficients.Clear();
}
public FourierSeries(string name, int period, int n = 5, double frequency = 2 * (double)Math.PI, int depth = 5)
{
_period = period;
_frequency = frequency;
_depth = depth;
_gauss = new GuassianQuadrature(n);
WarmUpPeriod = period;
CosineCoefficients = new List<double>();
SineCoefficients = new List<double>();
}
private static Func<double, double> _func;
private static int _n = 0;
public double AlphaIntegralFunction(double t) {
return _func(t) * Math.Cos(_n * t);
}
public double BetaIntegralFunction(double t) {
return _func(t) * Math.Sin(_n * t);
}
public void Update(double value, Func<double, double> f)
{
// update temp variables
_func = f;
// calculate coefficients
_leading = (1.0 / _frequency) * _gauss.Solve(f, 0, _frequency);
// clear coefficients
CosineCoefficients.Clear();
SineCoefficients.Clear();
for(int i = 1; i <= _depth; i++)
{
_n = i;
double a_n = (2.0 / _frequency) * _gauss.Solve(AlphaIntegralFunction, 0, _frequency);
CosineCoefficients.Add(a_n);
double b_n = (2.0 / _frequency) * _gauss.Solve(BetaIntegralFunction, 0, _frequency);
SineCoefficients.Add(b_n);
}
_value = CalculateFunction(value);
}
public double CalculateFunction(double value, int iterations) {
double result = value;
for(int i = 0; i <= iterations; i++)
result = CalculateFunction(result);
return result;
}
public double CalculateFunction(double value) {
var function = LeadingCoefficient;
for(int i = 0; i < _depth; i++) {
function += (CosineCoefficients[i] * (double)Math.Cos((double)((i + 1) * (double)Math.PI * value / _frequency))) / (i + 1);
function += (SineCoefficients[i] * (double)Math.Sin((double)((i + 1) * (double)Math.PI * value / _frequency))) / (i + 1);
}
return function;
}
}
}namespace QuantConnect.Indicators {
public class GuassianQuadrature {
private readonly int N;
private readonly double[] _roots;
private readonly double[] _weights;
private readonly double[,] _coefficients;
public GuassianQuadrature(int N) {
this.N = N;
this._roots = new double[N];
this._weights = new double[N];
this._coefficients = new double[N + 1, N + 1];
GenerateCoefficients();
GenerateRoots();
}
private void GenerateCoefficients() {
_coefficients[0, 0] = _coefficients[1, 1] = 1;
for (int n = 2; n <= N; n++) {
_coefficients[n, 0] = -(n - 1) * _coefficients[n - 2, 0] / n;
for (int i = 1; i <= n; i++) {
_coefficients[n, i] = ((2 * n - 1) * _coefficients[n - 1, i - 1]
- (n - 1) * _coefficients[n - 2, i]) / n;
}
}
}
private double Evaluate(int n, double x) {
double s = _coefficients[n, n];
for (int i = n; i > 0; i--)
s = s * x + _coefficients[n, i - 1];
return s;
}
private double Differentiate(int n, double x) {
return n * (x * Evaluate(n, x) - Evaluate(n - 1, x)) / (x * x - 1);
}
private void GenerateRoots() {
double x, x1;
for (int i = 1; i <= N; i++) {
x = Math.Cos(Math.PI * (i - 0.25) / (N + 0.5));
do {
x1 = x;
x -= Evaluate(N, x) / Differentiate(N, x);
} while (x != x1);
_roots[i - 1] = x;
x1 = Differentiate(N, x);
_weights[i - 1] = 2 / ((1 - x * x) * x1 * x1);
}
}
public double Solve(Func<Double, Double> f, double a, double b) {
double c1 = (b - a) / 2, c2 = (b + a) / 2, sum = 0;
for (int i = 0; i < N; i++)
sum += _weights[i] * f(c1 * _roots[i] + c2);
return c1 * sum;
}
}
}/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq;
using MathNet.Numerics;
using MathNet.Numerics.LinearAlgebra;
namespace QuantConnect.Indicators
{
/// <summary>
/// The Least Squares Moving Average (LSMA) first calculates a least squares regression line
/// over the preceding time periods, and then projects it forward to the current period. In
/// essence, it calculates what the value would be if the regression line continued.
/// Source: https://rtmath.net/helpFinAnalysis/html/b3fab79c-f4b2-40fb-8709-fdba43cdb363.htm
/// </summary>
public class LeastSquaresMovingAverage : WindowIndicator<IndicatorDataPoint>, IIndicatorWarmUpPeriodProvider
{
/// <summary>
/// Array representing the time.
/// </summary>
private readonly double[] _t;
/// <summary>
/// The point where the regression line crosses the y-axis (price-axis)
/// </summary>
public IndicatorBase<IndicatorDataPoint> Intercept { get; }
/// <summary>
/// The regression line slope
/// </summary>
public IndicatorBase<IndicatorDataPoint> Slope { get; }
/// <summary>
/// Required period, in data points, for the indicator to be ready and fully initialized.
/// </summary>
public int WarmUpPeriod => Period;
/// <summary>
/// Initializes a new instance of the <see cref="LeastSquaresMovingAverage"/> class.
/// </summary>
/// <param name="name">The name of this indicator</param>
/// <param name="period">The number of data points to hold in the window</param>
public LeastSquaresMovingAverage(string name, int period)
: base(name, period)
{
_t = Vector<double>.Build.Dense(period, i => i + 1).ToArray();
Intercept = new Identity(name + "_Intercept");
Slope = new Identity(name + "_Slope");
}
/// <summary>
/// Initializes a new instance of the <see cref="LeastSquaresMovingAverage"/> class.
/// </summary>
/// <param name="period">The number of data points to hold in the window.</param>
public LeastSquaresMovingAverage(int period)
: this($"LSMA({period})", period)
{
}
/// <summary>
/// Computes the next value of this indicator from the given state
/// </summary>
/// <param name="window"></param>
/// <param name="input">The input given to the indicator</param>
/// <returns>
/// A new value for this indicator
/// </returns>
protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
{
// Until the window is ready, the indicator returns the input value.
if (window.Samples <= window.Size) return input.Value;
// Sort the window by time, convert the observations to double and transform it to an array
var series = window
.OrderBy(i => i.Time)
.Select(i => Convert.ToDouble(i.Value))
.ToArray();
// Fit OLS
var ols = Fit.Line(x: _t, y: series);
Intercept.Update(input.Time, (decimal)ols.Item1);
Slope.Update(input.Time, (decimal)ols.Item2);
// Calculate the fitted value corresponding to the input
return Intercept.Current.Value + Slope.Current.Value * Period;
}
/// <summary>
/// Resets this indicator and all sub-indicators (Intercept, Slope)
/// </summary>
public override void Reset()
{
Intercept.Reset();
Slope.Reset();
base.Reset();
}
}
}namespace QuantConnect.Algorithm.CSharp
{
using System;
using System.Numerics;
public class ClientAlgorithm : QCAlgorithm
{
// BACKTESTING PARAMETERS
// =================================================================================================================
// general settings:
// set starting cash
private int _startingCash = 100000;
// backtesting start date time:
// date setting variables
private int _startYear = 2022;
private int _startMonth = 1;
private int _startDay = 3;
// backtesting end date time:
// determines whether there is a specified end date
// if false it will go to the current date (if 'true' it will go to the specified date)
private bool _enableEndDate = true;
// date setting variables
private int _endYear = 2022;
private int _endMonth = 1;
private int _endDay = 3;
// enable plotting
private bool enablePlotting = true;
// universe settings:
// universe selection type
// determines whether securities are selected by QC's universe function
// or manually by the user
// manual = false; QC universe = true
private readonly bool _universeSelectionType = false;
// number of securities for the universe selection to select
private readonly int _stockCount = 5;
// use default values for universe
private readonly bool _useDefaultSymbolBase = false;
// stock list for equities
// list of equities you want in the universe
// used in manual selection of universe
// set selectionType = false for activation
private readonly SymbolBase[] _universeList = new SymbolBase[]{
new SymbolBase(
"AMZN", // symbol ticker
Resolution.Minute, // data resolution
1, // data resolution consolidation rate
1.0m // portfolio allocation percentage
)
};
// default symbol base settings
// used if using QC universe selection or a base is not detected for
// an added security
private readonly SymbolBase _defaultSymbolBase = new SymbolBase(
"<BASE>", // do not modify
// parameters to modify:
Resolution.Minute, // data resolution
1, // data resolution consolidation rate
1.0m // portfolio allocation percentage
);
// position settings:
// percent of portfolio to use for trading
// must be below 1
private readonly decimal _portfolioAllocation = 1m;
// =================================================================================================================
// contains all SymbolBase definitions
private Dictionary<string, SymbolBase> _symbolBases = new Dictionary<string, SymbolBase>();
// creates new universe variable setting
private Dictionary<Symbol, SymbolData> _universe = new Dictionary<Symbol, SymbolData>();
// security changes variable
private SecurityChanges _securityChanges = SecurityChanges.None;
// define offset universe to avoid first candle
private bool offsetUniverse = true;
public override void Initialize()
{
// set start date
SetStartDate(_startYear, _startMonth, _startDay);
// set end date
if(_enableEndDate)
SetEndDate(_endYear, _endMonth, _endDay);
// set starting cash
SetCash(_startingCash);
// add default symbol base
_symbolBases.Add("<BASE>", _defaultSymbolBase);
// initialize universe
if(!_universeSelectionType) {
foreach(SymbolBase sb in _universeList) {
_symbolBases.Add(sb.Symbol, sb);
AddEquity(sb.Symbol, sb.SymbolResolution);
}
} else {
UniverseSettings.Resolution = _defaultSymbolBase.SymbolResolution;
AddUniverse(CoarseFilterFunction, FineFilterFunction
);
}
}
public double f(double x) {
return Math.Exp(x);
}
// filter based on CoarseFundamental
public IEnumerable<Symbol> CoarseFilterFunction(IEnumerable<CoarseFundamental> coarse) {
// returns the highest DollarVolume stocks
// returns "totalNumberOfStocks" amount of stocks
return (from stock in coarse
where !stock.HasFundamentalData
orderby stock.DollarVolume descending
select stock.Symbol).Take(_stockCount);
return Universe.Unchanged;
}
// filters out all symbols not contained in the NASDAQ exchange
public IEnumerable<Symbol> FineFilterFunction(IEnumerable<FineFundamental> fine) {
return (from stock in fine
select stock.Symbol).Take(_stockCount);
}
private static double[] _coefficients = null;
public double Regression(double t) {
if(_coefficients == null)
return 0.0;
double sum = 0;
for(int i = 0; i < _coefficients.Count(); i++)
sum += Math.Pow(t, i) * _coefficients[i];
return sum;
}
private decimal time = 0;
public void OnDataConsolidated(object sender, TradeBar bar) {
SymbolData sd = _universe[bar.Symbol];
sd.Close.Add(bar.Close);
if(!sd.Close.IsReady)
return;
_coefficients = new double[] {(double)sd.LeastSquares.Intercept.Current.Value, (double)sd.LeastSquares.Slope.Current.Value};
if(time % 5 == 0)
sd.Fourier.Update((double)sd.Close[0], Regression);
if(!sd.IsReady) {
return;
}
if(enablePlotting) {
Plot("FS", "P", bar.Close);
Plot("FS", "F0", sd.Fourier.CalculateFunction(0));
Plot("FS", "F1", sd.Fourier.CalculateFunction(1));
Plot("LS", "I", sd.LeastSquares.Intercept);
Plot("LS", "S", sd.LeastSquares.Slope);
Plot("FSC", "A0", sd.Fourier.LeadingCoefficient);
Plot("FSC", "A1", sd.Fourier.CosineCoefficients[0]);
Plot("FSC", "B1", sd.Fourier.SineCoefficients[0]);
//Plot("FSS", "B1", sd.Fourier.SineCoefficients[0]);
//Plot("FSS", "B2", sd.Fourier.SineCoefficients[1]);
}
/*if(coeff < -3 && !Portfolio[sd.Symbol].Invested)
SetHoldings(sd.Symbol, 1);
else if(coeff > 3)
Liquidate(sd.Symbol);
sd.Fourier.Update(sd.Close[0], sd.LeastSquares.Intercept, sd.LeastSquares.Slope);*/
}
public decimal[] ForwardEuler(Func<decimal, decimal> f, decimal ya, int a, int b, int n) {
decimal dt = (b - a * 1.0m) / n;
decimal[] y = new decimal[n + 1];
decimal[] t = new decimal[n + 1];
y[0] = ya;
t[0] = 0;
for(int i = 1; i < n + 1; i++) {
t[i] = t[i - 1] + dt;
y[i] = y[i - 1] + (dt * f(t[i]));
}
return y;
}
// OnSecuritiesChanged runs when the universe updates current securities
public override void OnSecuritiesChanged(SecurityChanges changes) {
_securityChanges = changes;
// remove stocks from list that get removed from universe
foreach (var security in _securityChanges.RemovedSecurities) {
if(Securities[security.Symbol].Invested) {
Log($"{Time}->Portfolio: Liquidated security {security.Symbol} on universe exit");
Liquidate(security.Symbol);
}
_universe.Remove(security.Symbol);
Log($"{Time}->Universe: Removed security {security.Symbol} from universe");
}
// add new securities to universe list
foreach(var security in _securityChanges.AddedSecurities) {
// creare new SymbolData object
SymbolData sd;
// if no base exists for symbol use default
if(!_symbolBases.ContainsKey(security.Symbol) || _useDefaultSymbolBase)
sd = new SymbolData(this, security.Symbol, _symbolBases["<BASE>"]);
// otherwise use defined base
else
sd = new SymbolData(this, security.Symbol, _symbolBases[security.Symbol]);
// initialize consolidator and store if needed
TickConsolidator tickConsolidator = null;
TradeBarConsolidator barConsolidator = null;
if(sd.SymbolBase.SymbolResolution == Resolution.Tick) {
var consolidator = sd.GetTickConsolidator();
if(consolidator != null) {
consolidator.DataConsolidated += OnDataConsolidated;
SubscriptionManager.AddConsolidator(sd.Symbol, consolidator);
tickConsolidator = consolidator;
}
} else {
var consolidator = sd.GetConsolidator();
if(consolidator != null) {
consolidator.DataConsolidated += OnDataConsolidated;
SubscriptionManager.AddConsolidator(sd.Symbol, consolidator);
barConsolidator = consolidator;
}
}
// initialize indicators:
sd.Fourier = new FourierSeries(sd.Symbol, 15, 5, 2 * Math.PI, 2);
sd.LeastSquares = new LeastSquaresMovingAverage(sd.Symbol, 2);
// register indicators
if(tickConsolidator != null) {
RegisterIndicator(sd.Symbol, sd.LeastSquares, tickConsolidator);
}
else {
RegisterIndicator(sd.Symbol, sd.LeastSquares, barConsolidator);
}
// load historical data
var historical = History(sd.Symbol, sd.LeastSquares.WarmUpPeriod, sd.SymbolBase.SymbolResolution);
foreach(var bar in historical) {
sd.LeastSquares.Update(bar.Time, bar.Close);
}
// add SymbolData to universe
_universe.Add(security.Symbol, sd);
Log($"{Time}->Universe: Added security {security.Symbol} to universe");
}
}
public class SymbolBase {
public readonly string Symbol;
public readonly Resolution SymbolResolution;
public readonly int SymbolConsolidationRate;
public readonly decimal PortfolioAllocation;
public SymbolBase(string symbol = "<BASE>", Resolution symbolResolution = Resolution.Minute, int symbolConsolidationRate = 1, decimal portfolioAllocation = 1.0m) {
Symbol = symbol;
SymbolResolution = symbolResolution;
SymbolConsolidationRate = symbolConsolidationRate;
PortfolioAllocation = portfolioAllocation;
}
}
// default class containing all symbol information
public class SymbolData {
// Variables:
// algorithm
public readonly ClientAlgorithm Algorithm;
// symbol
public readonly string Symbol;
// symbol base
public readonly SymbolBase SymbolBase;
// is ready
public bool IsReady => Close.IsReady && Fourier.IsReady && LeastSquares.IsReady;
// previous close
public RollingWindow<decimal> Close = new RollingWindow<decimal>(10);
// fourier series
public FourierSeries Fourier;
// least squares SMA
public LeastSquaresMovingAverage LeastSquares;
public SymbolData(ClientAlgorithm algorithm, Symbol symbol, SymbolBase symbolBase) {
Algorithm = algorithm;
Symbol = symbol;
SymbolBase = symbolBase;
}
public TradeBarConsolidator GetConsolidator() {
TimeSpan timeSpan;
switch(SymbolBase.SymbolResolution) {
case Resolution.Second:
timeSpan = TimeSpan.FromSeconds(SymbolBase.SymbolConsolidationRate);
break;
case Resolution.Minute:
timeSpan = TimeSpan.FromMinutes(SymbolBase.SymbolConsolidationRate);
break;
case Resolution.Hour:
timeSpan = TimeSpan.FromHours(SymbolBase.SymbolConsolidationRate);
break;
case Resolution.Daily:
timeSpan = TimeSpan.FromDays(SymbolBase.SymbolConsolidationRate);
break;
default:
return null;
}
return new TradeBarConsolidator(timeSpan);
}
public TickConsolidator GetTickConsolidator() {
return new TickConsolidator(SymbolBase.SymbolConsolidationRate);
}
}
}
}