Beispiel #1
0
        /// <summary>
        /// Calculate Stochastic Oscillator, as described here:
        /// <see href="https://en.wikipedia.org/wiki/Stochastic_oscillator"/>
        /// </summary>
        /// <param name="series">input time series</param>
        /// <param name="n">oscillator period</param>
        /// <param name="parentId">caller cache id, optional</param>
        /// <param name="memberName">caller's member name, optional</param>
        /// <param name="lineNumber">caller line number, optional</param>
        /// <returns>Stochastic Oscillator as time series</returns>
        public static _StochasticOscillator StochasticOscillator(this ITimeSeries <double> series, int n = 14,
                                                                 CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0)
        {
            var cacheId = new CacheId(parentId, memberName, lineNumber,
                                      series.GetHashCode(), n);


            var container = Cache <_StochasticOscillator> .GetData(
                cacheId,
                () => new _StochasticOscillator());

            double hh = series
                        .Highest(n, cacheId)[0];

            double ll = series
                        .Lowest(n, cacheId)[0];

            double price = series[0];

            container.PercentK = IndicatorsBasic.BufferedLambda(
                v => 100.0 * (price - ll) / Math.Max(1e-10, hh - ll),
                50.0,
                cacheId);

            container.PercentD = container.PercentK
                                 .SMA(3, cacheId);

            return(container);
        }
Beispiel #2
0
 /// <summary>
 /// Calculate range over the specified number of past bars.
 /// </summary>
 /// <param name="series">input time series</param>
 /// <param name="n">number of bars to search</param>
 /// <returns>range between highest and lowest value of past n bars</returns>
 public static ITimeSeries <double> Range(this ITimeSeries <double> series, int n)
 {
     return(series
            .Highest(n)
            .Subtract(series
                      .Lowest(n)));
 }
Beispiel #3
0
        /// <summary>
        /// Calculate range over the specified number of past bars.
        /// </summary>
        /// <param name="series">input time series</param>
        /// <param name="n">number of bars to search</param>
        /// <param name="parentId">caller cache id, optional</param>
        /// <param name="memberName">caller's member name, optional</param>
        /// <param name="lineNumber">caller line number, optional</param>
        /// <returns>range between highest and lowest value of past n bars</returns>
        public static ITimeSeries <double> Range(this ITimeSeries <double> series, int n,
                                                 CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0)
        {
            var cacheId = new CacheId(parentId, memberName, lineNumber,
                                      series.GetHashCode(), n);

            return(series
                   .Highest(n, cacheId)
                   .Subtract(series
                             .Lowest(n, cacheId), cacheId));
        }
Beispiel #4
0
        /// <summary>
        /// Return current drawdown in percent, as value between 0 and 1.
        /// </summary>
        /// <param name="series">input time series</param>
        /// <param name="n">length of observation window</param>
        /// <param name="parentId">caller cache id, optional</param>
        /// <param name="memberName">caller's member name, optional</param>
        /// <param name="lineNumber">caller line number, optional</param>
        /// <returns>drawdown as time series</returns>
        public static ITimeSeries <double> Drawdown(this ITimeSeries <double> series, int n,
                                                    CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0)
        {
            var cacheId = new CacheId(parentId, memberName, lineNumber,
                                      series.GetHashCode(), n);

            // TODO: rewrite this, using buffered lambda, see MaxDrawdown
            return(IndicatorsBasic.Const(1.0, cacheId)
                   .Subtract(
                       series
                       .Divide(
                           series
                           .Highest(n, cacheId),
                           cacheId),
                       cacheId));
        }
Beispiel #5
0
        /// <summary>
        /// Calculate volatility estimate from recent trading range.
        /// </summary>
        /// <param name="series">input time series</param>
        /// <param name="n">length of calculation window</param>
        /// <param name="parentId">caller cache id, optional</param>
        /// <param name="memberName">caller's member name, optional</param>
        /// <param name="lineNumber">caller line number, optional</param>
        /// <returns>volatility as time series</returns>
        public static ITimeSeries <double> VolatilityFromRange(this ITimeSeries <double> series, int n,
                                                               CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0)
        {
            var cacheId = new CacheId(parentId, memberName, lineNumber,
                                      series.GetHashCode(), n);

            return(IndicatorsBasic.BufferedLambda(
                       (v) =>
            {
                double hi = series.Highest(n)[0];
                double lo = series.Lowest(n)[0];

                return 0.80 * Math.Sqrt(1.0 / n) * Math.Log(hi / lo);
            },
                       0.0,
                       cacheId));
        }
Beispiel #6
0
        /// <summary>
        /// Calculate Williams %R, as described here:
        /// <see href="https://en.wikipedia.org/wiki/Williams_%25R"/>
        /// </summary>
        /// <param name="series">input time series</param>
        /// <param name="n">period</param>
        /// <param name="parentId">caller cache id, optional</param>
        /// <param name="memberName">caller's member name, optional</param>
        /// <param name="lineNumber">caller line number, optional</param>
        /// <returns>Williams %R as time series</returns>
        public static ITimeSeries <double> WilliamsPercentR(this ITimeSeries <double> series, int n = 10,
                                                            CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0)
        {
            var cacheId = new CacheId(parentId, memberName, lineNumber,
                                      series.GetHashCode(), n);

            return(IndicatorsBasic.BufferedLambda(
                       (v) =>
            {
                double hh = series.Highest(n)[0];
                double ll = series.Lowest(n)[0];
                double price = series[0];
                return -100.0 * (hh - price) / Math.Max(1e-10, hh - ll);
            },
                       -50.0,
                       cacheId));
        }
Beispiel #7
0
        override public void Run()
        {
            //---------- initialization

            // set simulation time frame
            StartTime = DateTime.Parse("01/01/2017", CultureInfo.InvariantCulture);
            EndTime   = DateTime.Parse("08/01/2018", CultureInfo.InvariantCulture);

            // set account value
            Deposit(_initialCash);
            CommissionPerShare = 0.01;

            // add instruments
            // the underlying must be added explicitly,
            // as the simulation engine requires it
            AddDataSource(_underlyingNickname);
            AddDataSource(_optionsNickname);

            //---------- simulation

            // loop through all bars
            foreach (DateTime simTime in SimTimes)
            {
                // find the underlying instrument
                // we could also find the underlying from the option chain
                _underlyingInstrument = _underlyingInstrument ?? FindInstrument(_underlyingNickname);

                // retrieve the underlying spot price
                double underlyingPrice = _underlyingInstrument.Close[0];
                _initialUnderlyingPrice = _initialUnderlyingPrice ?? underlyingPrice;

                // calculate volatility
                ITimeSeries <double> volatilitySeries = _underlyingInstrument.Close
                                                        .Volatility(10)
                                                        .Multiply(Math.Sqrt(252.0));
                double volatility = Math.Max(
                    volatilitySeries.EMA(21)[0],
                    volatilitySeries.Highest(5)[0]);

                // find all expiry dates on the 3rd Friday of the month
                List <DateTime> expiryDates = OptionChain(_optionsNickname)
                                              .Where(o => o.OptionExpiry.DayOfWeek == DayOfWeek.Friday && o.OptionExpiry.Day >= 15 && o.OptionExpiry.Day <= 21 ||
                                                     o.OptionExpiry.DayOfWeek == DayOfWeek.Saturday && o.OptionExpiry.Day >= 16 && o.OptionExpiry.Day <= 22)
                                              .Select(o => o.OptionExpiry)
                                              .Distinct()
                                              .ToList();

                // select expiry 3 to 4 weeks out
                DateTime expiryDate = expiryDates
                                      .Where(d => (d - simTime).TotalDays >= 21 &&
                                             (d - simTime).TotalDays <= 28)
                                      .FirstOrDefault();

                // retrieve option chain for this expiry
                List <Instrument> optionChain = OptionChain(_optionsNickname)
                                                .Where(o => o.OptionIsPut &&
                                                       o.OptionExpiry == expiryDate)
                                                .ToList();

                // if we are currently flat, attempt to open a position
                if (Positions.Count == 0)
                {
                    // determine strike price: far away from spot price
                    double strikePrice = _underlyingInstrument.Close[0]
                                         / Math.Exp(1.75 * Math.Sqrt((expiryDate - simTime).TotalDays / 365.25) * volatility);

                    // find contract closest to our desired strike
                    Instrument shortPut = optionChain
                                          .OrderBy(o => Math.Abs(o.OptionStrike - strikePrice))
                                          .FirstOrDefault();

                    // enter short put position
                    if (shortPut != null)
                    {
                        // Interactive Brokers margin requirements for short naked puts:
                        // Put Price + Maximum((15 % * Underlying Price - Out of the Money Amount),
                        //                     (10 % * Strike Price))
                        double margin = Math.Max(0.15 * underlyingPrice - Math.Max(0.0, underlyingPrice - shortPut.OptionStrike),
                                                 0.10 * underlyingPrice);
                        int contracts = (int)Math.Floor(Math.Max(0.0, _regTMarginToUse * Cash / (100.0 * margin)));

                        shortPut.Trade(-contracts, OrderType.closeThisBar)
                        .Comment = "open";
                    }
                }

                // monitor and maintain existing positions
                else // if (Postions.Count != 0)
                {
                    // find our currently open position
                    // we might need fancier code, in case we have more than
                    // one position open
                    Instrument shortPut = Positions.Keys.First();

                    // re-evaluate the likely trading range
                    double expectedLowestPrice = _underlyingInstrument.Close[0]
                                                 / Math.Exp(0.60 * Math.Sqrt((shortPut.OptionExpiry - simTime).Days / 365.25) * volatility);

                    // exit, when the risk of ending in the money is too high
                    // and, the contract is actively traded
                    if (expectedLowestPrice < shortPut.OptionStrike &&
                        shortPut.BidVolume[0] > 0 &&
                        shortPut.Ask[0] < 2 * shortPut.Bid[0])
                    {
                        shortPut.Trade(-Positions[shortPut], OrderType.closeThisBar)
                        .Comment = "exit early";
                    }
                }

                // plot the underlying against our strategy results, plus volatility
                _plotter.SelectChart("nav vs time", "time"); // this will go to Sheet1
                _plotter.SetX(simTime);
                _plotter.Plot(_underlyingInstrument.Symbol, underlyingPrice / (double)_initialUnderlyingPrice);
                _plotter.Plot("volatility", volatilitySeries[0]);
                _plotter.Plot("net asset value", NetAssetValue[0] / _initialCash);
            }

            //---------- post-processing

            // create a list of trades on Sheet2
            _plotter.SelectChart("trades", "time");
            foreach (LogEntry entry in Log)
            {
                _plotter.SetX(entry.BarOfExecution.Time);
                _plotter.Plot("action", entry.Action);
                _plotter.Plot("instr", entry.Symbol);
                _plotter.Plot("qty", entry.OrderTicket.Quantity);
                _plotter.Plot("fill", entry.FillPrice);
                _plotter.Plot("comment", entry.OrderTicket.Comment ?? "");
            }

            FitnessValue = NetAssetValue[0];
        }