How to protect your stock investment from overnight price drops?

--

Are there strategies to shield your investment portfolio from abrupt market shifts, aside from conventional wisdom like sector diversification, and without depending on Machine Learning or AI technologies, which many small investors do not utilize?

Abstract

Data Collection: Aggregated minute-by-minute data from various stocks, with an added time differential of 60 days, 41525K minutes captured simulating about 85 business days. Analysis: The data is divided into three phases — the initial 20,000 minutes show stability, followed by a pronounced upward trend, and finally, a phase with fluctuations around ±50%. Proposed Strategy: To mitigate risk, the entire portfolio is sold at the end of each day and repurchased the following day. Strategy Assumptions: The premise is that major market movements typically don’t happen within the same trading day. Data Preparation: An end-of-day marker is inserted at the conclusion of each trading day for clarity. Also, “Lagged Time Context Shifting” is applied. Outcomes: Implementing this strategy yielded a 15% return on investment over 85 business days, avoiding the 50% loss that would have resulted from a passive, no-action approach.

Outline

1- Introduction.

2.1- Data Acquisition.

2.2- Analysis.

3- Data Processing: End-of-Day Flag.

4- Methodology.

5- Simulation.

6- Conclusion.

1- Introduction

For investors who prioritize minimizing risk over maximizing profits, particularly in the volatile market conditions seen during events like the COVID pandemic, there’s a straightforward strategy to consider. It’s observed that significant market fluctuations often happen overnight, not during regular trading hours, largely in response to companies releasing their financial reports each quarter. If these reports are less favorable than expected, it can lead to sudden drops in stock prices.

To safeguard your investment portfolio against such unforeseen drops, one could adopt a simple strategy: sell off the entire portfolio before the market closes, and repurchase it the next day. While this can be executed manually, automating the process through algorithmic trading platforms like Alpaca API presents a more efficient solution. This method aims to mitigate the risk of overnight market volatility affecting your investments.

Disclaimer: This strategy is presented strictly for educational purposes. It’s essential to acknowledge that there are numerous factors to consider before implementing such a strategy. Market conditions, transaction costs, tax implications, and the potential for missed gains are significant concerns that could impact the overall effectiveness of this approach. Additionally, it’s important to note that I am not a quant or a professional investor. This discussion is intended to spark interest and provoke thought rather than serve as professional investment advice.

2.1- Data Acquisition.

import yfinance as yf
import pandas as pd
import numpy as np
from datetime import timedelta
import matplotlib.pyplot as plt

target_col_name = "Close"

data_acquisition_interval = '1M'
data_acquisition_period = '1d'
data_acquisition_datetime_col_name = "Datetime"

time_variable_name = "time"


def fetch_ticker_data(ticker_symbol, start_dates, end_dates, interval=data_acquisition_interval):
"""Fetch historical data for a given ticker symbol over specified date ranges."""
ticker_data = yf.Ticker(ticker_symbol)
all_dfs = []
for start, end in zip(start_dates, end_dates):
df = ticker_data.history(period=data_acquisition_period, start=start, end=end, interval=interval)
all_dfs.append(df[[target_col_name]])
return pd.concat(all_dfs).reset_index().rename ({"index" :data_acquisition_datetime_col_name},axis =1)

def process_data(data):
"""Process data by setting datetime and sorting."""
data[data_acquisition_datetime_col_name] = pd.to_datetime(data[data_acquisition_datetime_col_name])
data = data.sort_values(data_acquisition_datetime_col_name).reset_index(drop=True)
data[time_variable_name] = data.index
return data

def save_data(data, filename):
"""Save data to a CSV file."""
data.to_csv(filename)

# Usage example
ticker_symbol = 'TSLA'
start_dates = ['2024-01-29', '2024-02-05', '2024-02-12', '2024-02-19' , '2024-02-24']
end_dates = ['2024-02-02', '2024-02-09', '2024-02-19', '2024-02-23' , '2024-03-2']
data = fetch_ticker_data(ticker_symbol, start_dates, end_dates)
print (data)
data = process_data(data)
print (data)
save_data(data, f"{ticker_symbol}.csv")


i =1
for stock in [ticker_symbol,"AMZN" , "AAPL","NVDA","WFRD" ,'BLDR' ,"AGYS" , "DG"]:

i = i + 60 # Add time difference
data_stock = fetch_ticker_data(stock, start_dates, end_dates)
print (data_stock)
data_stock = process_data(data_stock)
print (data_stock)
pdat = process_data (data_stock)
pdat ['Datetime'] = pdat ['Datetime'] + timedelta (days=i)
data = data.append (pdat)

data = data.reset_index (drop=True)
data ["time"] = data.index

data.head ()
Datetime Close time
0 2024-02-12 09:30:00-05:00 192.873306 0
1 2024-02-12 09:31:00-05:00 192.882294 1
2 2024-02-12 09:32:00-05:00 192.070007 2
3 2024-02-12 09:33:00-05:00 191.455002 3
4 2024-02-12 09:34:00-05:00 193.395004 4

data.tail ()
Datetime Close time
41520 2025-06-25 16:55:00-04:00 149.449997 41520
41521 2025-06-25 16:56:00-04:00 149.419998 41521
41522 2025-06-25 16:57:00-04:00 149.309998 41522
41523 2025-06-25 16:58:00-04:00 149.485001 41523
41524 2025-06-25 16:59:00-04:00 149.259995 41524

data.plot.scatter ( "time","Close")
Figure 1: Concatenated Stocks with continuous time on minute-by-minute resolution on the X axis and Close on Y axis.

2.2 Analysis

The dataset is divided into three unique phases: an initial period of 20,000 minutes featuring stable conditions, succeeded by a notable uptrend, and concluding with a phase of high fluctuation, showcasing swings of approximately ±50%. By combining a variety of stocks across different price ranges, we construct a synthetic stock that mirrors the complex behavior of market movements. This construct is utilized to explore the market dynamics our study seeks to understand. The dataset spans 415,250 minutes, approximating 85 business days, with a deliberate 60-day interval inserted between each stock to ensure clear differentiation in the combined dataset.

3- Data Processing: End-of-Day Flag.

A calculation was performed to determine the time difference in hours between each data point and its preceding one. A flag indicating “end of day” was added for time differences greater than 5 hours.

The code sequences include:

  • Assigning the previous timestamp and close price to new columns Date_lag and close_lag, respectively, by shifting the Datetime and Close columns down by one.
  • Calculating the time delta Date_delta between the current and lagged timestamps.
  • Extracting days and seconds from Date_delta to calculate Date_delta_days and Date_delta_seconds.
  • Converting Date_delta_seconds into minutes (Date_delta_minutes) and hours (Date_delta_hours).
  • Applying a condition to mark the end of the day end_of_day if the Date_delta_hours is 5 or more.
data ["Date_lag"] = data ['Datetime'].shift (1)
data ["close_lag"] = data ['Close'].shift (1)

data ["Date_delta"] = data ["Datetime"] - data ["Date_lag"]
data ["Date_delta_days"] = data ["Date_delta"].dt.days
data ["Date_delta_seconds"] = data ["Date_delta"].dt.seconds
data ["Date_delta_minutes"] = data ["Date_delta_seconds"] / 60
data ["Date_delta_hours"] = data ["Date_delta_minutes"] / 60
data ["end_of_day"] = data ["Date_delta_hours"] >= 5

data

Datetime Close time Date_lag close_lag Date_delta Date_delta_days Date_delta_seconds Date_delta_minutes Date_delta_hours end_of_day
0 2024-02-12 09:30:00-05:00 192.873306 0 NaT NaN NaT NaN NaN NaN NaN False
1 2024-02-12 09:31:00-05:00 192.882294 1 2024-02-12 09:30:00-05:00 192.873306 0 days 00:01:00 0.0 60.0 1.0 0.016667 False
2 2024-02-12 09:32:00-05:00 192.070007 2 2024-02-12 09:31:00-05:00 192.882294 0 days 00:01:00 0.0 60.0 1.0 0.016667 False
3 2024-02-12 09:33:00-05:00 191.455002 3 2024-02-12 09:32:00-05:00 192.070007 0 days 00:01:00 0.0 60.0 1.0 0.016667 False
4 2024-02-12 09:34:00-05:00 193.395004 4 2024-02-12 09:33:00-05:00 191.455002 0 days 00:01:00 0.0 60.0 1.0 0.016667 False
... ... ... ... ... ... ... ... ... ... ... ...
41520 2025-06-25 16:55:00-04:00 149.449997 41520 2025-06-25 16:54:00-04:00 149.405701 0 days 00:01:00 0.0 60.0 1.0 0.016667 False
41521 2025-06-25 16:56:00-04:00 149.419998 41521 2025-06-25 16:55:00-04:00 149.449997 0 days 00:01:00 0.0 60.0 1.0 0.016667 False
41522 2025-06-25 16:57:00-04:00 149.309998 41522 2025-06-25 16:56:00-04:00 149.419998 0 days 00:01:00 0.0 60.0 1.0 0.016667 False
41523 2025-06-25 16:58:00-04:00 149.485001 41523 2025-06-25 16:57:00-04:00 149.309998 0 days 00:01:00 0.0 60.0 1.0 0.016667 False
41524 2025-06-25 16:59:00-04:00 149.259995 41524 2025-06-25 16:58:00-04:00 149.485001 0 days 00:01:00 0.0 60.0 1.0 0.016667 False
41525 rows × 11 columns

The close_lag column, wherever the end-of-day flag is set to True, the corresponding Close value occurs one to several business days later. This indicates that, although the initial data collection and typical conventions suggest that the Close value is tied to the current time context, it is actually close_lag that represents the present time context. Meanwhile, the Close value points to a future moment, spanning from the next day to several days ahead. This approach essentially redefines the temporal relationship between data points by using lagged values to represent the present context, thereby adjusting the temporal analysis framework to account for the actual operational or decision-making timeline.

4- Methodology

Sell the entire portfolio when the end-of-day flag is set to True. Conversely, when the flag is False, reinvest all capital into the portfolio at the close_lag price and maintain this position until the end-of-day flag turns True once more.

def auto_trade_strategy(df, starting_capital=10000):
# Set up initial investment details
capital = starting_capital
shares_held = 0

# Lists to track capital, shares, and portfolio value over time
capital_tracking = []
shares_tracking = []
portfolio_value_tracking = []

for index, row in df.iterrows():
end_of_day = row["end_of_day"]

if end_of_day and shares_held > 0:
# Sell all shares and update capital
capital += shares_held * row['close_lag']
shares_held = 0
elif not end_of_day and capital > 0:
# Allocate all capital to buy shares
shares_held += capital / row['close_lag']
capital = 0

# Record the current state
shares_tracking.append(shares_held)
capital_tracking.append(capital)
portfolio_value = capital + shares_held * row['close_lag']
portfolio_value_tracking.append(portfolio_value)

return capital_tracking, shares_tracking, portfolio_value_tracking

This function automates trading based on the end-of-day flag. It switches between holding cash and shares to navigate market fluctuations, aiming to capitalize on price movements reflected by the close_lag.

5- Simulation

Simulating backtesting with the proposed Methodology starting at an initial capital of $10K and 0 Shares.

values_list_out  = automatic_trading(data.dropna(), initial_capital=10000)  [-1]
shares_list_out = automatic_trading(data.dropna(), initial_capital=10000) [-2]
capital_list_out = automatic_trading(data.dropna(), initial_capital=10000) [-3]

data ["value"] = np.NaN
data ["shares"] = np.NaN
data ["capital"] = np.NaN
data ["value"].iloc [1:] = values_list_out
data ["shares"].iloc [1:] = shares_list_out
data ["capital"].iloc [1:] = capital_list_out

data
Datetime Close time Date_lag close_lag Date_delta Date_delta_days Date_delta_seconds Date_delta_minutes Date_delta_hours end_of_day value shares capital
0 2024-02-12 09:30:00-05:00 192.873306 0 NaT NaN NaT NaN NaN NaN NaN False NaN NaN NaN
1 2024-02-12 09:31:00-05:00 192.882294 1 2024-02-12 09:30:00-05:00 192.873306 0 days 00:01:00 0.0 60.0 1.0 0.016667 False 10000.000000 51.847506 0.0
2 2024-02-12 09:32:00-05:00 192.070007 2 2024-02-12 09:31:00-05:00 192.882294 0 days 00:01:00 0.0 60.0 1.0 0.016667 False 10000.465976 51.847506 0.0
3 2024-02-12 09:33:00-05:00 191.455002 3 2024-02-12 09:32:00-05:00 192.070007 0 days 00:01:00 0.0 60.0 1.0 0.016667 False 9958.350952 51.847506 0.0
4 2024-02-12 09:34:00-05:00 193.395004 4 2024-02-12 09:33:00-05:00 191.455002 0 days 00:01:00 0.0 60.0 1.0 0.016667 False 9926.464451 51.847506 0.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
41520 2025-06-25 16:55:00-04:00 149.449997 41520 2025-06-25 16:54:00-04:00 149.405701 0 days 00:01:00 0.0 60.0 1.0 0.016667 False 14367.165745 96.162099 0.0
41521 2025-06-25 16:56:00-04:00 149.419998 41521 2025-06-25 16:55:00-04:00 149.449997 0 days 00:01:00 0.0 60.0 1.0 0.016667 False 14371.425367 96.162099 0.0
41522 2025-06-25 16:57:00-04:00 149.309998 41522 2025-06-25 16:56:00-04:00 149.419998 0 days 00:01:00 0.0 60.0 1.0 0.016667 False 14368.540621 96.162099 0.0
41523 2025-06-25 16:58:00-04:00 149.485001 41523 2025-06-25 16:57:00-04:00 149.309998 0 days 00:01:00 0.0 60.0 1.0 0.016667 False 14357.962732 96.162099 0.0
41524 2025-06-25 16:59:00-04:00 149.259995 41524 2025-06-25 16:58:00-04:00 149.485001 0 days 00:01:00 0.0 60.0 1.0 0.016667 False 14374.791392 96.162099 0.0
41525 rows × 14 columns
Figure 2 Time index on X-axis : Minute by Minute and Shares on Y-axis bought at value close_lag with all capital.
Figure 3 Y axis : Capital Value ($) , X-axis (Minutes)
Figure 4 : X-axis is minutes and Y-axis is the value of portfolio ($).

6- Conclusion

The strategy deployed utilized the concept of compound reinvestment, with shares being sold at the end of each day and then repurchased using the initial investment amount along with any earned gains. This method led to a profit of around $15,000 over a span of 85 business days, as shown in Figure 4. Conversely, without any intervention, the portfolio would have halved in value, decreasing from $200 initially to $100 by the stock’s end, assuming no trading activity occurred. However, had the stock’s final value remained at approximately $200, similar to the initial value depicted in Figure 1, the outcome would have been different. It’s important to note from Figure 1 that there were intra-day market disturbances after the 30,000-minute mark, leading to the stock’s end, which this strategy couldn’t address, resulting in the depicted fluctuations in Figure 4. Nevertheless, the strategy generally maintained controlled fluctuations, and due to its predictability, it’s possible to forecast the overall moving average of the portfolio value day by day, allowing for further strategic compound reinvestments. Overall, while the strategy’s 14–15% return over what equates to approximately 4 months may not seem exceedingly lucrative, it represents a relatively safe and predictable approach, offering potential for enhanced profitability through additional strategic applications.

--

--

Emad Ezzeldin ,Sr. DataScientist@UnitedHealthGroup

5 years Data Scientist and a MSc from George Mason University in Data Analytics. I enjoy experimenting with Data Science tools. emad.ezzeldin4@gmail.com