Hi there,Im hoping somebody can share some insight on this because I feel like im going crazy.
I have a verrry simple reproduceable Algo which has hard codes long/short BTCUSDT trades using SetHoldings on the Binance Margin Broker. The orders are being filled, and looking at the updated holdings value that number is changing inbeweeen trades. However, when looking at the resulting statistics everything is zero execpt the fees. Whats going on here? I would expect at least SOME results in the other metrics, this is telling me something is broken?
This works when trading SPY, so im assuming this is something i need to do differently when trading BTCUSDT Binance Margin?
20240322 09:38:37.449 TRACE::
STATISTICS:: Total Orders 23
STATISTICS:: Average Win 0%
STATISTICS:: Average Loss 0%
STATISTICS:: Compounding Annual Return 0%
STATISTICS:: Drawdown 0%
STATISTICS:: Expectancy 0
STATISTICS:: Net Profit 0%
STATISTICS:: Sharpe Ratio 0
STATISTICS:: Sortino Ratio 0
STATISTICS:: Probabilistic Sharpe Ratio 0%
STATISTICS:: Loss Rate 0%
STATISTICS:: Win Rate 0%
STATISTICS:: Profit-Loss Ratio 0
STATISTICS:: Alpha 0
STATISTICS:: Beta 0
STATISTICS:: Annual Standard Deviation 0
STATISTICS:: Annual Variance 0
STATISTICS:: Information Ratio 0
STATISTICS:: Tracking Error 0
STATISTICS:: Treynor Ratio 0
STATISTICS:: Total Fees ₮425.17
STATISTICS:: Estimated Strategy Capacity ₮960000000.00
STATISTICS:: Lowest Capacity Asset BTCUSDT 18N
STATISTICS:: Portfolio Turnover 2247.40%
STATISTICS:: OrderListHash 457ef36c8c3920a961de0a0aa06c25b1
Here is the reproduceable source code, if someone can please give me some ideas what could be going wrong here.
# region imports
from AlgorithmImports import *
# endregion
class Sandbox(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2024, 2, 27) # Set Start Date
self.SetEndDate(2024, 2, 28) # Set End Date self.SetEndDate(2013, 10, 11)
self.SetAccountCurrency("USDT")
self.AddCrypto("BTCUSDT", Resolution.Second, market="binance")
self.SetBrokerageModel(BrokerageName.Binance, AccountType.Margin)
self.SetBenchmark("BTCUSDT")
self.SetCash(10000) # Set Strategy Cash
def OnData(self, data: Slice):
"""OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
"""
marginRemaining = self.Portfolio.GetMarginRemaining("BTCUSDT")
holding = self.Portfolio["BTCUSDT"]
if long_short_orders.get(str(data.Time)) == 'long':
self.SetHoldings("BTCUSDT", 1)
self.Debug(f"{str(data.Time)}: {long_short_orders.get(str(data.Time))} ~ {holding}")
if long_short_orders.get(str(data.Time)) == 'short':
self.SetHoldings("BTCUSDT", -1)
self.Debug(f"{str(data.Time)}: {long_short_orders.get(str(data.Time))} ~ {holding}")
if long_short_orders.get(str(data.Time)) == 'done':
self.SetHoldings("BTCUSDT", 0)
self.Debug(f"{str(data.Time)}: {long_short_orders.get(str(data.Time))} ~ {holding}")
self.Debug("Done")
def OnOrderEvent(self, orderEvent):
if orderEvent.Status == OrderStatus.Filled:
self.Debug(f"Order filled: {orderEvent}")
long_short_orders = {
'2024-02-27 05:35:00':'long',
'2024-02-27 05:52:00':'short',
'2024-02-27 05:54:00':'long',
'2024-02-27 06:07:00':'short',
'2024-02-27 06:18:00':'long',
'2024-02-27 06:25:00':'short',
'2024-02-27 06:29:00':'long',
'2024-02-27 06:45:00':'short',
'2024-02-27 06:49:00':'long',
'2024-02-27 06:55:00':'short',
'2024-02-27 07:07:00':'long',
'2024-02-27 07:14:00':'short',
'2024-02-27 07:18:00':'long',
'2024-02-27 07:22:00':'short',
'2024-02-27 07:37:00':'long',
'2024-02-27 07:48:00':'short',
'2024-02-27 07:55:00':'long',
'2024-02-27 08:29:00':'short',
'2024-02-27 08:32:00':'long',
'2024-02-27 08:49:00':'short',
'2024-02-27 09:09:00':'long',
'2024-02-27 09:43:00':'short',
'2024-02-27 10:04:00':'done',
}
Thanks!
Mia Alissi
Nick, from the information provided and the code snippet, it seems like your algorithm is executing trades as expected since you're seeing changes in holdings and incurring fees. However, the statistics showing all zeros except for the fees indicate that the algorithm might not be capturing profit or loss from these trades correctly, or the trades are not impacting the portfolio value as expected.
Here are a few things to consider that might help diagnose the issue:
Resolution and Market Impact: You're using
Resolution.Second
for trading BTCUSDT on Binance. This high frequency might lead to many trades with minimal profit/loss per trade, especially considering the spread and slippage. The cumulative effect might be negligible, leading to seemingly zero impact on the statistics.Price Movement: Ensure there's significant price movement within your trading period that would lead to noticeable gains or losses. If the price movement is minimal or if your entry and exit points are too close in value, the net result might be close to zero after accounting for fees.
Order Execution: Verify that your orders are being executed at expected prices. Use the
OnOrderEvent
method to log order execution details. You're already logging when an order is filled, but consider logging the execution price and comparing it with your expectations.Profit and Loss Calculation: The algorithm framework calculates statistics based on the performance of your portfolio over time. If your trades are not leading to an increase or decrease in portfolio value (after accounting for fees), the statistics will reflect that. Remember, fees can erode small profits, especially in high-frequency trading.
Debugging: Increase the verbosity of your debugging. Log the portfolio value, cash balance, and holdings before and after trades to see how they're affected. This might give you insights into why the statistics are not reflecting expected outcomes.
SetHoldings vs. Manual Orders: You're using
SetHoldings
to enter and exit positions. While this is convenient, it abstracts away the details of order execution. For troubleshooting, consider using manual order methods (MarketOrder
,LimitOrder
, etc.) where you have more control and visibility over the order size and execution price.Time Zone: Ensure the timestamps in
long_short_orders
match the data's timezone. LEAN uses UTC time; if there's a mismatch, orders might not be placed as expected.Data Subscription: Double-check that your data subscription is correct and that you're receiving the expected data for BTCUSDT. Missing or incorrect data can lead to unexpected behavior.
If after checking these points the issue persists, consider simplifying the algorithm to trade less frequently or over a longer period to see if the statistics start reflecting the expected behavior. This might help isolate whether the issue is with the trading logic, data, or the calculation of statistics.
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Nick Grobler
Hey Mia, thanks for your super fast response!
This is a good checklist, ill keep this pinned for the future! The higher resolution had crossed my mind but it seemed odd that this could be the reason why I woudnt see all NULL results.
Your point about increasing the period window was the golden ticket here though. If I increase the EndDate by an extra day I start to get results. However I feel like this is a bug or should be clearly documented, as the trades are exactly the same as before, and the final one sets the holding to 0 so increasing the EndDate shouldnt change anything. Also the orders in both cases fall well within the Set Date windows.
It appears LEAN requires greater than 1 day in order to calculate Result Statistics, is this a known thing?
Mia Alissi
Nick, I'm glad to hear that extending the end date helped reveal some results! Your observation about needing more than one day for LEAN to calculate result statistics is insightful and touches on a nuanced aspect of backtesting with LEAN.
In LEAN, certain statistics and calculations, especially those related to annual returns, Sharpe Ratio, and other metrics that depend on a time series of data, may require more than a single day of data to produce meaningful results. This is because:
Annualization: Metrics like the Compounding Annual Return and Sharpe Ratio involve annualization calculations. These calculations assume a certain number of trading days in a year to extrapolate the performance over a year. With only a single day of data, the annualization process might not work as expected, leading to zero or undefined results.
Daily Returns Calculation: Some statistics require at least two data points to calculate daily returns. The daily return is a fundamental component in calculating volatility, Sharpe Ratio, and other risk-adjusted return metrics. Without at least two days, it's impossible to compute a daily return.
Settlement and Realization of PnL: In real-world trading and in simulations, the realization of profits and losses (PnL) often requires a "settlement" period. Extending the end date beyond your last trade allows the simulation to process any final PnL adjustments, fees, and other end-of-day calculations that might not be fully accounted for on the last trading day.
Documentation and Expectations: This behavior underscores the importance of having clear documentation and setting the right expectations for how backtesting engines like LEAN handle time series data and statistical calculations. It's a good practice to allow a buffer period after your last trade within the simulation to ensure all metrics are calculated correctly.
While not explicitly documented as a requirement, this behavior aligns with the general principle that statistical measures of performance are more meaningful and accurate over longer periods. For short backtests or those with a very narrow time window, it's always a good idea to extend the simulation period slightly beyond the last trading action to ensure all metrics are computed correctly.
This isn't necessarily a bug, but rather a characteristic of how financial statistics are calculated within the framework. However, it's an excellent point that this behavior could be more clearly documented to help users understand how to structure their backtests for accurate results.
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Nick Grobler
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!