Hello! Welcome to your sixth lesson in the "Foundations of Crypto LFT and Data Handling" module.
Introduction
In our last lesson, we explored the concept of stationarity and used formal statistical tests (ADF and KPSS) to analyze our time-series data. We confirmed the common understanding that asset prices are generally non-stationary, which justifies momentum strategies, while their returns are often stationary, making them suitable for statistical modeling. This analysis, however, rested on a crucial assumption: that our data is clean and complete.
Today, we confront the messy reality of real-world financial data. This lesson addresses the final learning outcome of this module: Implement robust methods to detect and handle crypto-specific data anomalies, such as exchange downtime, flash crashes, and missing values.
The principle of "garbage in, garbage out" is critically important in quantitative trading. A flawless strategy will fail if it's fed corrupted or incomplete data. Crypto markets, being 24/7 and less regulated, are particularly prone to data quality issues. We will explore three categories of anomalies, using the infamous Bitcoin flash crash of May 19, 2021, as a running case study to ground our discussion in a real-world event.
To start, let's consider why data quality is so fundamental.
Free Historical Crypto Trade/Tick data from Binance
This video by Chad Thackray opens with a succinct explanation of why data quality is paramount in backtesting, especially for OHLCV data aggregated from multiple sources.
Please watch the first 1 minute and 25 seconds (00:00 - 01:25). The key takeaway is that aggregated data can contain extreme high and low values from low-volume exchanges, distorting the picture of the market and leading to flawed backtest results.
This lesson will equip you with the techniques to identify and rectify such data issues, ensuring the integrity of the data that will underpin your entire trading system.
1. Missing Data: Gaps from Exchange Downtime
The most common data anomaly is missing values. In crypto, this often occurs when an exchange's API goes down during periods of high volatility or scheduled maintenance. Our case study, the May 19 flash crash, provides a perfect example of this.
Bitcoin Flash Crash on May 19, 2021
This research paper, 'Bitcoin Flash Crash on May 19, 2021', documents the event in detail. We'll use it to understand how exchange downtime manifests in the data.
Read the section '4.2 Binance Outage' (page 10). Focus on the description of user complaints, API disruptions, and the initial data gaps reported in both tick-level and minute-level feeds. This illustrates a real-world scenario of missing data during a critical market event.
Detection of Missing Data
For time-series data with a regular frequency (e.g., 1-minute bars), detecting missing data is straightforward. We can check if the time difference between consecutive rows matches the expected frequency.
In pandas, if you have a DatetimeIndex, you can find gaps like this:
import pandas as pd
# Assuming 'df' is your OHLCV data with a DatetimeIndex
# Let's create a sample df with a gap
data = {'close': [100, 101, 105, 106]}
index = pd.to_datetime(['2023-01-01 10:00', '2023-01-01 10:01', '2023-01-01 10:03', '2023-01-01 10:04'])
df = pd.DataFrame(data, index=index)
# Resample to the expected frequency (e.g., '1T' for 1 minute)
# This will create rows for the missing timestamps with 'NaN' values
df_resampled = df.reindex(pd.date_range(start=df.index.min(), end=df.index.max(), freq='1T'))
# Find the missing timestamps
missing_timestamps = df_resampled[df_resampled['close'].isnull()].index
print(f"Original DataFrame:\n{df}\n")
print(f"Resampled DataFrame:\n{df_resampled}\n")
print(f"Detected missing timestamp(s): {missing_timestamps}")
Handling Missing Data (Imputation)
Once we've identified gaps, we must fill them. This process is called imputation. The choice of method is critical, as incorrect imputation can introduce biases.
Handling Missing Value in Time Series Data using Python
This video provides a practical guide to handling missing values in time-series data using Python, covering visualization and several common imputation techniques.
Please watch the following two segments: Visualizing Missing Data (10:47 - 13:00): This part shows how to create a 'missingness plot' to visually identify patterns in missing data. Implementing Imputation (23:06 - 26:20): Focus on the implementation of forward fill (ffill) and backward fill (bfill). Pay attention to the logic and the code used. The video discusses other advanced methods, but for our purposes, ffill and bfill are the most relevant.
In the context of financial backtesting, we must be extremely careful to avoid lookahead bias—using information that would not have been available at that point in time.
Let's evaluate the common methods:
-
Forward Fill (
ffillorpad): Propagates the last valid observation forward.- Logic: Assumes the price remained unchanged during the gap.
- Bias: No lookahead bias. This is the most common and accepted method for backtesting, as it reflects the information that was available right before the gap.
- Use Case: Ideal for filling gaps from exchange downtime.
-
Backward Fill (
bfillorbackfill): FillsNaNs with the next valid observation.- Logic: Uses a future price to fill a past gap.
- Bias: Introduces lookahead bias. Your backtest would be trading on future knowledge.
- Use Case: Should almost never be used in a causal backtesting pipeline.
-
Linear Interpolation: Fills
NaNs with values spaced evenly on a line between the last and next valid observations.- Logic: Assumes a smooth price transition.
- Bias: Introduces lookahead bias as it requires the next valid price.
- Use Case: Useful for data visualization, but not for generating trading signals in a backtest.
For our purposes, ffill is the most robust and defensible choice.
# Continuing from the previous example
print(f"DataFrame with missing values:\n{df_resampled}\n")
# Apply forward fill
df_filled = df_resampled.fillna(method='ffill')
print(f"DataFrame after forward fill:\n{df_filled}")
2. Flash Crashes and Price Outliers
Flash crashes are sudden, severe, and short-lived price drops, often followed by a rapid recovery. They can be caused by fat-finger errors, cascading liquidations, or algorithm malfunctions. These are not missing data, but bad data that can wreak havoc on a trading strategy by:
- Distorting moving averages and other indicators.
- Triggering stop-losses unnecessarily.
- Generating false trading signals.
Detection: Rolling Z-Score
A simple percentage-based filter (e.g., "ignore returns > 20%") is brittle. A more robust, adaptive method is to use a rolling Z-score. The Z-score measures how many standard deviations a data point is from the mean. By using a rolling mean and standard deviation, the threshold adapts to recent volatility.
where and are the mean and standard deviation of the series over a rolling window of size ending at time .
An Integrated Framework for Cryptocurrency Price Forecasting and Anomaly Detection
This research paper proposes a framework for anomaly detection in cryptocurrency prices. It provides a clear, formal description of the rolling Z-score method.
Please read the section '4.2. Preprocessing' (focus on the paragraph describing the Z-Score calculation) and review 'Algorithm 1' on the following page. This will give you a formal understanding of how to calculate the rolling mean, standard deviation, and Z-score to identify abnormal data points.
A common threshold is to flag any data point where as an anomaly.
Here is a Python implementation of this technique:
def detect_outliers_zscore(series, window=30, threshold=3.0):
"""
Detects outliers in a time series using a rolling Z-score.
Args:
series (pd.Series): The time series data (e.g., log returns).
window (int): The size of the rolling window.
threshold (float): The Z-score threshold to classify a point as an outlier.
Returns:
pd.Series: A boolean series, True for outliers.
"""
rolling_mean = series.rolling(window=window).mean()
rolling_std = series.rolling(window=window).std()
z_scores = (series - rolling_mean) / rolling_std
return abs(z_scores) > threshold
# Example with log returns
# Let's inject an outlier into our simulated data
df_simulated = pd.DataFrame({'log_return': np.random.normal(0, 0.01, 100)})
df_simulated.loc[50, 'log_return'] = 0.15 # A 15% return, a clear outlier
outliers = detect_outliers_zscore(df_simulated['log_return'], window=20, threshold=3)
print("Detected outliers:")
print(df_simulated[outliers])
Handling Outliers
Once an outlier is detected, you have several options:
- Remove the Bar: Treat the anomalous OHLCV bar as invalid and remove it. You can then use
ffillon the price series to handle the resulting gap. - Cap the Value: Replace the anomalous value with a less extreme one, for example, the value corresponding to the threshold (e.g., ). This preserves the direction of the move but tames its magnitude.
- Flag and Ignore: Keep the data but flag it. Your signal generation logic can then be programmed to ignore these periods, preventing trades based on anomalous data.
For a backtesting system, removing the bar and using ffill on the price is often the most conservative and easiest-to-implement approach.
3. Advanced Anomaly Detection: Data Integrity
What if the data isn't just missing or spiky, but has been manipulated or incorrectly "repaired" by the exchange? This is a more insidious problem. The May 19 flash crash paper provides a fascinating look at how to investigate this. After the outage, Binance back-filled the missing data, but its authenticity was questionable.
The paper uses Benford's Law, a principle from mathematics, to perform a forensic analysis. The law states that in many naturally occurring sets of numbers, the leading digit is likely to be small. For example, the number 1 appears as the leading digit about 30% of the time, while 9 appears less than 5% of the time. Deviations from this pattern can suggest that data is fabricated.
Bitcoin Flash Crash on May 19, 2021
Let's return to the flash crash paper to see a powerful, non-obvious technique for detecting data manipulation.
Read section '5. Has Binance faked Trading Data?' (pages 11-13). You don't need to replicate the statistical tests, but focus on: The core idea of Benford's Law. How the authors apply it to the transaction volumes of the back-filled data. The visual evidence in Figure 2(a), which shows a clear deviation from the expected distribution. The final conclusion: the back-filled data is statistically different from the reference data, suggesting it may have been manipulated.
The key takeaway here is not that you need to implement Benford's Law in your daily pipeline. Rather, it's about fostering a healthy skepticism of your data sources. When you observe strange phenomena, especially around major market events, it's crucial to investigate and, if necessary, seek out data from alternative, more reliable sources. This analysis highlights that even data from a top-tier exchange is not infallible.
Conclusion
This lesson completes our foundational module on data handling. We've moved from acquiring and storing data to understanding its statistical properties and, finally, to cleaning and validating it.
Key Takeaways:
- Data anomalies are a fact of life in crypto markets. Ignoring them will lead to flawed strategies and unreliable backtest results.
- Missing Data: Can be detected by checking for gaps in timestamps. It should be handled with forward fill (
ffill) to avoid lookahead bias in backtesting. - Flash Crashes/Outliers: Can be detected using a rolling Z-score on returns. They can be handled by removing the anomalous data and using
ffillon the price series. - Data Integrity: Be critical of your data sources. In cases of suspected manipulation, advanced forensic techniques like Benford's Law can be insightful, but the primary action is to validate data against other sources.
You are now prepared to build trading logic on a foundation of clean, robust, and well-understood data.
Preview of the Next Module:
With our data pipeline complete, we are ready to move on to the exciting part: creating trading signals. The next module, "Momentum Signal Engineering," will kick off with the theoretical underpinnings of our strategy. The first lesson will cover the learning outcome: "Derive the mathematical relationship between momentum and the autocorrelation of financial returns." This will connect the statistical properties we've just studied directly to the concept of momentum.