Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature 8239 adds algo exception handler #8265

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Algorithm.Python/QuantConnect.Algorithm.Python.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
<Content Include="ExpiryHelperAlphaModelFrameworkAlgorithm.py" />
<Content Include="BasicTemplateFuturesFrameworkAlgorithm.py" />
<Content Include="BasicTemplateOptionsFrameworkAlgorithm.py" />
<Content Include="FormalFluorescentYellowPenguin.py" />
<Content Include="InsightWeightingFrameworkAlgorithm.py" />
<None Include="CompositeRiskManagementModelFrameworkAlgorithm.py" />
<None Include="BlackLittermanPortfolioOptimizationFrameworkAlgorithm.py" />
Expand Down
18 changes: 18 additions & 0 deletions Algorithm/QCAlgorithm.Indicators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2342,6 +2342,24 @@ public VolumeWeightedMovingAverage VWMA(Symbol symbol, int period, Resolution? r
return indicator;
}

/// <summary>
/// Creates a new Vortex indicator for the symbol. The indicator will be automatically
/// updated on the given resolution.
/// </summary>
/// <param name="symbol">The symbol whose VWMA we want</param>
/// <param name="period">The smoothing period used to smooth the computed VWMA values</param>
/// <param name="resolution">The resolution</param>
/// <param name="selector">Selects a value from the BaseData to send into the indicator, if null defaults to casting the input value to a TradeBar</param>
/// <returns>A new Vortex indicator with the specified smoothing period</returns>
[DocumentationAttribute(Indicators)]
public VortexIndicator VTX(Symbol symbol, int period, Resolution? resolution = null, Func<IBaseData, IBaseDataBar> selector = null)
{
var name = CreateIndicatorName(symbol, $"VTX({period})", resolution);
var indicator = new VortexIndicator(period);
InitializeIndicator(indicator, resolution, selector, symbol);
return indicator;
}

/// <summary>
/// Creates a new Williams %R indicator. This will compute the percentage change of
/// the current closing price in relation to the high and low of the past N periods.
Expand Down
9 changes: 9 additions & 0 deletions Algorithm/QCAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3362,5 +3362,14 @@ public void SetStatisticsService(IStatisticsService statisticsService)
_statisticsService = statisticsService;
}
}

public void OnError(Exception exception)
{
// Log the exception
DebugMessages.Enqueue(exception.ToString());

// Perform necessary actions such as closing positions and opening orders
Liquidate();
}
}
}
22 changes: 22 additions & 0 deletions AlgorithmFactory/Python/Wrappers/AlgorithmPythonWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public class AlgorithmPythonWrapper : BasePythonWrapper<IAlgorithm>, IAlgorithm
private dynamic _onAssignmentOrderEvent;
private dynamic _onSecuritiesChanged;
private dynamic _onFrameworkSecuritiesChanged;
private dynamic _onError;

/// <summary>
/// True if the underlying python algorithm implements "OnEndOfDay"
Expand Down Expand Up @@ -120,6 +121,7 @@ public AlgorithmPythonWrapper(string moduleName)
// determines whether OnData method was defined or inherits from QCAlgorithm
// If it is not, OnData from the base class will not be called
_onData = _algorithm.GetPythonMethod("OnData");
_onError = _algorithm.GetPythonMethod("OnError");

_onMarginCall = _algorithm.GetPythonMethod("OnMarginCall");

Expand Down Expand Up @@ -1243,5 +1245,25 @@ public void SetTags(HashSet<string> tags)
{
_baseAlgorithm.SetTags(tags);
}

/// <summary>
/// Handles exceptions thrown during the execution of the algorithm.
/// </summary>
/// <param name="exception">The exception that was thrown</param>
public void OnError(Exception exception)
{
if (_onError != null)
{
using (Py.GIL())
{
_onError(exception);
}
}
else
{
// Default error handling if no custom OnError method is defined
Console.WriteLine($"Unhandled exception: {exception}");
}
}
}
}
7 changes: 7 additions & 0 deletions Common/Interfaces/IAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -919,5 +919,12 @@ Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool fillForw
/// </summary>
/// <param name="tags">The tags</param>
void SetTags(HashSet<string> tags);

/// <summary>
/// Method to handle runtime errors.
/// </summary>
/// <param name="exception">The exception that occurred.</param>
void OnError(Exception exception);

}
}
132 changes: 132 additions & 0 deletions Indicators/Vortex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* 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 QuantConnect.Data.Market;
using QuantConnect.Indicators;

/// <summary>
/// This indicator computes the Vortex Indicator which measures the strength of a trend and its direction.
/// The Vortex Indicator is calculated by comparing the difference between the current high and the previous low with the difference between the current low and the previous high.
/// The values are accumulated and smoothed using a rolling window of the specified period.
/// </summary>

public class VortexIndicator : IndicatorBase<IBaseDataBar>
{
private readonly int _period;
private readonly RollingWindow<IBaseDataBar> _window;

/// <summary>
/// Initializes a new instance of the <see cref="VortexIndicator"/> class.
/// </summary>
/// <param name="period">The number of periods used to calculate the indicator.</param>
public VortexIndicator(int period) : base("VTX")
{
_period = period;
_window = new RollingWindow<IBaseDataBar>(period + 1);
}

/// <summary>
/// Computes the next value of the Vortex Indicator.
/// </summary>
/// <param name="input">The input data point.</param>
/// <returns>The value of the Vortex Indicator.</returns>
protected override decimal ComputeNextValue(IBaseDataBar input)
{
_window.Add(input);

if (_window.Count < _period + 1)
{
return 0m; // or possibly Decimal.MinValue to indicate insufficient data
}

decimal sumTR = 0m;
decimal sumVMPlus = 0m;
decimal sumVMMinus = 0m;

// Starting from 1 since we want to compare current bar with the previous bar
for (int i = 1; i < _window.Count; i++)
{
var current = _window[i - 1]; // current bar
var previous = _window[i]; // previous bar

decimal tr = Math.Max(current.High - current.Low, Math.Max(Math.Abs(current.High - previous.Close), Math.Abs(current.Low - previous.Close)));
decimal vmPlus = Math.Abs(current.High - previous.Low);
decimal vmMinus = Math.Abs(current.Low - previous.High);

sumTR += tr;
sumVMPlus += vmPlus;
sumVMMinus += vmMinus;
}

decimal positiveVortex = sumVMPlus / sumTR;
decimal negativeVortex = sumVMMinus / sumTR;

PositiveVortexIndicator.Update(input.Time, positiveVortex);
NegativeVortexIndicator.Update(input.Time, negativeVortex);

// Depending on your test setup, you might want to return different values:
return positiveVortex; // If the test expects Positive Vortex
// return negativeVortex; // If the test expects Negative Vortex
}

/// <summary>
/// Gets the positive vortex indicator.
/// </summary>
public IndicatorBase<IndicatorDataPoint> PositiveVortexIndicator { get; } = new Identity("PositiveVortex");

/// <summary>
/// Gets the negative vortex indicator.
/// </summary>
public IndicatorBase<IndicatorDataPoint> NegativeVortexIndicator { get; } = new Identity("NegativeVortex");

/// <summary>
/// Gets a value indicating whether this indicator is ready and fully initialized.
/// </summary>
public override bool IsReady => _window.Count == _period + 1;

/// <summary>
/// Adds two VortexIndicator instances together.
/// </summary>
/// <param name="left">The left VortexIndicator.</param>
/// <param name="right">The right VortexIndicator.</param>
/// <returns>A new VortexIndicator instance representing the sum of the two indicators.</returns>
public static VortexIndicator operator +(VortexIndicator left, VortexIndicator right)
{
return new VortexIndicator(left._period + right._period);
}

/// <summary>
/// Subtracts one VortexIndicator from another.
/// </summary>
/// <param name="left">The left VortexIndicator.</param>
/// <param name="right">The right VortexIndicator.</param>
/// <returns>A new VortexIndicator instance representing the difference between the two indicators.</returns>
public static VortexIndicator operator -(VortexIndicator left, VortexIndicator right)
{
return new VortexIndicator(left._period - right._period);
}

/// <summary>
/// Resets the indicator to its initial state.
/// </summary>
public override void Reset()
{
_window.Reset();
PositiveVortexIndicator.Reset();
NegativeVortexIndicator.Reset();
base.Reset();
}
}
42 changes: 42 additions & 0 deletions Tests/Indicators/VortexTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using NUnit.Framework;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
using System;

namespace QuantConnect.Tests.Indicators
{
[TestFixture]
public class VortexIndicatorTests : CommonIndicatorTests<IBaseDataBar>
{
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
{

return new VortexIndicator(14); // Assuming a 14-day period for the Vortex calculations
}

protected override string TestFileName => "spy_with_vtx.csv";

protected override string TestColumnName => "plus_vtx";

[Test]
public override void ComparesAgainstExternalData()
{
var vortex = CreateIndicator();
const double epsilon = .0001;

// Test positive Vortex Indicator values (+VI)
TestHelper.TestIndicator(vortex, TestFileName, "plus_vtx",
(ind, expected) => Assert.AreEqual(expected, (double)((VortexIndicator)ind).PositiveVortexIndicator.Current.Value,epsilon)
);

// Reset indicator to ensure clean state for next test
vortex.Reset();

// Test negative Vortex Indicator values (-VI)
TestHelper.TestIndicator(vortex, TestFileName, "minus_vtx",
(ind, expected) => Assert.AreEqual(expected, (double)((VortexIndicator)ind).NegativeVortexIndicator.Current.Value,epsilon)
);
}
}
}

3 changes: 3 additions & 0 deletions Tests/QuantConnect.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,9 @@
<Content Include="TestData\test_forex_fills_mch_quantity.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="TestData\spy_with_vtx.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="TestData\symbol-properties\symbol-properties-database.csv">
Expand Down
Loading