Files
Martin-Molinero 27de93f78f
Syntax Tests / build (push) Has been cancelled
API Tests / build (push) Has been cancelled
Benchmarks / build (push) Has been cancelled
Build & Test Lean / build (push) Has been cancelled
Regression Tests / build (push) Has been cancelled
Report Generator Tests / build (push) Has been cancelled
Research Regression Tests / build (push) Has been cancelled
Python Virtual Environments / build (push) Has been cancelled
Market orders wait for fresh data instead of filling on stale prices (#9535)
* Market orders wait for fresh data instead of filling on stale prices

A market order would previously fill immediately on the most recent
available data even when that data was older than StalePriceTimeSpan
(default one hour), only attaching a warning. This is unrealistic for a
coarse resolution asset (hour/daily) where the latest bar is the stale
previous close when the order is placed mid-bar or via an intraday
scheduled event.

The default fill models (FillModel, EquityFillModel, FutureFillModel) now
wait for fresh data instead of filling on a stale price, but only for hour
and daily resolutions; the order fills when the next bar closes. For
minute/second/tick subscriptions the previous behavior is kept (fill on
the stale price with a warning), since stale data there is a genuine gap
rather than a bar still forming.

Adds HourResolutionMarketOrderStalePriceRegressionAlgorithm, updates the
FillOutsideHours daily expectation, and regenerates statistics for the
hour/daily algorithms whose fills change. FutureOptionDaily buys and
liquidates a day apart now (a same-day buy + liquidate cannot fill on
daily data once stale fills are disabled).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Normalize and correct StalePriceTimeSpan XML docs

The interface and class docs now match and reflect the actual behavior:
the wait-for-fresh-data only applies to hour/daily resolutions, while
minute/second/tick subscriptions still fill on stale data with a warning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fill resting market orders at the bar open instead of the close

A hour/daily market order that was resting before the current bar opened
(it predates the bar - placed after the previous close or while waiting
for fresh data) now fills at the bar open, the price when trading resumed
(like a MarketOnOpen), instead of the bar close. Orders placed during the
bar still fill at the current/close price, so intraday mid-bar fills are
unchanged. Equity fills are unchanged (resting equity orders are already
converted to MarketOnOpen by QCAlgorithm.MarketOrder).

Adds the shared FillModel.GetMarketFillPrice helper used by the base
FillModel and FutureFillModel, a unit test, and regenerates statistics for
the affected daily/hour futures, index and crypto regression algorithms
(order counts unchanged, only fill prices).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add regression algorithm asserting resting market orders fill at the bar open

RestingMarketOrderFillsAtBarOpenRegressionAlgorithm buys a daily future on the
bar that delivers it (fills at that bar's close) and submits a liquidation while
the market is closed (overnight pulse, no fresh bar). The liquidation rests and
fills on a later bar at the bar open, not its close - asserting the new
GetMarketFillPrice behavior. The in-bar buy is asserted to fill at the close, for
contrast.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Carry the bar start time on Prices instead of re-reading the cache

Add Prices.Time (the bar start, mirroring BaseData.Time/EndTime), populated from
the source bar/tick in every GetPrices path. GetMarketFillPrice now uses
prices.Time directly instead of a second asset.Cache.GetData() lookup. Behavior
is unchanged (prices.Time equals the previously read cache time).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add regression algorithm asserting in-session hour orders fill at the latest close

HourMarketOrderFillsAtBarCloseRegressionAlgorithm submits an hour resolution
market order mid-bar (via an intraday scheduled event) while the market is open,
using the default one hour StalePriceTimeSpan. It asserts the order fills
immediately at the latest available bar's close - not waiting and not at the bar
open - since the latest bar is within the stale window. Guards the resting-order
open-fill behavior against affecting ordinary in-session fills.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Regenerate custom fill model algorithm statistics for the open-fill change

CustomModelsAlgorithm and CustomPartialFillModelAlgorithm subscribe SPY at hour
resolution and their custom fill models delegate to base.MarketFill, so resting
orders now fill at the bar open. Regenerate their statistics (C#/Python) and the
inline expected statistics of the PEP8StyleCustomModelsWork test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 11:59:19 -03:00

204 lines
9.5 KiB
C#

/*
* 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 QuantConnect.Interfaces;
using QuantConnect.Securities;
using QuantConnect.Orders.Fills;
using QuantConnect.Configuration;
namespace QuantConnect
{
/// <summary>
/// This class includes user settings for the algorithm which can be changed in the <see cref="IAlgorithm.Initialize"/> method
/// </summary>
public class AlgorithmSettings : IAlgorithmSettings
{
private static TimeSpan _defaultDatabasesRefreshPeriod =
TimeSpan.TryParse(Config.Get("databases-refresh-period", "1.00:00:00"), out var refreshPeriod) ? refreshPeriod : Time.OneDay;
// We default this to true so that we don't terminate live algorithms when the
// brokerage account has existing holdings for an asset that is not supported by Lean.
// Users can override this on initialization so that the algorithm is not terminated when
// placing orders for assets without a correct definition or mapping.
private static bool _defaultIgnoreUnknownAssetHoldings = Config.GetBool("ignore-unknown-asset-holdings", true);
/// <summary>
/// Gets whether or not WarmUpIndicator is allowed to warm up indicators
/// </summary>
public bool AutomaticIndicatorWarmUp { get; set; }
/// <summary>
/// True if should rebalance portfolio on security changes. True by default
/// </summary>
public bool? RebalancePortfolioOnSecurityChanges { get; set; }
/// <summary>
/// True if should rebalance portfolio on new insights or expiration of insights. True by default
/// </summary>
public bool? RebalancePortfolioOnInsightChanges { get; set; }
/// <summary>
/// The absolute maximum valid total portfolio value target percentage
/// </summary>
/// <remarks>This setting is currently being used to filter out undesired target percent values,
/// caused by the IPortfolioConstructionModel implementation being used.
/// For example rounding errors, math operations</remarks>
public decimal MaxAbsolutePortfolioTargetPercentage { get; set; }
/// <summary>
/// The absolute minimum valid total portfolio value target percentage
/// </summary>
/// <remarks>This setting is currently being used to filter out undesired target percent values,
/// caused by the IPortfolioConstructionModel implementation being used.
/// For example rounding errors, math operations</remarks>
public decimal MinAbsolutePortfolioTargetPercentage { get; set; }
/// <summary>
/// Configurable minimum order margin portfolio percentage to ignore bad orders, orders with unrealistic small sizes
/// </summary>
/// <remarks>Default value is 0.1% of the portfolio value. This setting is useful to avoid small trading noise when using SetHoldings</remarks>
public decimal MinimumOrderMarginPortfolioPercentage { get; set; }
/// <summary>
/// Gets/sets the maximum number of concurrent market data subscriptions available
/// </summary>
/// <remarks>
/// All securities added with <see cref="IAlgorithm.AddSecurity"/> are counted as one,
/// with the exception of options and futures where every single contract in a chain counts as one.
/// </remarks>
[Obsolete("This property is deprecated. Please observe data subscription limits set by your brokerage to avoid runtime errors.")]
public int DataSubscriptionLimit { get; set; } = int.MaxValue;
/// <summary>
/// Gets/sets the SetHoldings buffers value.
/// The buffer is used for orders not to be rejected due to volatility when using SetHoldings and CalculateOrderQuantity
/// </summary>
public decimal? FreePortfolioValue { get; set; }
/// <summary>
/// Gets/sets the SetHoldings buffers value percentage.
/// This percentage will be used to set the <see cref="FreePortfolioValue"/>
/// based on the <see cref="SecurityPortfolioManager.TotalPortfolioValue"/>
/// </summary>
public decimal FreePortfolioValuePercentage { get; set; }
/// <summary>
/// Gets/sets if Liquidate() is enabled
/// </summary>
public bool LiquidateEnabled { get; set; }
/// <summary>
/// Gets/sets the minimum time span elapsed to consider a market fill price as stale (defaults to one hour)
/// </summary>
/// <remarks>
/// In the default fill models, a market order on an hour or daily resolution subscription is not filled on
/// data older than this time span; instead it waits for fresh data (e.g. the next bar), avoiding a
/// fill at the stale previous close. Market orders on minute/second/tick subscriptions still fill on stale
/// data, only adding a warning message. Tighten it (e.g. to one minute) to make hour/daily orders wait for
/// the next bar more aggressively.
/// </remarks>
/// <seealso cref="FillModel"/>
/// <seealso cref="ImmediateFillModel"/>
public TimeSpan StalePriceTimeSpan { get; set; }
/// <summary>
/// The warmup resolution to use if any
/// </summary>
/// <remarks>This allows improving the warmup speed by setting it to a lower resolution than the one added in the algorithm</remarks>
public Resolution? WarmupResolution { get; set; }
/// <summary>
/// The warmup resolution to use if any
/// </summary>
/// <remarks>This allows improving the warmup speed by setting it to a lower resolution than the one added in the algorithm.
/// Pass through version to be user friendly</remarks>
public Resolution? WarmUpResolution
{
get
{
return WarmupResolution;
}
set
{
WarmupResolution = value;
}
}
/// <summary>
/// Number of trading days per year for this Algorithm's portfolio statistics.
/// </summary>
/// <remarks>Effect on
/// <see cref="Statistics.PortfolioStatistics.AnnualVariance"/>,
/// <seealso cref="Statistics.PortfolioStatistics.AnnualStandardDeviation"/>,
/// <seealso cref="Statistics.PortfolioStatistics.SharpeRatio"/>,
/// <seealso cref="Statistics.PortfolioStatistics.SortinoRatio"/>,
/// <seealso cref="Statistics.PortfolioStatistics.TrackingError"/>,
/// <seealso cref="Statistics.PortfolioStatistics.InformationRatio"/>.
/// </remarks>
public int? TradingDaysPerYear { get; set; }
/// <summary>
/// True if daily strict end times are enabled
/// </summary>
public bool DailyPreciseEndTime { get; set; }
/// <summary>
/// True if extended market hours should be used for daily consolidation, when extended market hours is enabled
/// </summary>
public bool DailyConsolidationUseExtendedMarketHours { get; set; }
/// <summary>
/// Gets the time span used to refresh the market hours and symbol properties databases
/// </summary>
public TimeSpan DatabasesRefreshPeriod { get; set; }
/// <summary>
/// Determines whether to terminate the algorithm when an asset holding is not supported by Lean or the brokerage.
/// Defaults to true, meaning that the algorithm will not be terminated if an asset holding is not supported.
/// </summary>
public bool IgnoreUnknownAssetHoldings { get; set; }
/// <summary>
/// Performance tracking sample period to use if any, useful to debug performance issues
/// </summary>
public TimeSpan PerformanceSamplePeriod { get; set; }
/// <summary>
/// Determines whether to seed initial prices for all selected and manually added securities.
/// </summary>
public bool SeedInitialPrices { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="AlgorithmSettings"/> class
/// </summary>
public AlgorithmSettings()
{
LiquidateEnabled = true;
DailyPreciseEndTime = true;
FreePortfolioValuePercentage = 0.0025m;
// Because the free portfolio value has a trailing behavior by default, let's add a default minimum order margin portfolio percentage
// to avoid tiny trades when rebalancing, defaulting to 0.1% of the TPV
MinimumOrderMarginPortfolioPercentage = 0.001m;
StalePriceTimeSpan = Time.OneHour;
MaxAbsolutePortfolioTargetPercentage = 1000000000;
MinAbsolutePortfolioTargetPercentage = 0.0000000001m;
DatabasesRefreshPeriod = _defaultDatabasesRefreshPeriod;
IgnoreUnknownAssetHoldings = _defaultIgnoreUnknownAssetHoldings;
SeedInitialPrices = false;
}
}
}