Overall Statistics
Total Trades
2
Average Win
0%
Average Loss
-0.76%
Compounding Annual Return
-97.986%
Drawdown
1.100%
Expectancy
-1
Net Profit
-0.755%
Sharpe Ratio
0
Loss Rate
100%
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.50
/*
 * 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 System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using QuantConnect.Securities;

namespace QuantConnect.Algorithm.CSharp
{
    /// <summary>
    /// This regression algorithm checks if all the option chain data coming to the algo is consistent with current securities manager state
    /// </summary>
    public class TestAlgorithm2 : QCAlgorithm
    {
        private const string UnderlyingTicker = "SPY";
        public readonly Symbol Underlying = QuantConnect.Symbol.Create(UnderlyingTicker, SecurityType.Equity, Market.USA);
        public readonly Symbol OptionSymbol = QuantConnect.Symbol.Create(UnderlyingTicker, SecurityType.Option, Market.USA);

        public override void Initialize()
        {
            SetStartDate(2015, 12, 24);
            SetEndDate(2015, 12, 24);
            SetCash(10000);

            var equity = AddEquity(UnderlyingTicker);
            var option = AddOption(UnderlyingTicker);

            // set our strike/expiry filter for this option chain
            option.SetFilter(u => u.IncludeWeeklys()
                                    .Strikes(-2, +2)
                                    .Expiration(TimeSpan.Zero, TimeSpan.FromDays(10)));

            // use the underlying equity as the benchmark
            SetBenchmark(equity.Symbol);
        }

        /// <summary>
        /// Event - v3.0 DATA EVENT HANDLER: (Pattern) Basic template for user to override for receiving all subscription data in a single event
        /// </summary>
        /// <param name="slice">The current slice of data keyed by symbol string</param>
        public override void OnData(Slice slice)
        {
            if (!Portfolio.Invested)
            {
                OptionChain chain;
                if (slice.OptionChains.TryGetValue(OptionSymbol, out chain))
                {
                    // check if data is consistent
                    foreach (var o in chain)
                    {
                        Log(string.Format("chain:{0} Bid:{1} Ask:{2} IV:{3}", o.Symbol, o.BidPrice, o.AskPrice, o.ImpliedVolatility));
                        if (!Securities.ContainsKey(o.Symbol))
                        {
                            // inconsistency found: option chains contains contract information that is not available in securities manager and not available for trading
                            throw new Exception(string.Format("inconsistency found: option chains contains contract {0} that is not available in securities manager and not available for trading", o.Symbol.Value));
                        }
                    }

                    
                    // trade
                    var contract = (
                        from optionContract in chain.OrderByDescending(x => x.Strike)
                        where optionContract.Right == OptionRight.Call
                        where optionContract.Expiry == Time.Date
                        where optionContract.Strike < chain.Underlying.Price
                        select optionContract
                        ).Skip(2).FirstOrDefault();

                    if (contract != null)
                    {
                        MarketOrder(contract.Symbol, 1);
                        MarketOnCloseOrder(contract.Symbol, -1);
                    }
                }
            }
        }

        /// <summary>
        /// Order fill event handler. On an order fill update the resulting information is passed to this method.
        /// </summary>
        /// <param name="orderEvent">Order event details containing details of the evemts</param>
        /// <remarks>This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects</remarks>
        public override void OnOrderEvent(OrderEvent orderEvent)
        {
            Log(orderEvent.ToString());
        }
    }
}
using System.Collections.Concurrent;
namespace QuantConnect {

    /*
    *   Relative Strength Index Indicator:
    *
    *                    100  
    *   RSI = 100 -  ------------
    *                   1 + RS
    *
    *   Where RS = Avg of X Period Close Up / Absolute(Avg) X of Period Close Down.
    *   
    */
    public class RelativeStrengthIndexCustom
    {
        
        //Public Access to the RSI Output
        public decimal RSI {
            get {
                return (100 - (100 / (1 + _rs)));
            }
        }
        
        //Public Access to Know if RSI Indicator Ready
        public bool Ready {
            get {
                return (_upward.Count >= _period) && (_downward.Count >= _period);
            }
        }
        
        //Private Class Variables:
        private decimal _rs = 0;
        private bool _ema = false;
        private decimal _period = 14;
        private decimal _joinBars = 1;
        private Candle _superCandle = new Candle();
        private Candle _previousCandle = new Candle();
        private FixedSizedQueue<decimal> _downward = new FixedSizedQueue<decimal>(0);
        private FixedSizedQueue<decimal> _upward = new FixedSizedQueue<decimal>(0);
        private decimal _upwardSum = 0, _avgUpward = 0;
        private decimal _downwardSum = 0, _avgDownward = 0;
        
        //Initialize the RSI with 'period' candles
        public RelativeStrengthIndexCustom(int period, int joinBars = 1, bool useEMA = false) {
            
            //Range check variables:
            if (period < 2) period = 2;
            
            //Class settings:
            _period = (decimal)period;  // How many samples is the RSI?
            _ema = useEMA;              // Use the EMA average for RSI
            _joinBars = joinBars;       // Join multiple tradebars together
            
            //Remember the upward and downward movements in a FIFO queue:
            _upward = new FixedSizedQueue<decimal>(period);
            _downward = new FixedSizedQueue<decimal>(period);
            
            //Online implementation of SMA - needs moving sum of all components:
            _upwardSum = 0; _downwardSum = 0;
        }
        
        //Add a new sample to build the RSI Indicator:
        public void AddSample(TradeBar bar) { 
            
            //Build a multibar candle, until join reached return.
            _superCandle.Update(bar);
            if (_superCandle.Samples < _joinBars) return;
            
            //Initialize the first loop.
            if (_previousCandle.Samples == 0) {
                _previousCandle = _superCandle;
                _superCandle = new Candle();
                return;
            }
            
            //Get the difference between this bar and previous bar:
            decimal difference = _superCandle.Close - _previousCandle.Close;
            
            //Update the Moving Average Calculations:
            if (difference >= 0) {
                if (_ema) {
                    _avgUpward = UpdateDirectionalEMA(ref _upward, difference);
                    _avgDownward = UpdateDirectionalEMA(ref _downward, 0);
                } else {
                    _avgUpward = UpdateDirectionalSMA(ref _upward, ref _upwardSum, difference);
                    _avgDownward = UpdateDirectionalSMA(ref _downward, ref _downwardSum, 0);
                }
            }
            if (difference <= 0) {
                difference = Math.Abs(difference);
                if (_ema) {
                    _avgUpward = UpdateDirectionalEMA(ref _upward, 0);
                    _avgDownward = UpdateDirectionalEMA(ref _downward, difference);
                } else {
                    _avgUpward = UpdateDirectionalSMA(ref _upward, ref _upwardSum, 0);
                    _avgDownward = UpdateDirectionalSMA(ref _downward, ref _downwardSum, difference);
                }
            }
            
            //Refresh RS Factor:
            //RS Index Automatically Updated in the Public Property Above:
            if (_avgDownward != 0) {
                _rs = _avgUpward / _avgDownward;
            } else {
                _rs = Decimal.MaxValue - 1;
            }
            
            //Reset for next loop:
            _previousCandle = _superCandle;
            _superCandle = new Candle();
        }
        
        
        // Update the moving average and fixed length queue in a generic fashion to work for up and downward movement.
        // Return the average.
        private decimal UpdateDirectionalSMA(ref FixedSizedQueue<decimal> queue, ref decimal sum, decimal sample) {
            
            //Increment Sum
            sum += sample;
            
            //If we've shuffled off queue, remove from sum:
            if(queue.Enqueue(sample)) {
                sum -= queue.LastDequeued;
            }
            
            //When less than period samples, only divide by the number of samples.
            if (queue.Count < _period) {
                return (sum / (decimal)queue.Count);
            } else {
                return (sum / _period);
            }
        } 
        
        
        // Update the moving average and fixed length queue in a generic fashion to work for up and downward movement.
        // Return the average.
        private decimal UpdateDirectionalEMA(ref FixedSizedQueue<decimal> queue, decimal sample) {
            queue.Enqueue(sample);
            if (queue.Count == 1) {
                return sample;
            } else {
                return (1m / _period) * sample  +  ((_period - 1m) / _period) * queue.LastEnqueued; 
            }
        }
        
        
        
        //Fixed length queue that dumps things off when no more space in queue.
        private class FixedSizedQueue<T> : ConcurrentQueue<T> {
            public int Size { get; private set; }
            public T LastDequeued { get; private set; }
            public T LastEnqueued {get; private set;}
            public bool Dequeued { get; private set; }
            public FixedSizedQueue(int size) { Size = size; }
            public new bool Enqueue(T obj) {
                base.Enqueue(obj);
                LastEnqueued = obj;
                Dequeued = false;
                lock (this) {
                    if (base.Count > Size) {
                        T outObj;
                        Dequeued = base.TryDequeue(out outObj);
                        LastDequeued = outObj;
                    }
                }
                return Dequeued;
            }
        }
        
        /// <summary>
        /// Simple online "super-tradebar" generator for making an OHLC from multiple bars.
        /// </summary>
        public class Candle {
            
            public decimal Open = 0;
            public decimal High = Decimal.MinValue;
            public decimal Low = Decimal.MaxValue;
            public decimal Close = 0;
            public int Samples = 0;
            
            public void Update(TradeBar bar) {
                if (Open == 0) Open = bar.Open;
                if (High < bar.High) High = bar.High;
                if (Low > bar.Low) Low = bar.Low;
                Close = bar.Close;
                Samples++;
            }
        }
        
    }
}