By Alfredo Baraldi – July 1, 2025

Does the Square-Root Price Impact Law Belong to Strict Universal Scalings? Quantitative Support from a Comprehensive Study of the Tokyo Stock Exchange

Yuki Sato and Kiyoshi Kanazawa Department of Physics, Graduate School of Science, Kyoto University, Kyoto 606-8502, Japan (Date: November 22, 2024)


What is meant by: “Does the square-root price impact law belong to the strict universal scalings?”

The question, “Does the square-root price impact law belong to the strict universal scalings?” refers to a central debate in econophysics and financial market microstructure: “Does the price impact law truly follow a universal rule, valid for all markets and financial instruments?”

The square-root price impact law states that the average price impact of a large-volume trade grows in proportion to the square root of the traded volume. In formula: I(Q) ∝ Q^δ ; where I(Q) is the price impact and Q is the order volume, with δ ≈ 0.5.

The term strict universal scalings implies that this law should hold precisely and identically for all stocks, markets, and strategies, without exception. Some scholars have argued that the exponent delta may vary from market to market, while others believe it must be exactly 0.5 everywhere.

A recent study addressed this issue with exceptionally detailed data: a comprehensive analysis of eight years of transactions on all liquid stocks on the Tokyo Stock Exchange, including every single trading account. The results? The exponent delta was found to be very close to 0.5, both at the individual stock and individual trader level, with minimal statistical error margins.

In summary: yes, according to this research, the square-root price impact law truly belongs to the universal scaling laws, at least in the context of the Japanese market. This finding could have significant implications for risk management and the execution strategies of institutional investors.


What studies support this law in American and European markets?

The square-root price impact law has been the subject of numerous empirical studies in both American and European markets, and the results are surprisingly consistent.

Studies on American Markets

  • Grinold & Kahn (1999): Pioneers in using the square-root law in market impact models, with practical applications in quantitative trading systems.
  • Almgren et al. (2005): Empirically confirmed that the average impact grows as Q0.5 for realistically sized orders.
  • Moro et al. (2009): Analyzed millions of orders on U.S. stocks, finding a clear sublinear relationship between volume and impact.
  • Gatheral (2010s): Proposed theoretical models compatible with the law, such as the propagator model and the locally linear order book model.

Studies on European Markets

  • Bouchaud et al. (2011): Showed that impact in European markets also follows a square-root law, with data from exchanges like Euronext and Deutsche Börse.
  • Lillo et al. (2003–2016): Conducted in-depth studies on meta-orders in Europe, confirming the law’s validity even for split and algorithmic orders.
  • Ancerno Dataset (2007–2009): Used to analyze millions of meta-orders on European and American stocks, it showed the law holds in both contexts.

These studies not only confirm the law empirically but also propose theoretical explanations based on market equilibrium, microstructure, and liquidity dynamics.


Can you provide examples of the law’s practical applications in stock markets?

The square-root impact law has very concrete applications in financial markets, especially for those managing large orders. Here are some practical examples of its use:

1. Optimizing Algorithmic Trading Strategies

TWAP (Time Weighted Average Price) or VWAP (Volume Weighted Average Price) strategies use the law to decide how much and when to break up a large order. Knowing that the impact grows sublinearly ( ∝  Q ^ 0.5  ) helps minimize execution costs.

2. Risk Management in Institutional Funds

Portfolio managers use it to estimate the implicit cost of liquidity. For instance, if a fund wants to sell a €100 million position, it can pre-estimate the price impact to decide whether it’s better to sell immediately, in tranches, or use a specialized broker.

3. Calibrating Market Impact Models on Execution Desks

Trading desks use statistical models that include the square-root law to simulate impact scenarios. These models help negotiate commissions with brokers, plan complex sales, or evaluate pre-trade strategies.

4. Pricing of Over-the-Counter (OTC) Transactions

For illiquid instruments or derivatives, the expected impact of liquidating the position is estimated. The law provides a guide for adding liquidity premiums to the price.

“Pricing of over-the-counter (OTC) transactions” in this context means the square-root price impact law is used to determine the price of financial instruments that are not traded on a regulated exchange, but directly between two parties (OTC).

Specifically:

  • Illiquid instruments or derivatives: These are assets or financial contracts that do not have an active and transparent market (like stocks traded on an exchange). Buying and selling them is more complex, and the price impact of a trade can be significant.
  • Estimating the expected impact if the position were to be liquidated: When you own one of these instruments and want to sell it (liquidate the position), it’s hard to know in advance what price you’ll get due to poor liquidity. The square-root price impact law helps estimate how much the price might move in response to the sale.
  • Adding liquidity premiums to the price: Since these instruments are less liquid and a sale can negatively impact the price, whoever sells them (or buys them from someone who needs to liquidate) will ask for or offer a price that incorporates a “liquidity premium.” This premium is an additional cost (or discount) to compensate for the risk and the price impact that could be generated. The law provides a quantitative basis for calculating the amount of this premium, making the price fairer and more predictable even for these non-standard instruments.

5. Strategy in Robo-Advisory or Portfolio Rebalancing Systems

Even automated portfolio rebalancing requires accurate impact estimates to avoid price distortions during large-scale adjustments.


Can you provide a purchase example?

Let’s walk through a practical numerical example of a purchase using the square-root price impact law—this way, we can understand how much a large order might drive up the price.

Scenario: Purchasing Stellantis Shares

  • Current Average Price: €18
  • Average Daily Volume (ADV): 2,000,000 shares
  • Quantity to Purchase: 400,000 shares
  • Fraction of ADV:
  • Empirical Impact Coefficient (Y): 0.012 (hypothetical value for liquid European stocks)
  • Exponent: δ=0.5

Let’s apply the formula:

  • Expected Impact on Average Price: +0.54%

Translated into price: New Estimated Price

So, the purchase could push the price to around €18.10, due to the upward pressure generated by the demand. This type of estimate is crucial for an institutional buyer who needs to decide how to buy (all at once? in tranches? with algorithms?) to avoid moving the market too much against them.


How does the search for alpha interact with the price impact law?

This is a very insightful point: how the search for alpha interacts with the price impact law and, more broadly, with market price formation. Let’s connect the pieces from a strategic perspective.

1. Alpha as a Generator of Informed Demand

The calculation of alpha (i.e., expected return in excess of the market) drives traders to buy or sell securities they believe are undervalued or overvalued. This generates informed demand, which is different from passive liquidity.

The statement “This generates informed demand, different from passive liquidity” means that the activity of traders seeking “alpha” (an excess return over the market) creates a specific type of demand in financial markets.

Let’s break down the terms:

  • Informed Demand: Refers to buy or sell orders placed on the market based on information or analysis that the trader believes will give them an edge. If a trader thinks a stock is undervalued, they buy it because they have “information” (or an analysis) suggesting a future price increase. This demand is not random; it’s the result of a strategic decision based on an expectation of profit.
  • Passive Liquidity: Refers to orders (typically “limit orders,” which are orders with a price limit) that are placed in the order book (the list of all buy and sell offers at various prices) and wait to be executed. These orders are there to provide liquidity to the market, allowing others to buy or sell quickly. Those who place them are not necessarily actively seeking alpha but are rather providing the opportunity for trades to occur, often with an incentive (like a small discount on commissions).

The sentence highlights that alpha is not obtained simply by exploiting existing liquidity (which would be the opposite action, “taking” liquidity), but rather by introducing new buying or selling pressure on the market. This pressure, stemming from alpha-driven decisions, actively changes prices, leading to what is called “price impact.”

If many traders identify the same alpha, the aggregate demand pushes the price of the security until the alpha is absorbed: this is the market pressure that fuels price impact. The impact often follows the square-root law, but the square root of what? Precisely the volume derived from alpha-seeking strategies!

2. Feedback Effect: Impact and Alpha Decay

When a trade is executed, the price moves. But if the price movement reduces the expected alpha… then:

  • Impact is an implicit cost that “eats away” at the alpha.
  • This is especially true for large orders or in illiquid markets.
  • Therefore, the ex-ante calculation of impact (via the Q elevate 0.5 law) is used to decide if the alpha is even worth trading.

3. Quantitative Models: Alpha Conditional on Impact

In practice, many modern quantitative models don’t just look for “gross” alpha, but net alpha, which is what remains after the expected impact:

This leads to more sophisticated execution strategies, such as:

  • Rebalancing conditional on impact risk.
  • “What-if” simulations to determine the optimal execution time.
  • Limiting order size to maximize residual alpha.

We could even say that the impact law is the natural filter that separates achievable alpha opportunities from illusory ones.


Can you show a quantitative example where we estimate net alpha based on volume and impact?

I’ll show how price impact can reduce net alpha, using a practical, hedge-fund-style example.

Scenario A quant manager identifies an expected alpha of +1.2% on a stock, based on fundamental and technical signals.

  • Wants to buy 300,000 shares.
  • Average Daily Volume (ADV): 1,000,000 shares.
  • Average Price: €20.
  • Empirical Impact Coefficient (Y): 0.015.
  • Exponent (square root):

In the context of the square-root price impact law, “Exponent: delta=0.5 (square root)” means that the relationship between price impact (I) and traded volume (Q) is defined by a power of 0.5.

In simple terms:

  • delta (delta) is the exponent in the formula describing how price impact changes as volume changes.
  • 0.5 indicates that the impact is not linear but grows proportionally to the square root of the volume.

This means that if an order’s volume doubles, the price impact does not double but increases by a factor of sqrt2 – √2 (about 1.414). If the volume quadruples, the impact doubles (because  √4 = 2). This “sublinear” relationship is a key feature of the square-root price impact law.

Impact Estimation :

  • The estimated impact is +0.82%.

Net Alpha =Gross Alpha−Impact=1.2%−0.82%=0.38%

Interpretation The strategy still has value (positive net alpha), but it’s much thinner than it initially appeared. In some cases, it might not justify the operational or market risk. And if we increased the quantity to 500,000 shares? The impact would grow, and the net alpha could even become negative.


Can you calculate the maximum purchasable volume for the net alpha to remain positive?

Let’s start with the data from the previous example:

  • Gross Alpha: α=1.2%
  • ADV (Average Daily Volume): 1,000,000 shares
  • Impact Coefficient: Y=0.015
  • Exponent: : δ=0.5

We want to find the volume Q such that:

Solving for Q:

If the volume were to increase beyond this amount, the impact cost would exceed the alpha, turning a good insight into an unprofitable trade.


Can you turn this calculation into a simple, reusable formula to estimate the operational limits of any alpha signal?

Here is a generic formula you can use to calculate the maximum purchasable (or sellable) volume so that the net alpha remains positive:

Maximum Alpha-Compatible Volume Formula

Where:

  • Qmax: maximum tradable volume
  • alpha : gross alpha (expressed in decimals, e.g., 1.2% = 0.012)
  • Y: empirical impact coefficient
  • ADV: average daily volume

Quick Example (with the previous data):

  • alpha=0.012
  • Y=0.015
  • ADV = 1,000,000

Exactly as calculated before! Now you can easily apply it to any investment strategy or trading signal to assess how much capital to risk without compromising the expected net value.


Can we turn this formula into an Excel version and a Python script for automation?

Here are both the Excel version and the Python script to calculate the maximum tradable volume for a given alpha, following the square-root impact law.

Excel Version

You can create a sheet with these columns:

ParameterValueCell
Gross Alpha (delta \ alpha)0.012C1
Impact Coeff. (Y)0.015C2
ADV (Daily Volume)1,000,000C3
Q_maxC4

Esporta in Fogli

In cell C4, enter the formula: =C3*(C1/C2)^2

The cell with Q_max will return the maximum purchasable or sellable volume.

Python Version

Python

def calculate_max_volume(alpha, Y, ADV):
  """
  Calculates the maximum tradable volume given a certain alpha, 
  impact coefficient, and ADV.
  """
  max_q = ADV * (alpha / Y)**2
  return max_q

# Example:
alpha = 0.012    # 1.2%
Y = 0.015
ADV = 1_000_000

max_q = calculate_max_volume(alpha, Y, ADV)
print(f"Maximum tradable volume: {int(max_q):,} shares")

Output:

Maximum tradable volume: 640,000 shares

Can we expand the script to compare different alpha signals and integrate it into a backtesting app?

Integrating this formula into a backtesting app allows you not only to evaluate the effectiveness of a trading signal but also to verify if it’s actually tradable without canceling out the alpha.

Here is a modular implementation idea:

1. Max Volume Calculation Function (already done)

Python

def max_tradable_volume(alpha, impact_coefficient, ADV):
    return ADV * (alpha / impact_coefficient)**2

2. Backtest with Impact Control

Suppose you have a list of signals, each with a date, ticker, expected alpha, suggested quantity, ADV, etc.

Python

import pandas as pd

# Example DataFrame of signals
signals = pd.DataFrame({
    'date': ['2025-07-01', '2025-07-01'],
    'ticker': ['ENEL.MI', 'STM.MI'],
    'alpha': [0.012, 0.02],
    'impact_coefficient': [0.015, 0.02],
    'ADV': [1_000_000, 800_000],
    'requested_quantity': [400_000, 300_000]
})

# Adds a column with the maximum allowed volume
signals['Q_max'] = signals.apply(
    lambda row: max_tradable_volume(row['alpha'], row['impact_coefficient'], row['ADV']),
    axis=1
)

# Flag: excessive or allowed order
signals['order_allowed'] = signals['requested_quantity'] <= signals['Q_max']

3. Integrated Analysis in the Backtest

During the simulation, you can block (or reduce) orders that violate the limit, or apply a dynamic alpha haircut, like this:

Python

signals['net_alpha'] = signals.apply(
    lambda row: row['alpha'] - row['impact_coefficient'] * (row['requested_quantity'] / row['ADV'])**0.5,
    axis=1
)

And then test the performance using only signals with a positive net alpha.


Can you show me how to build a module with functions to generate charts, statistics, or export validated signals?

Here is an example of a backtesting framework with market impact, designed for realistic alpha signals and volumes conditioned by the square-root impact law. I’ll provide a modular base in Python that’s easy to extend.

Framework Structure

  1. Signal Input: A CSV file or DataFrame with data like ticker, date, alpha, ADV, impact coefficient Y, and suggested quantity.
  2. Impact Module: Calculates the maximum compatible volume and the net alpha.
  3. Backtest Engine: Simulates execution only if the trade is feasible.
  4. Reporting: Displays performance, distribution of allowed/rejected orders, and charts.

Simplified Base Code

Python

import pandas as pd

def get_max_tradable_volume(alpha, Y, ADV):
  """Calculates the maximum tradable volume."""
  return ADV * (alpha / Y)**2

def calculate_net_alpha(row):
  """Calculates the net alpha after market impact."""
  impact = row['Y'] * (row['requested_quantity'] / row['ADV'])**0.5
  return row['alpha'] - impact

def process_signals(df):
  """Processes the trading signals to determine execution."""
  df['max_q'] = df.apply(lambda row: get_max_tradable_volume(row['alpha'], row['Y'], row['ADV']), axis=1)
  df['net_alpha'] = df.apply(calculate_net_alpha, axis=1)
  df['order_allowed'] = df['requested_quantity'] <= df['max_q']
  df['executed'] = df['order_allowed'] & (df['net_alpha'] > 0)
  return df

# Example: load signals
# signals = pd.read_csv("signals.csv") # or create DataFrame manually
# results = process_signals(signals)

# Basic output
# print(results[['ticker', 'alpha', 'max_q', 'net_alpha', 'executed']])

Additional (Modular) Analysis

  • Charts of Gross Alpha vs. Net Alpha.
  • Percentage of allowed orders.
  • Aggregate portfolio performance weighted only on “executed” trades.

If you like, I can add a function that creates an automatic PDF report, generates charts with Matplotlib, or simulates a cumulative portfolio against a benchmark.


Now let’s add visualization and export tools to our backtesting framework so you can get a clear picture of performance and save the results of allowed signals.

1. Visualization with Matplotlib

For example, a chart comparing Gross Alpha vs. Net Alpha:

Python

import matplotlib.pyplot as plt

def plot_alpha_comparison(df):
  """
  Creates a scatter plot of Gross Alpha vs. Net Alpha.
  """
  plt.figure(figsize=(8,6))
  plt.scatter(df['alpha'], df['net_alpha'], c=df['executed'].map({True:'green', False:'red'}), alpha=0.7)
  plt.plot([0, df['alpha'].max()], [0, df['alpha'].max()], '--', color='gray', label='Breakeven Line')
  plt.title('Gross Alpha vs. Net Alpha')
  plt.xlabel('Gross Alpha')
  plt.ylabel('Net Alpha')
  plt.legend()
  plt.grid(True)
  plt.show()

# You can call plot_alpha_comparison(results) after processing the signals

2. Exporting Allowed Signals

To save only signals with a positive net alpha and an allowed order size to a CSV:

Python

def export_valid_signals(df, filename='valid_signals.csv'):
  """
  Filters for valid signals and exports them to a CSV file.
  """
  valid_signals = df[df['executed']]
  valid_signals.to_csv(filename, index=False)
  print(f"{len(valid_signals)} valid signals exported to {filename}")

3. Synthetic Performance Report

Prints a basic summary:

Python

def report_statistics(df):
  """
  Calculates and prints key statistics from the signals DataFrame.
  """
  total_signals = len(df)
  executed_signals = df['executed'].sum()
  pct_executed = (executed_signals / total_signals * 100) if total_signals > 0 else 0
  avg_net_alpha = df[df['executed']]['net_alpha'].mean()

  print(f"--- Backtest Summary ---")
  print(f"Total signals processed: {total_signals}")
  print(f"Allowed and executed signals: {executed_signals} ({pct_executed:.1f}%)")
  if executed_signals > 0:
    print(f"Average net alpha (executed only): {avg_net_alpha:.4f} or {avg_net_alpha:.2%}")


Can you package everything into a Jupyter Notebook or a single .py file, ready to be reused or shared? Can you also add functions to calculate cumulative PnL to track an equity curve?

I will now create both formats for you: a Jupyter Notebook and a reusable .py file for your mini-framework for backtesting with market impact.

1. Jupyter Notebook (Backtest_Impact.ipynb)

A notebook is ideal for:

  • Interactively testing signals.
  • Visualizing charts in real-time.
  • Easily modifying parameters.

I recommend structuring it this way:

  1. Import Libraries and Data: Load from CSV or a dummy dataset.
  2. Calculate Q_max and Net Alpha: Modular functions for max volume and order validation.
  3. Analysis and Charts: Scatter plot of Gross vs. Net Alpha, distribution of allowed orders, and a cumulative PnL curve.
  4. Export Valid Signals.

2. Python File (backtest_impact.py)

This Python module is perfect to:

  • Import into other projects.
  • Automate analyses.
  • Integrate into pipelines or APIs.

I can add Matplotlib integration, performance logging, and saving to Excel with pandas.ExcelWriter. Here are the files, ready to be used.


Project Structure and Content

I suggest creating a folder named impact_backtester containing:

  • Backtest_Impact.ipynb — The interactive notebook.
  • backtest_impact.py — The reusable Python module.
  • signals.csv — An example file with simulated data.

Content of backtest_impact.py

Python

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def get_max_tradable_volume(alpha, Y, ADV):
    """Calculates the maximum tradable volume for a signal."""
    if Y <= 0: return float('inf')
    return ADV * (alpha / Y)**2

def calculate_net_alpha(row):
    """Calculates the net alpha after estimated market impact."""
    impact = row['Y'] * (row['requested_quantity'] / row['ADV'])**0.5
    return row['alpha'] - impact

def process_signals(df):
    """Processes raw signals to determine which are tradable."""
    df['max_q'] = df.apply(lambda row: get_max_tradable_volume(row['alpha'], row['Y'], row['ADV']), axis=1)
    df['net_alpha'] = df.apply(calculate_net_alpha, axis=1)
    df['order_allowed'] = df['requested_quantity'] <= df['max_q']
    df['executed'] = df['order_allowed'] & (df['net_alpha'] > 0)
    return df

def export_valid_signals(df, filename='valid_signals.csv'):
    """Exports all executed signals to a CSV file."""
    valid_signals = df[df['executed']]
    valid_signals.to_csv(filename, index=False)
    print(f"{len(valid_signals)} valid signals exported to {filename}")




Here is an example of a CSV file containing simulated trading signals, already compatible with the framework we've built:
📄 Content of signal.csv
   

data,ticker,alfa,Y,ADV,requested_quantity

2025-07-01,ENEL.MI,0.012,0.015,1000000,400000

2025-07-01,STM.MI,0.020,0.018,800000,300000

2025-07-01,ENI.MI,0.009,0.012,1200000,350000

2025-07-01,ISP.MI,0.015,0.020,900000,500000

2025-07-01,LUX.MI,0.010,0.017,950000,200000

📥 How to save it:

  • Open a text editor or IDE (e.g., VS Code, Notepad++).
  • Paste the content above.
  • Save the file as signal.csv in the same folder as your notebook or Python module.

I can add additional fields such as volatility, average price, or signal classification (momentum, value, etc.). Alternatively, I can generate a much larger CSV with 50+ dummy signals. Let me know how you’d like to expand this dataset!

I’m adding extra fields to your signal.csv file to enrich your backtesting analysis. Here’s what the new dataset could look like:

📄 New content of signal.csv

date,ticker,alpha,Y,ADV,requested_quantity,average_price,volatility,signal_class
2025-07-01,ENEL.MI,0.012,0.015,1000000,400000,5.40,0.21,value
2025-07-01,STM.MI,0.020,0.018,800000,300000,43.20,0.33,momentum
2025-07-01,ENI.MI,0.009,0.012,1200000,350000,14.80,0.18,mean-reversion
2025-07-01,ISP.MI,0.015,0.020,900000,500000,2.45,0.27,macro
2025-07-01,LUX.MI,0.010,0.017,950000,200000,89.10,0.25,sector-rotation

🔍 Description of the new fields

  • average_price: reference price used to estimate the impact in euros
  • volatility: daily or weekly estimate of the stock’s volatility (e.g., standard deviation of returns)
  • class_signal: type of signal that generated the alpha (e.g., value, momentum, macroeconomic, etc.)

With these additional fields, you can:

  • Estimate the absolute monetary cost of the impact (€)
  • Analyze performance by strategy type
  • Filter or weight signals based on volatility

Now, let’s enhance the framework to:

Calculate a signal efficiency index as alpha per unit of risk:

Compare performance by signal class, for example: momentum, value, macro.

🧠 Extension: Efficiency and Performance by Signal Class

Let’s add these steps to our code:

📌 1. Signal Efficiency Calculation

def add_efficiency(df):
  """
  Calculates and adds an efficiency ratio column to the DataFrame.
  """
  # Avoid division by zero or null values
  df['efficiency'] = df.apply(lambda row: row['net_alpha'] / row['volatility']
                                      if row['volatility'] > 0 else None, axis=1)
  return df

📊 2. Comparison by class_signal

def summarize_by_class(df):
  """
  Groups data by signal class and calculates performance metrics.
  """
  # Consider only successfully executed signals
  executed_df = df[df['executed']].copy()

  class_summary = executed_df.groupby('signal_class').agg({
      'net_alpha': 'mean',
      'efficiency': 'mean',
      'ticker': 'count'
  }).rename(columns={'ticker': 'signal_count'})

  class_summary = class_summary.sort_values(by='efficiency', ascending=False)

  print("📈 Performance by signal class:")
  return class_summary

3. Optional Chart: Efficiency by Class

import matplotlib.pyplot as plt

def plot_efficiency_by_class(df):
  """
  Creates a bar chart showing the average efficiency for each signal class.
  """
  # Assumes a function 'summarize_by_class' exists
  class_summary = summarize_by_class(df)

  class_summary['efficiency'].plot(kind='barh', color='skyblue')
  plt.xlabel("Net Alpha / Volatility")
  plt.title("Average Efficiency by Signal Class")
  plt.grid(True)
  plt.tight_layout()
  plt.show()

Now I’ll guide you through the creation of both the updated .py module and the expanded Jupyter Notebook, integrating the following:

  • Calculation of signal efficiency (net_alpha / volatility)
  • Analysis by signal class
  • Comparative chart across classes

🐍 backtest_impact.py — Script updated

import pandas as pd

def get_max_tradable_volume(alpha, Y, ADV):
  """Calculates the maximum tradable volume for a signal."""
  return ADV * (alpha / Y)**2

def calculate_net_alpha(row):
  """Calculates the net alpha after estimated market impact."""
  impact = row['Y'] * (row['requested_quantity'] / row['ADV'])**0.5
  return row['alpha'] - impact

def process_signals(df):
  """Processes the raw signals to determine which are tradable."""
  df['max_q'] = df.apply(
      lambda row: get_max_tradable_volume(row['alpha'], row['Y'], row['ADV']), axis=1
  )
  df['net_alpha'] = df.apply(calculate_net_alpha, axis=1)
  df['order_allowed'] = df['requested_quantity'] <= df['max_q']
  df['executed'] = df['order_allowed'] & (df['net_alpha'] > 0)
  return df

def add_efficiency_ratio(df):
  """Adds an efficiency ratio (net alpha / volatility) to the DataFrame."""
  df['efficiency'] = df.apply(
      lambda row: row['net_alpha'] / row['volatility'] if row['volatility'] > 0 else None,
      axis=1
  )
  return df

def summarize_by_class(df):
  """Generates a summary of performance metrics grouped by signal class."""
  executed_df = df[df['executed']]
  class_summary = executed_df.groupby('signal_class').agg({
      'net_alpha': 'mean',
      'efficiency': 'mean',
      'ticker': 'count'
  }).rename(columns={'ticker': 'signal_count'})
  return class_summary.sort_values(by='efficiency', ascending=False)

def export_valid_signals(df, filename='valid_signals.csv'):
  """Exports all executed signals to a CSV file."""
  valid_signals = df[df['executed']]
  valid_signals.to_csv(filename, index=False)
  print(f"{len(valid_signals)} signals exported to {filename}")

📓 📓 Backtest_Impact.ipynb — Expanded Notebook
Inside the notebook, you can add these cells (after importing the data):

 from backtest_impact import process_signals, add_efficiency_ratio, summarize_by_class, export_valid_signals

# Load CSV data
import pandas as pd
df = pd.read_csv("signals.csv")

# Process and extend signals
df = process_signals(df)
df = add_efficiency_ratio(df)

# Show summary by class
print(summarize_by_class(df))

And for the comparative chart

import matplotlib.pyplot as plt

def plot_efficiency_by_class(df):
    """
    Creates a bar chart showing the average efficiency for each signal class.
    """
    # Assumes a 'summarize_by_class' function is defined
    class_summary = summarize_by_class(df)
    
    class_summary['efficiency'].plot(kind='barh', color='cornflowerblue')
    
    plt.title("Average Efficiency by Signal Class")
    plt.xlabel("Net Alpha / Volatility")
    plt.grid(True)
    plt.tight_layout()
    plt.show()

# --- Function Call ---
plot_efficiency_by_class(df)

Here is a simulated dataset with 50 realistic trading signals, ready to be used with the framework. Save this as signals_extended.csv.

I’ve generated rows with:

  • Plausible tickers from the Italian market
  • A realistic distribution of alpha between 0.005 and 0.025
  • Volatility ranging from 0.15 to 0.35
  • class_signal values among: value, momentum, macro, mean-reversion, sector-rotation

Here is a segnali_estesi.csv file with 50 pre-formatted rows. Follow these steps:

Save the file as extended_signals.csv in your project folder

Copy all the text below

Paste it into a text editor (e.g., Notepad, VS Code)

date,ticker,alpha,Y,ADV,requested_quantity,average_price,volatility,signal_class
2025-07-01,ENEL.MI,0.012,0.015,1000000,400000,5.42,0.21,value
2025-07-01,STM.MI,0.019,0.017,800000,250000,42.85,0.29,momentum
2025-07-01,ENI.MI,0.010,0.013,1200000,300000,14.90,0.18,macro
2025-07-01,ISP.MI,0.008,0.012,900000,350000,2.44,0.26,mean-reversion
2025-07-01,LUX.MI,0.011,0.016,950000,200000,89.40,0.25,sector-rotation
2025-07-02,TIT.MI,0.009,0.014,920000,300000,0.22,0.19,value
2025-07-02,UCG.MI,0.015,0.017,1100000,400000,18.80,0.27,momentum
2025-07-02,ATL.MI,0.017,0.018,870000,360000,21.20,0.22,macro
2025-07-02,SRG.MI,0.010,0.014,800000,280000,5.05,0.20,mean-reversion
2025-07-02,MONC.MI,0.012,0.015,670000,250000,13.10,0.23,value
2025-07-03,ERG.MI,0.013,0.016,750000,320000,26.50,0.21,sector-rotation
2025-07-03,BMED.MI,0.016,0.018,950000,400000,0.69,0.19,macro
2025-07-03,BAMI.MI,0.010,0.014,820000,300000,3.60,0.17,mean-reversion
2025-07-03,A2A.MI,0.008,0.012,740000,250000,1.74,0.20,value
2025-07-03,CNH.MI,0.014,0.017,990000,410000,11.50,0.24,momentum
2025-07-04,REC.MI,0.011,0.015,830000,300000,7.22,0.22,macro
2025-07-04,CAPR.MI,0.009,0.013,880000,310000,5.81,0.18,sector-rotation
2025-07-04,ACE.MI,0.013,0.015,940000,350000,18.10,0.26,mean-reversion
2025-07-04,LEON.MI,0.016,0.019,970000,420000,13.60,0.29,momentum
2025-07-04,PRY.MI,0.012,0.016,890000,360000,36.80,0.24,value
2025-07-05,SPM.MI,0.010,0.014,930000,300000,5.30,0.20,macro
2025-07-05,TER.MI,0.015,0.017,860000,330000,7.95,0.21,momentum
2025-07-05,RTD.MI,0.011,0.013,870000,290000,4.60,0.22,macro
2025-07-05,EDNR.MI,0.009,0.012,910000,310000,3.90,0.18,mean-reversion
2025-07-05,MEDA.MI,0.014,0.016,940000,350000,6.70,0.25,sector-rotation
2025-07-06,ENAV.MI,0.010,0.014,880000,280000,4.12,0.20,value
2025-07-06,BF.MI,0.012,0.015,850000,300000,20.30,0.23,momentum
2025-07-06,DIA.MI,0.017,0.018,910000,390000,1.40,0.27,macro
2025-07-06,CDP.MI,0.011,0.013,920000,310000,0.65,0.16,mean-reversion
2025-07-06,CEL.MI,0.015,0.017,870000,360000,3.95,0.24,sector-rotation
2025-07-07,FCA.MI,0.013,0.015,940000,370000,9.80,0.22,momentum
2025-07-07,PST.MI,0.010,0.014,880000,280000,6.10,0.20,value
2025-07-07,GEO.MI,0.011,0.015,900000,310000,7.75,0.21,macro
2025-07-07,LAT.MI,0.014,0.017,890000,330000,8.90,0.23,mean-reversion
2025-07-07,SWK.MI,0.012,0.016,930000,350000,12.80,0.20,sector-rotation
2025-07-08,ZUC.MI,0.009,0.013,870000,290000,0.45,0.18,value
2025-07-08,AMS.MI,0.016,0.018,960000,400000,6.50,0.24,momentum
2025-07-08,FIN.MI,0.010,0.015,900000,300000,1.11,0.19,macro
2025-07-08,IGD.MI,0.013,0.016,950000,340000,3.15,0.23,mean-reversion
2025-07-08,BRI.MI,0.011,0.015,940000,310000,2.92,0.22,value
2025-07-09,GLT.MI,0.014,0.017,980000,380000,0.84,0.26,sector-rotation
2025-07-09,ANS.MI,0.012,0.016,910000,330000,3.10,0.21,macro
2025-07-09,KRA.MI,0.015,0.018,890000,360000,19.30,0.27,momentum
2025-07-09,TEL.MI,0.013,0.016,860000,350000,22.70,0.25,value
2025-07-09,VIS.MI,0.010,0.014,900000,290000,7.60,0.20,mean-reversion
2025-07-10,XEL.MI,0.014,0.017,950000,390000,1.65,0.23,macro
2025-07-10,YUX.MI,0.012,0.015,920000,320000,5.50,0.21,momentum
2025-07-10,ZEN.MI,0.009,0.013,870000,300000,6.22,0.18,value
2025-07-10,WLD.MI,0.013,0.015,910000,330000,4.01,0.24,sector-rotation

Let’s now add a simulated “realized profit” field for each signal in your dataset to build a cumulative PnL curve in the notebook or module.

We’ll assume that each executed signal generates a return (PnL) proportional to its net alpha plus some random noise (to simulate uncertainty). 📈

Python

import numpy as np

def simulate_profit(df, seed=42):
  """
  Simulates the daily and cumulative Profit and Loss (PnL) for executed signals.
  """
  np.random.seed(seed)

  # Only on executed signals
  df['daily_pnl'] = df.apply(lambda row:
      (row['net_alpha'] + np.random.normal(loc=0.0, scale=0.005)) * row['requested_quantity'] * row['average_price']
      if row['executed'] else 0, axis=1
  )

  # Calculate cumulative PnL curve
  df['cumulative_pnl'] = df['daily_pnl'].cumsum()
  return df

Once the field is added, you can visualize the aggregate performance:

Python

import matplotlib.pyplot as plt

def plot_pnl_curve(df):
  """
  Plots the cumulative Profit and Loss (PnL) curve over time.
  """
  plt.figure(figsize=(10,6))
  # Assumes 'date' and 'cumulative_pnl' columns exist
  plt.plot(df['date'], df['cumulative_pnl'], marker='o', linestyle='-')
  plt.title("Cumulative PnL Curve")
  plt.xlabel("Date")
  plt.ylabel("Cumulative Profit (€)")
  plt.grid(True)
  plt.tight_layout()
  plt.show()

LEAVE A REPLY

Please enter your comment!
Please enter your name here