Overall Statistics
Total Trades
6684
Average Win
0.03%
Average Loss
-0.03%
Compounding Annual Return
23.146%
Drawdown
6.600%
Expectancy
0.182
Net Profit
20.574%
Sharpe Ratio
1.472
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.30
Alpha
0.181
Beta
-0.033
Annual Standard Deviation
0.12
Annual Variance
0.014
Information Ratio
0.275
Tracking Error
0.135
Treynor Ratio
-5.276
Total Fees
$0.00
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using QuantConnect.Orders.Fills;
using QuantConnect.Securities;
using System;
using System.Linq;

namespace QuantConnect.Algorithm.CSharp
{
    public sealed class ImmediateOptimisticStopFillModel : ImmediateFillModel
    {
        /// <summary>
        /// Default limit order fill model in the base security class.
        /// </summary>
        /// <param name="asset">Security asset we're filling</param>
        /// <param name="order">Order packet to model</param>
        /// <returns>Order fill information detailing the average price and quantity filled.</returns>
        /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/>
        /// <seealso cref="MarketFill(Security, MarketOrder)"/>
        public override OrderEvent LimitFill(Security asset, LimitOrder order)
        {
            //Initialise;
            var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone);
            var fill = new OrderEvent(order, utcTime, 0);

            //If its cancelled don't need anymore checks:
            if (order.Status == OrderStatus.Canceled) return fill;

            //Get the range of prices in the last bar:
            var prices = GetPrices(asset, order.Direction);

            //-> Valid Live/Model Order: 
            switch (order.Direction)
            {
                case OrderDirection.Buy:
                    //Buy limit seeks lowest price
                    if (prices.Low < order.LimitPrice)
                    {
                        //Set order fill:
                        fill.Status = OrderStatus.Filled;
                        // fill at the worse price this bar or the limit price, this allows far out of the money limits
                        // to be executed properly
                        fill.FillPrice = order.LimitPrice;
                    }
                    break;
                case OrderDirection.Sell:
                    //Sell limit seeks highest price possible
                    if (prices.High > order.LimitPrice)
                    {
                        fill.Status = OrderStatus.Filled;
                        // fill at the worse price this bar or the limit price, this allows far out of the money limits
                        // to be executed properly
                        fill.FillPrice = order.LimitPrice;
                    }
                    break;
            }

            // assume the order completely filled
            if (fill.Status == OrderStatus.Filled)
            {
                fill.FillQuantity = order.Quantity;
                fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order);
            }

            return fill;
        }

        /// <summary>
        /// Default stop fill model implementation in base class security. (Stop Market Order Type)
        /// </summary>
        /// <param name="asset">Security asset we're filling</param>
        /// <param name="order">Order packet to model</param>
        /// <returns>Order fill information detailing the average price and quantity filled.</returns>
        /// <seealso cref="MarketFill(Security, MarketOrder)"/>
        /// <seealso cref="SecurityTransactionModel.LimitFill"/>
        public override OrderEvent StopMarketFill(Security asset, StopMarketOrder order)
        {
            //Default order event to return.
            var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone);
            var fill = new OrderEvent(order, utcTime, 0);

            // make sure the exchange is open before filling
            if (!IsExchangeOpen(asset)) return fill;

            //If its cancelled don't need anymore checks:
            if (order.Status == OrderStatus.Canceled) return fill;

            //Get the range of prices in the last bar:
            var prices = GetPrices(asset, order.Direction);

            //Calculate the model slippage: e.g. 0.01c
            var slip = asset.SlippageModel.GetSlippageApproximation(asset, order);

            //Check if the Stop Order was filled: opposite to a limit order
            switch (order.Direction)
            {
                case OrderDirection.Sell:
                    //-> 1.1 Sell Stop: If Price below setpoint, Sell:
                    if (prices.Low < order.StopPrice)
                    {
                        fill.Status = OrderStatus.Filled;
                        fill.FillPrice = order.StopPrice - slip;
                    }
                    break;

                case OrderDirection.Buy:
                    //-> 1.2 Buy Stop: If Price Above Setpoint, Buy:
                    if (prices.High > order.StopPrice)
                    {
                        fill.Status = OrderStatus.Filled;
                        fill.FillPrice = order.StopPrice + slip;
                    }
                    break;
            }

            // assume the order completely filled
            if (fill.Status == OrderStatus.Filled)
            {
                fill.FillQuantity = order.Quantity;
                fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order);
            }

            return fill;
        }

        /// <summary>
        /// Get the minimum and maximum price for this security in the last bar:
        /// </summary>
        /// <param name="asset">Security asset we're checking</param>
        /// <param name="direction">The order direction, decides whether to pick bid or ask</param>
        private Prices GetPrices(Security asset, OrderDirection direction)
        {
            var low = asset.Low;
            var high = asset.High;
            var open = asset.Open;
            var close = asset.Close;
            var current = asset.Price;

            if (direction == OrderDirection.Hold)
            {
                return new Prices(current, open, high, low, close);
            }

            // Only fill with data types we are subscribed to
            var subscriptionTypes = asset.Subscriptions.Select(x => x.Type).ToList();

            // Tick
            var tick = asset.Cache.GetData<Tick>();
            if (subscriptionTypes.Contains(typeof(Tick)) && tick != null)
            {
                var price = direction == OrderDirection.Sell ? tick.BidPrice : tick.AskPrice;
                if (price != 0m)
                {
                    return new Prices(price, 0, 0, 0, 0);
                }

                // If the ask/bid spreads are not available for ticks, try the price
                price = tick.Price;
                if (price != 0m)
                {
                    return new Prices(price, 0, 0, 0, 0);
                }
            }

            // Quote
            var quoteBar = asset.Cache.GetData<QuoteBar>();
            if (subscriptionTypes.Contains(typeof(QuoteBar)) && quoteBar != null)
            {
                var bar = direction == OrderDirection.Sell ? quoteBar.Bid : quoteBar.Ask;
                if (bar != null)
                {
                    return new Prices(bar);
                }
            }

            // Trade
            var tradeBar = asset.Cache.GetData<TradeBar>();
            if (subscriptionTypes.Contains(typeof(TradeBar)) && tradeBar != null)
            {
                return new Prices(tradeBar);
            }

            return new Prices(current, open, high, low, close);
        }

        /// <summary>
        /// Determines if the exchange is open using the current time of the asset
        /// </summary>
        private static bool IsExchangeOpen(Security asset)
        {
            if (!asset.Exchange.DateTimeIsOpen(asset.LocalTime))
            {
                // if we're not open at the current time exactly, check the bar size, this handle large sized bars (hours/days)
                var currentBar = asset.GetLastData();
                if (asset.LocalTime.Date != currentBar.EndTime.Date || !asset.Exchange.IsOpenDuringBar(currentBar.Time, currentBar.EndTime, false))
                {
                    return false;
                }
            }
            return true;
        }

        private class Prices
        {
            public readonly decimal Current;
            public readonly decimal Open;
            public readonly decimal High;
            public readonly decimal Low;
            public readonly decimal Close;

            public Prices(IBar bar)
                : this(bar.Close, bar.Open, bar.High, bar.Low, bar.Close)
            {
            }

            public Prices(decimal current, decimal open, decimal high, decimal low, decimal close)
            {
                Current = current;
                Open = open == 0 ? current : open;
                High = high == 0 ? current : high;
                Low = low == 0 ? current : low;
                Close = close == 0 ? current : close;
            }
        }
    }
}
using Accord.MachineLearning.VectorMachines;
using Accord.MachineLearning.VectorMachines.Learning;
using Accord.Statistics.Kernels.Sparse;
using QuantConnect.Securities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

namespace QuantConnect.Algorithm.CSharp.Xilera
{
    public partial class NewsAlgorithm : QCAlgorithm
    {
        private const string _tickers = @"
        ADAP
        AKTX
        AMRN
        AMFW
        AZN
        AV
        BCS
        BBL
        BP
        BTI
        BT
        CUK
        DEO
        GSK
        GWPH
        HSBC
        IHG
        LYG
        LXFR
        MTP
        MTFB
        NGG
        PSO
        PUK
        RELX
        RIO
        RBS
        SHPG
        SNN
        SMMT
        UL
        VOD
        WPPGY
        ";

        private const string _backtestLink = "https://www.dropbox.com/s/0c3rdc2b75byd0z/news.csv?dl=1";

        private readonly Dictionary<DateTime, double[]> _backtestNews = new Dictionary<DateTime, double[]>();
        private readonly List<string> _tokenTable = new List<string>();
        private readonly Dictionary<string, int> _tokenLookup = new Dictionary<string, int>();


        private readonly Dictionary<Symbol, Tradable> _tradables = new Dictionary<Symbol, Tradable>();

        private static NewsAlgorithm _instance;

        public override void Initialize()
        {
            _instance = this;

            SetCash(100000);
            SetStartDate(2017, 1, 1);

            var tickers = _tickers.Split(new char[] { '\n', ' ' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var t in tickers)
            {
                var ticker = t.Trim();
                if (ticker == "")
                    continue;

                var tradable = new Tradable(ticker);
                _tradables[tradable.Symbol] = tradable;
            }

            Debug("Downloading backtest data");
            DownloadBacktestData();

            Debug("Training model");
            foreach (var tradable in _tradables.Values)
                tradable.TrainModel();

            var someSecurity = _tradables.Values.First().Security;
            Schedule.On(DateRules.EveryDay(someSecurity.Symbol), TimeRules.BeforeMarketClose(someSecurity.Symbol, 11), Rebalance);

            Debug("Init finished");
        }

        private void DownloadBacktestData()
        {
            string csvData;

            using (var client = new WebClient())
            {
                csvData = client.DownloadString(_backtestLink);
            }

            var tokens = new Dictionary<string, string[]>();

            foreach (var line in csvData.Split('\n'))
            {
                var fields = line.Split(';');
                if (fields.Length != 2)
                    continue;

                var date = fields[0];
                var bbc = fields[1];

                var bbcTokens = TokenizeString(bbc);
                tokens[date] = bbcTokens;
                RegisterTokens(bbcTokens);
            }

            foreach (var kv in tokens)
            {
                _backtestNews[GetDateFromString(kv.Key)] = BuildSparseFeatureVector(kv.Value);
            }
        }

        private static DateTime GetDateFromString(string str)
        {
            var year = int.Parse(str.Substring(0, 4));
            var month = int.Parse(str.Substring(4, 2));
            var day = int.Parse(str.Substring(6, 2));
            return new DateTime(year, month, day).Date;
        }

        private static string[] TokenizeString(string str)
        {
            str = str.ToLowerInvariant();
            return str.Split(' ', '\n', ';', ',', '.', ':');
        }

        private void RegisterTokens(string[] tokens)
        {
            foreach (var token in tokens)
            {
                if (!_tokenLookup.ContainsKey(token))
                {
                    int index = _tokenTable.Count;
                    _tokenLookup[token] = index;
                    _tokenTable.Add(token);
                }
            }
        }

        private double[] BuildSparseFeatureVector(string[] tokens)
        {
            var features = new List<double>();

            var countedTokens = new Dictionary<string, int>();

            foreach (var token in tokens)
            {
                int count;
                if (countedTokens.TryGetValue(token, out count))
                {
                    countedTokens[token] = 1 + count;
                }
                else
                {
                    countedTokens[token] = 1;
                }
            }
            
            foreach (var kv in countedTokens)
            {
                int index = _tokenLookup[kv.Key];
                features.Add(1 + index);
                features.Add(kv.Value);
            }

            return features.ToArray();
        }

        private void Rebalance()
        {
            double sum = 0;
            foreach (var tradable in _tradables.Values)
            {
                sum += Math.Abs(tradable.PredictDirection());
            }

            if (sum == 0)
            {
                Liquidate();
            }
            else
            {
                foreach (var tradable in _tradables.Values)
                {
                    var dir = tradable.PredictDirection();
                    SetHoldings(tradable.Symbol, dir / sum);
                }
            }
        }

        private class Tradable
        {
            public readonly Security Security;

            public Symbol Symbol { get { return Security.Symbol; } }

            private SupportVectorMachine<SparseLinear> _classifier;

            public Tradable(string ticker)
            {
                Security = _instance.AddEquity(ticker, Resolution.Minute, leverage: 2);
                Security.FeeModel = new ConstantFeeTransactionModel(0);
            }

            private void GetInstances(out double[][] features, out double[] labels)
            {
                var f = new List<double[]>();
                var l = new List<double>();

                var minDate = _instance._backtestNews.Keys.Min();
                
                var bars = _instance.History(Symbol, minDate, _instance.UtcTime - TimeSpan.FromDays(5), Resolution.Daily);
                string currentDate = _instance.UtcTime.ToString("yyyyMMdd");
                if (bars.Count() < 2)
                {
                    _instance.Debug("Insufficient bars for " + Symbol.ToString());
                    features = new double[0][];
                    labels = new double[0];
                    return;
                }

                var prevBar = bars.First();
                foreach (var bar in bars.Skip(1))
                {
                    double[] inputs;
                    if (_instance._backtestNews.TryGetValue(prevBar.Time.Date, out inputs))
                    {
                        var change = bar.Close / prevBar.Close - 1;
                        int label = change >= 0 ? 1 : -1;
                        f.Add(inputs);
                        l.Add(label);
                    }

                    prevBar = bar;
                }

                features = f.ToArray();
                labels = l.ToArray();
            }

            public void TrainModel()
            {
                double[][] features;
                double[] labels;
                GetInstances(out features, out labels);

                if (labels.Length == 0)
                    return;

                var smo = new SequentialMinimalOptimization<SparseLinear>();
                smo.Complexity = 1000;
                _classifier = smo.Learn(features, labels);
            }

            public double PredictDirection()
            {
                if (_classifier == null)
                    return 0;

                double[] inputs;
                if (!_instance._backtestNews.TryGetValue(_instance.Time.Date, out inputs))
                    return 0;

                return _classifier.Compute(inputs);
            }
        }
    }
}