| Overall Statistics |
|
Total Orders 288 Average Win 0.47% Average Loss -0.38% Compounding Annual Return 29.702% Drawdown 5.700% Expectancy 0.165 Start Equity 100000 End Equity 109060.67 Net Profit 9.061% Sharpe Ratio 1.362 Sortino Ratio 1.334 Probabilistic Sharpe Ratio 67.537% Loss Rate 48% Win Rate 52% Profit-Loss Ratio 1.24 Alpha 0.132 Beta 0.372 Annual Standard Deviation 0.11 Annual Variance 0.012 Information Ratio 0.814 Tracking Error 0.123 Treynor Ratio 0.404 Total Fees $633.92 Estimated Strategy Capacity $24000000.00 Lowest Capacity Asset AAPL R735QTJ8XC9X Portfolio Turnover 235.15% Drawdown Recovery 73 |
#region imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Drawing;
using QuantConnect;
using QuantConnect.Algorithm.Framework;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Algorithm.Selection;
using QuantConnect.Api;
using QuantConnect.Parameters;
using QuantConnect.Benchmarks;
using QuantConnect.Brokerages;
using QuantConnect.Commands;
using QuantConnect.Configuration;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Auxiliary;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Custom;
using QuantConnect.Data.Custom.IconicTypes;
using QuantConnect.DataSource;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.Shortable;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Notifications;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.OptionExercise;
using QuantConnect.Orders.Slippage;
using QuantConnect.Orders.TimeInForces;
using QuantConnect.Python;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Positions;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.CryptoFuture;
using QuantConnect.Securities.IndexOption;
using QuantConnect.Securities.Interfaces;
using QuantConnect.Securities.Volatility;
using QuantConnect.Storage;
using QuantConnect.Statistics;
using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
using Calendar = QuantConnect.Data.Consolidators.Calendar;
#endregion
using System.Text.RegularExpressions;
namespace QuantConnect.Algorithm.CSharp
{
public class BenzingaNewsDataAlgorithm : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
SetCash(100000);
AddUniverseSelection(
new ManualUniverseSelectionModel(
QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA)
));
AddAlpha(new BenzingaAlphaModel());
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
AddRiskManagement(new NullRiskManagementModel());
SetExecution(new ImmediateExecutionModel());
}
}
public class BenzingaAlphaModel : AlphaModel
{
private List<Security> _securities = [];
// A custom word-score map for calculating the total sentiment score
private readonly Dictionary<string, int> _wordScores = new(){
{"good", 1}, {"great", 1}, {"best", 1}, {"growth", 1},
{"bad", -1}, {"terrible", -1}, {"worst", -1}, {"loss", -1}
};
public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice slice)
{
var insights = new List<Insight>();
foreach (var (benzingaSymbol, newsItem) in slice.Get<BenzingaNews>())
{
var security = algorithm.Securities[benzingaSymbol.Underlying];
var contentWords = newsItem.Contents.ToLower();
var score = 0;
foreach (var entry in _wordScores)
{
score += Regex.Matches(contentWords, entry.Key).Count * entry.Value;
}
var targetDirection = score > 0 ? InsightDirection.Up : InsightDirection.Flat;
security.Set("TargetDirection", targetDirection);
}
// Generate insights based on direction changes
foreach (var security in _securities)
{
if (!(slice.ContainsKey(security) && slice[security] != null && !slice[security].IsFillForward)) continue;
var targetDirection = security.Get<InsightDirection>("TargetDirection");
// Buy or sell if the sentiment has changed from our current holdings
if (security.Get<InsightDirection>("CurrentDirection") != targetDirection)
{
insights.Add(Insight.Price(security, TimeSpan.FromDays(14), targetDirection));
security.Set("CurrentDirection", targetDirection);
}
}
return insights;
}
public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
foreach (var security in changes.AddedSecurities)
{
// Requesting data to obtain the updated news for sentiment score calculation
var benzingaSymbol = algorithm.AddData<BenzingaNews>(security.Symbol).Symbol;
security.Set("CurrentDirection", InsightDirection.Flat);
security.Set("TargetDirection", InsightDirection.Flat);
// Historical data
var history = algorithm.History<BenzingaNews>(benzingaSymbol, 14, Resolution.Daily);
algorithm.Debug($"We got {history.Count()} items from our history request");
_securities.Add(security);
security.Set("BenzingaSymbol", benzingaSymbol);
}
foreach (var security in changes.RemovedSecurities)
{
if (_securities.Contains(security))
{
algorithm.RemoveSecurity(security.Get<Symbol>("BenzingaSymbol"));
_securities.Remove(security);
}
}
}
}
}