| Overall Statistics |
|
Total Orders 8 Average Win 0% Average Loss -0.01% Compounding Annual Return -32.602% Drawdown 0.800% Expectancy -1 Start Equity 100000 End Equity 99281.93 Net Profit -0.718% Sharpe Ratio -10.591 Sortino Ratio -10.429 Probabilistic Sharpe Ratio 0.008% Loss Rate 100% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.294 Beta 0.063 Annual Standard Deviation 0.03 Annual Variance 0.001 Information Ratio 0.122 Tracking Error 0.186 Treynor Ratio -4.99 Total Fees $2.40 Estimated Strategy Capacity $220000.00 Lowest Capacity Asset AAPL YL0TBVB83CP2|AAPL R735QTJ8XC9X Portfolio Turnover 0.42% |
#region imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Drawing;
using QuantConnect;
using QuantConnect.Algorithm.Framework;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Algorithm.Selection;
using QuantConnect.Parameters;
using QuantConnect.Benchmarks;
using QuantConnect.Brokerages;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Custom;
using QuantConnect.DataSource;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Notifications;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.Interfaces;
using QuantConnect.Storage;
using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion
namespace QuantConnect.Algorithm.CSharp
{
public class UglySkyBlueScorpion : QCAlgorithm
{
bool _isFutureAccountType = false;
Symbol _equitySymbol;
Symbol _optionContract;
Symbol _futureContract;
List<Symbol> _symbolsToTrade = new();
public override void Initialize()
{
SetStartDate(2024, 7, 20);
SetCash(100000);
SetBrokerageModel(BrokerageName.TradeStation);
if (!_isFutureAccountType)
{
_equitySymbol = AddEquity("AAPL", fillForward: false).Symbol;
var option = AddOption(_equitySymbol, fillForward: false);
option.SetFilter(u => u.Strikes(0, +1).CallsOnly().Expiration(0, 30));
_symbolsToTrade.Add(_equitySymbol);
}
else
{
var future = AddFuture(Futures.Indices.SP500EMini, fillForward: false);
future.SetFilter(TimeSpan.Zero, TimeSpan.FromDays(182));
}
}
int _testCase = 0;
int _openOrdersTimeout;
public override void OnData(Slice slice)
{
Debug($"Time {slice.Time}. Bars: {string.Join(",", slice.Bars)}. QuoteBars: {string.Join(",", slice.QuoteBars)}");
if (!SetFutureContract(slice))
{
Debug($"{Time}: Waiting for future contract to be set...");
return;
}
if (!SetOptionContract(slice))
{
Debug($"{Time}: Waiting for option contract to be set...");
return;
}
foreach (var symbol in _symbolsToTrade)
{
if (Securities[symbol].Price == 0)
{
Debug($"{Time}: Waiting for {symbol} to have price...");
return;
}
}
// TRADE
if (_testCase == 0)
{
if (Portfolio.Invested)
{
Debug($"{Time}: Liquidating so we start from scratch");
Liquidate();
return;
}
if (Transactions.GetOpenOrders().Count > 0)
{
Debug($"{Time}: Cancelling open orders so we start from scratch");
Transactions.CancelOpenOrders();
return;
}
Debug($"{Time}: Sending market orders");
foreach (var symbol in _symbolsToTrade)
{
MarketOrder(symbol, 1);
}
_testCase = 1;
}
else if (_testCase == 1)
{
Debug($"{Time}: Sending limit orders");
foreach (var symbol in _symbolsToTrade)
{
// bellow market price so triggers asap
LimitOrder(symbol, -1, GetOrderPrice(symbol, aboveTheMarket: false));
}
_testCase = 2;
}
else if (_testCase == 2)
{
if (Portfolio.Invested)
{
// should be filled
Debug($"{Time}: Liquidating so we start from scratch");
Liquidate();
return;
}
Debug($"{Time}: Sending StopMarketOrder orders");
foreach (var symbol in _symbolsToTrade)
{
// Buy Stop order is always placed above the current market price
StopMarketOrder(symbol, 1, GetOrderPrice(symbol, aboveTheMarket: true));
}
_testCase = 3;
}
else if (_testCase == 3)
{
if (Transactions.GetOpenOrders().Count > 0)
{
if (_openOrdersTimeout++ > 3)
{
Debug($"{Time}: Tiemout waiting for orders to fill, cancelling");
Transactions.CancelOpenOrders();
return;
}
else
{
Debug($"{Time}: Has open orders, waiting...");
return;
}
}
Debug($"{Time}: Sending StopLimitOrder orders");
foreach (var symbol in _symbolsToTrade)
{
var aboveTheMarket = GetOrderPrice(symbol, aboveTheMarket: false);
StopLimitOrder(symbol, 1, aboveTheMarket, aboveTheMarket);
}
_testCase = 4;
Debug($"{Time}: The END!");
}
}
// get a valid price above or below market, for options respect 0.05m increment
private decimal GetOrderPrice(Symbol symbol, bool aboveTheMarket)
{
var assetPrice = Securities[symbol].Price;
if (aboveTheMarket)
{
if (symbol.SecurityType.IsOption() && assetPrice >= 2.95m)
{
return (assetPrice + 0.05m).DiscretelyRoundBy(0.05m);
}
assetPrice = assetPrice + Math.Min(assetPrice * 0.001m, 0.25m);
}
else
{
if (symbol.SecurityType.IsOption() && assetPrice >= 2.95m)
{
return (assetPrice - 0.05m).DiscretelyRoundBy(0.05m);
}
assetPrice = assetPrice - Math.Min(assetPrice * 0.001m, 0.25m);
}
return assetPrice;
}
private bool SetFutureContract(Slice slice)
{
if (!_isFutureAccountType)
{
return true;
}
if (_futureContract == null)
{
foreach(var chain in slice.FutureChains.Values)
{
var contract = ( from futuresContract in chain.OrderBy(x => x.Expiry)
where futuresContract.Expiry > Time.Date.AddDays(90)
select futuresContract
).FirstOrDefault();
_futureContract = contract.Symbol;
if (_futureContract != null)
{
_symbolsToTrade.Add(_futureContract);
break;
}
}
}
return _futureContract != null;
}
private bool SetOptionContract(Slice slice)
{
if (_isFutureAccountType)
{
return true;
}
if (_optionContract == null)
{
foreach(var optionChain in slice.OptionChains.Values)
{
var atmContract = optionChain
.OrderByDescending(x => x.Expiry)
.ThenBy(x => Math.Abs(optionChain.Underlying.Price - x.Strike))
.ThenByDescending(x => x.Right)
.FirstOrDefault();
_optionContract = atmContract.Symbol;
if (_optionContract != null)
{
_symbolsToTrade.Add(_optionContract);
break;
}
}
}
return _optionContract != null;
}
}
}