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
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
/*
 * 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 MathNet.Numerics.Statistics;

namespace QuantConnect.Algorithm.CSharp
{
	
    public class HurstExample : QCAlgorithm
    {

        HurstExponent hurst1;
        HurstExponent hurst2;
        HurstExponent hurst3;

        TradeBarConsolidator con2 = new TradeBarConsolidator(TimeSpan.FromDays(20));
        RelativeStrengthIndex rsi1;
        RelativeStrengthIndex rsi2;

        AverageDirectionalIndex adx;

        Dictionary<string, HurstExponent.HurstExponentResult> result = new Dictionary<string, HurstExponent.HurstExponentResult>
        {
            { "SPY", HurstExponent.HurstExponentResult.None },
            { "SH", HurstExponent.HurstExponentResult.None },
            { "UCO", HurstExponent.HurstExponentResult.None },
        };

        decimal volumeSum;


        public override void Initialize()
        {
            SetStartDate(2001, 1, 1);  //Set Start Date
            SetEndDate(2017, 1, 1);    //Set End Date
            SetCash(100000);     

            AddEquity("SPY", Resolution.Daily, Market.USA);
            AddEquity("SH", Resolution.Daily, Market.USA);
            AddEquity("UCO", Resolution.Daily, Market.USA);

            adx = ADX("SPY", 180, Resolution.Daily);
            rsi1 = RSI("SPY", 7, MovingAverageType.Exponential, Resolution.Daily);
            rsi2 = RSI("SH", 7, MovingAverageType.Exponential, Resolution.Daily);

            int period = 1000;
            hurst1 = new HurstExponent(period);
            hurst2 = new HurstExponent("", 100,10);
            hurst3 = new HurstExponent(period);

            con2.DataConsolidated += Con_DataConsolidated;
        }

        private void Con_DataConsolidated(object sender, Data.Market.TradeBar e)
        {
            hurst2.Update(ToPoint(e));
        }

        public override void OnData(Slice data)
        {
            if (data.Bars.ContainsKey("UCO"))
            {
                var bar = new TradeBar(data.Bars["UCO"]);
                volumeSum += data.Bars["UCO"].Volume;
                bar.Value = volumeSum;
                con2.Update(bar);
            }
            if (data.Bars.ContainsKey("SPY"))
            {
                hurst1.Update(ToPoint(data.Bars["SPY"]));
            }
            if (data.Bars.ContainsKey("SH") && rsi1.IsReady && rsi2.IsReady)
            {
                var reverting = rsi1.Current - rsi2.Current;
                data.Bars["SH"].Value = reverting;
                hurst3.Update(ToPoint(data.Bars["SH"]));
            }

            if (hurst1.IsReady)
            {
                Output(hurst1, "SPY");
            }
            if (hurst2.IsReady)
            {
                Output(hurst2, "UCO");
            }
            if (hurst3.IsReady)
            {
                Output(hurst3, "SH");
            }

        }

        private IndicatorDataPoint ToPoint(TradeBar bar)
        {
            return new IndicatorDataPoint(bar.Symbol, bar.Time, bar.Value);
        }

        private void Output(HurstExponent hurst, string symbol)
        {
            if (result[symbol] != hurst.CurrentResult())
            {
                Debug(string.Format("{0} {1} {2} {3}", this.Time, symbol, hurst.Current.Value.ToString("0.0000"), hurst.CurrentResult().ToString()));
                Plot(symbol.ToString() + ": " + hurst.CurrentResult().ToString(), hurst.Current.Value);

                result[symbol] = hurst.CurrentResult();
            }

        }

    }
    
    public class HurstExponent : IndicatorBase<IndicatorDataPoint>
    {

        int _period;
        int _lagVector;
        int[] lag;
        FixedSizeHashQueue<double> p;
        decimal _hurst;

        public enum HurstExponentResult
        {
            GeometricBrownianMotion,
            MeanReversion,
            Trend,
            StrongTrend,
            None
        }

        public HurstExponent(int period)
            : this(string.Format("Hurst({0})", period), period)
        {
        }

        public HurstExponent(string name, int period, int lagVector = 100)
            : base(name)
        {
            if (period <= lagVector)
            {
                throw new ArgumentException("The period must be greater than lag vector.");
            }

            _period = period;
            _lagVector = lagVector;

            lag = new int[lagVector - 2];

            for (int i = 2; i < lagVector; i++)
            {
                lag[i - 2] = i;
            }

            p = new FixedSizeHashQueue<double>(period);
        }

        public override bool IsReady
        {
            get
            {
                return p.Count() >= _period;
            }
        }

        public HurstExponentResult CurrentResult()
        {
            if (_hurst > 0.4m && _hurst < 0.6m)
            {
                return HurstExponentResult.GeometricBrownianMotion;
            }
            else if (_hurst > -0.1m && _hurst < 0.1m)
            {
                return HurstExponentResult.MeanReversion;
            }
            else if (_hurst > 0.9m)
            {
                return HurstExponentResult.StrongTrend;
            }
            else if (_hurst > 0.6m)
            {
                return HurstExponentResult.Trend;
            }

            return HurstExponentResult.None;
        }

        protected override decimal ComputeNextValue(IndicatorDataPoint input)
        {
            p.Add((double)input.Price);

            this.Current.Symbol = input.Symbol;

            if (!this.IsReady) { return 0; }

            var tau = new List<double>();

            foreach (var item in lag)
            {
                var start = p.Skip(item).ToArray();
                var end = p.Take(p.Count() - item).ToArray();

                var pp = new double[start.Length];
                for (int ii = 0; ii < start.Count(); ii++)
                {
                    pp[ii] = start[ii] - end[ii];
                }

                tau.Add(Math.Sqrt(pp.PopulationStandardDeviation()));
            }

            var x = lag.Select(l => Math.Log(l)).ToArray();
            var y = tau.Select(t => Math.Log(t)).ToArray();
            var fit = MathNet.Numerics.Fit.Polynomial(x, y, 1);

            if (double.IsNaN(fit[1]) || double.IsInfinity(fit[1]))
            {
                return -10m;
            }

            _hurst = (decimal)(fit[1] * 2.0);

            return _hurst;
        }
    }
    
}