Skip to content
All library documents

Intraday Trend-Day Trading from Afternoon Price Changes

Article Strategy library · Author: QuantRocket

Summary

This U.S. stock strategy takes a directional position when a security has moved far enough from the previous session’s close by 2:00 PM. It buys after a sufficiently large rise and shorts after a sufficiently large decline, then enters shortly after the signal and exits at the close. The implementation uses one-minute data from a leveraged ETF universe, allocates up to a fixed fraction of capital per position, and divides exposure among simultaneous signals.

The source specifies a minimum move threshold, per-share commission, slippage, market entries, and market-on-close exits. It provides implementation detail rather than reported backtest results, so it does not establish profitability. The method depends on the afternoon move persisting through the end of the session; transaction costs, intraday reversals, and the leveraged ETF universe may materially affect outcomes. The document does not specify a stop-loss rule or report evidence across other universes or market conditions.

Key ideas

  • The strategy measures each security’s afternoon price change relative to the prior close.
  • It takes long positions after sufficiently large gains and short positions after sufficiently large declines.
  • Entries occur shortly after the afternoon signal, with positions closed at the session end.
  • Position weights are capped per security and shared across concurrent signals.
  • The source specifies trading costs and order types but presents no performance results.

Tags

Full text
# TrendDayStrategy


# TrendDayStrategy









Intraday strategy that buys (sells) if the security is up (down) more
    than N% from yesterday's close as of 2:00 PM. Enters at 2:01 PM and
    exits the position at the market close.

## Source (Apache-2.0)

```python
# Copyright QuantRocket LLC - All Rights Reserved
#
# 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.

import pandas as pd
from moonshot import Moonshot
from moonshot.commission import PerShareCommission

class USStockCommission(PerShareCommission):
    BROKER_COMMISSION_PER_SHARE = 0.005

class TrendDayStrategy(Moonshot):
    """
    Intraday strategy that buys (sells) if the security is up (down) more
    than N% from yesterday's close as of 2:00 PM. Enters at 2:01 PM and
    exits the position at the market close.
    """

    CODE = 'trend-day'
    DB = 'usstock-1min'
    UNIVERSES = "leveraged-etf"
    DB_TIMES = ['14:00:00', '15:59:00']
    DB_FIELDS = ['Open','Close']
    MIN_PCT_CHANGE = 0.06
    COMMISSION_CLASS = USStockCommission
    SLIPPAGE_BPS = 3

    def prices_to_signals(self, prices: pd.DataFrame):

        closes = prices.loc["Close"]
        opens = prices.loc["Open"]

        # Take a cross section (xs) of prices to get a specific time's price;
        # the close of the 15:59 bar is the session close
        session_closes = closes.xs("15:59:00", level="Time")
        # the open of the 14:00 bar is the 14:00 price
        afternoon_prices = opens.xs("14:00:00", level="Time")

        # calculate the return from yesterday's close to 14:00
        prior_closes = session_closes.shift()
        returns = (afternoon_prices - prior_closes) / prior_closes

        # Go long if up more than N%, go short if down more than -N%
        long_signals = returns > self.MIN_PCT_CHANGE
        short_signals = returns < -self.MIN_PCT_CHANGE

        # Combine long and short signals
        signals = long_signals.astype(int).where(long_signals, -short_signals.astype(int))
        return signals

    def signals_to_target_weights(self, signals: pd.DataFrame, prices: pd.DataFrame):

        # allocate 20% of capital to each position, or equally divide capital
        # among positions, whichever is less
        target_weights = self.allocate_fixed_weights_capped(signals, 0.20, cap=1.0)
        return target_weights

    def target_weights_to_positions(self, target_weights: pd.DataFrame, prices: pd.DataFrame):

        # We enter on the same day as the signals/target_weights
        positions = target_weights.copy()
        return positions

    def positions_to_gross_returns(self, positions: pd.DataFrame, prices: pd.DataFrame):

        closes = prices.loc["Close"]

        # Our signal came at 14:00 and we enter at 14:01 (the close of the 14:00 bar)
        entry_prices = closes.xs("14:00:00", level="Time")
        session_closes = closes.xs("15:59:00", level="Time")

        # Our return is the 14:01-16:00 return, multiplied by the position
        pct_changes = (session_closes - entry_prices) / entry_prices
        gross_returns = pct_changes * positions
        return gross_returns

    def order_stubs_to_orders(self, orders: pd.DataFrame, prices: pd.DataFrame):

        # enter using market orders
        orders["Exchange"] = "SMART"
        orders["OrderType"] = "MKT"
        orders["Tif"] = "Day"

        # exit using MOC orders
        child_orders = self.orders_to_child_orders(orders)
        child_orders["OrderType"] = "MOC"

        orders = pd.concat([orders, child_orders])
        return orders

```

Shown in full with attribution under the source's licence. Licence: Apache-2.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.