| Overall Statistics |
|
Total Trades 2354 Average Win 0.09% Average Loss -0.01% Compounding Annual Return 45.513% Drawdown 5.400% Expectancy 1.116 Net Profit 13.240% Sharpe Ratio 2.654 Probabilistic Sharpe Ratio 81.757% Loss Rate 87% Win Rate 13% Profit-Loss Ratio 15.38 Alpha 0.038 Beta 0.821 Annual Standard Deviation 0.115 Annual Variance 0.013 Information Ratio -0.195 Tracking Error 0.102 Treynor Ratio 0.37 Total Fees $2725.29 |
using QuantConnect.Data.Custom.Tiingo;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
namespace QuantConnect
{
public partial class BootCampTask : QCAlgorithm
{
private RollingWindow<TradeBar> Window;
public override void Initialize()
{
SetStartDate(2016, 11, 1);
SetEndDate(2017, 3, 1);
var symbols = new[] {QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA),
QuantConnect.Symbol.Create("NKE", SecurityType.Equity, Market.USA)};
SetUniverseSelection(new ManualUniverseSelectionModel(symbols));
AddAlpha(new NewsSentimentAlphaModel());
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
SetExecution(new ImmediateExecutionModel());
SetRiskManagement(new NullRiskManagementModel());
}
}
public class NewsData
{
public Symbol Symbol { get; }
public RollingWindow<double> Window { get; }
public NewsData(Symbol symbol)
{
Symbol = symbol;
Window = new RollingWindow<double>(100);
}
}
public partial class NewsSentimentAlphaModel : AlphaModel
{
private double _score;
public Dictionary <Symbol, NewsData> _newsData = new Dictionary<Symbol, NewsData>();
public Dictionary<string, double> wordScores = new Dictionary<string, double>()
{
{"attractive",0.5}, {"bad",-0.5}, {"beat",0.5}, {"beneficial",0.5},
{"down",-0.5}, {"excellent",0.5}, {"fail",-0.5}, {"failed",-0.5}, {"good",0.5},
{"great",0.5}, {"growth",0.5}, {"large",0.5}, {"lose",-0.5}, {"lucrative",0.5},
{"mishandled",-0.5}, {"missed",-0.5}, {"missing",-0.5}, {"nailed",0.5},
{"negative",-0.5}, {"poor",-0.5}, {"positive",0.5}, {"profitable",0.5},
{"right",0.5}, {"solid",0.5}, {"sound",0.5}, {"success",0.5}, {"un_lucrative",-0.5},
{"unproductive",-0.5}, {"up",0.5}, {"worthwhile",0.5}, {"wrong",-0.5},
{"abandoned",-1},
{"abandonment",-1},
{"abandon",-1},
{"abase",-2},
{"abasement",-2},
{"abash",-2},
{"abate",-1},
{"abdicate",-1},
{"aberration",-2},
{"abhor",-2},
{"abhorred",-2},
{"abhorrence",-2},
{"abhorrent",-2},
{"abhorrently",-2},
{"abhors",-2},
{"abidance",2},
{"abide",2},
{"abject",-2},
{"abjectly",-2},
{"abjure",-1},
{"abilities",1},
{"ability",1},
{"able",1},
{"abnormal",-1},
{"abolish",-1},
{"abominable",-2},
{"abominably",-2},
{"abominate",-2}
//and so on
};
public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
{
var insights = new List<Insight>();
var news = data.Get<TiingoNews>();
foreach (var article in news.Values)
{
var words = article.Description.ToLower().Split(' ');
_score = words
.Where(x => wordScores.ContainsKey(x))
.Sum(x => wordScores[x]);
// 1. Get the underlying symbol and save to the variable symbol
var symbol = article.Symbol.Underlying;
// 2. Add scores to the rolling window associated with its _newsData symbol
_newsData[symbol].Window.Add(_score);
// 3. Sum the rolling window scores, save to sentiment
// If _sentiment aggregate score for the time period is greater than 5, emit an up insight
var sentiment = _newsData[symbol].Window.Sum();
if (sentiment > 5)
{
insights.Add(Insight.Price(symbol, TimeSpan.FromDays(1), InsightDirection.Up));
}
}
return insights;
}
public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
foreach (var security in changes.AddedSecurities)
{
var symbol = security.Symbol;
var newsAsset = algorithm.AddData<TiingoNews>(symbol);
_newsData[symbol] = new NewsData(newsAsset.Symbol);
}
foreach (var security in changes.RemovedSecurities)
{
NewsData newsData;
if (_newsData.Remove(security.Symbol, out newsData))
{
algorithm.RemoveSecurity(newsData.Symbol);
}
}
}
}
}