| 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++;
}
}
}
}