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