Introduction

The The Transient Impact Model is a mathematical model that is used to estimate the impact of large orders on a market. Pretend you're a big bank and you want to trade a market. You can't take all your size at once, that amount of buying or selling pressure would affect the price to much. That is why Transient Impact Models are used. When the value is lower institutions could have an easier time entering and exiting positions. I am taking a simpler approach to this strategy than in the references. 

Method

Transient Impact Formula Transient Impact Formula

Variables

Definitions:
α: Instantaneous impact coefficient, controls immediate effect of volume
β: Transient impact coefficient, governs decaying impact of past volumes
λ: Decay rate, controls how quickly past impacts diminish
vt: Trade volume at time t
lt: Predicted log return at time t
n: Total number of time steps
i: Summation index over past time steps

Instantaneous Impact

The instantaneous impact is determined by the trade volume of the previous time step:

Instantaneous = α ⋅ v t - 1

Transient Impact

The transient impact is the decayed effect of past trade volumes, calculated as:

Transient = β ⋅ ∑ i 0 ≤ i < t v i ⋅ e - λ ⋅ t - i

Predicted Log Return

The predicted log return is the sum of instantaneous and transient impacts:

l t = α ⋅ v t - 1 + β ⋅ ∑ i 0 ≤ i < t v i ⋅ e - λ ⋅ t - i

Loss Function

The loss function minimizes the squared error between the predicted and actual log returns:

Loss = ∑ t 1 ≤ t < n l t - l t 2

 

I am doing this analysis only on $SPY for the sake of simplicity. With a 252 day lookback period.

ticker = "SPY"
self.eq = self.add_equity(ticker, Resolution.DAILY).Symbol
        
self.lookback = 252
self.data_window = RollingWindow[TradeBar](self.lookback)
self.initial_params = [0.01, 0.01, 0.1]

self.ti_window = RollingWindow[float](3)

 

To set up our data to find log returns we can just use a rolling window.

tb = data.bars.get(self.eq)
self.data_window.add(tb)

dw = list(self.data_window)
volume = [tb.volume for tb in dw]
lgr = [np.log((dw[i-1].close / dw[i].close)) for i in range(1, len(dw))] # Log returns for later reusability

 

We can find the Alpha, Beta, and Lambda parameters by minimizing the function with Scipy.

def transient_impact(self, params, volume, lgr):
        # Get input how we need it
        alpha, beta, lambda_ = params
        volume = np.array(volume)

        # Get the current level of impact
        instantaneous = alpha * volume[:-1]

        # Get the impact we expect to see with exponential decay
        transient = np.array([beta * np.sum(volume[:t] * np.exp(-lambda_ * (t - np.arange(t)))) for t in range(1, len(volume))])

        # Evaluate the transient impact model value
        predicted_change = instantaneous + transient
        return np.sum((lgr - predicted_change) ** 2)

 

Like this

result = minimize(self.transient_impact, self.initial_params, args=(volume, lgr), method='CG')
# alpha, beta, lambda_ = result.x

 

After that we will add it to a rolling window to simply use our trading strategy.

ti = np.log10(self.transient_impact(result.x, volume, lgr) + 0.00000000001) # small value to avoid log(0)
self.ti_window.add(ti)

 

Next we can find out if there is a local minimum or max just by using a line.

ti_min = self.ti_window[2] > self.ti_window[1] and self.ti_window[0] > self.ti_window[1] # Simple local minimum in transient impact
# To make this more sophisticated, could use a longer ti window and check if the 0 index is a saddle point or not

 

Then we can use this to find out if there is a local top or bottom in the most recent returns that syncs with the Transient Impact Model. The reason we want to enter on dips of the Transient Impact Model is because that would be when institutions have the green light to use more size and get their positions on. We are aiming to ride the wave.

long = ti_min and self.data_window[1].close > self.data_window[0].close # Long off recent dips
short = ti_min and self.data_window[1].close < self.data_window[0].close # Short off recent tops

if long: # Enter long
    self.set_holdings(self.eq, 0.50, True)
elif short: # Enter short
    self.set_holdings(self.eq, -0.50, True)

Results

The results are very interesting. The model does well during notable market crashes. Like the Tech Burst in 2000, the Great Financial Crisis in 2008, the Currency Crisis in 2015, in fact it under performs in the 2018 Repo Crisis, 2020 Covid Crash, but improves in the 2022 Bear Market as well as other corrections in 2023. This solidifies the hypothesis that, with a better framework, the Transient Impact model could be useful tool to spot market reversals. This model does not include risk on trades. 

Discussion

A way to progress this model is to use a volatility metric and see if it syncs up with the Transient Impact Model. Also, using other forms of trend identification, as well as different kinds of analysis to find minimums and maximums. Techniques like finding saddle points, topological analysis, and GARCH volatility could be good starts.

Conclusion

To conclude, the market hypothesis is validated and perusing this with better tools involved could be worth while. There are also different kinds of Transient Impact Models with more and different parameters. These models could better model this context. I encourage discussion and people asking questions :).

References

https://arxiv.org/pdf/2205.00494 

https://arxiv.org/pdf/1305.4013 

https://mfe.baruch.cuny.edu/wp-content/uploads/2017/05/Chicago2016OptimalExecution.pdf 

https://www.imperial.ac.uk/media/imperial-college/research-centres-and-groups/cfm-imperial-institute-of-quantitative-finance/events/Lillo-Imperial-Lecture2.pdf