Estimate the Average Treatment effect on the Treated (ATT) of treatment_ticker exceeding treatment_threshold on dependent_ticker’s return, via Propensity Score Matching (PSM) on covariate_tickers.

Also known as: PSM, nearest-neighbor propensity matching.

For more information about the method, see causal_inference_model.get_propensity_score_matching.

Derives a binary “treatment” indicator the same way get_logistic_regression derives its binary outcome: 1 if treatment_ticker’s return in a given period exceeds treatment_threshold (0.0, i.e. a positive return, by default), else 0. This lets PSM answer e.g. “on periods where treatment_ticker has an outsized/positive move, is dependent_ticker’s return different than it would otherwise be – comparing only periods that LOOK similar on covariate_tickers (to control for the possibility that treatment_ticker tends to move on the same periods/regimes that also independently affect dependent_ticker)?”

The formula is as follows:

\[\text{Dₜ} = 1 \;\text{if the treatment ticker's}\; \text{column} (\text{Return by default}) \text{is above}\; \text{treatment\_threshold} (0 \;\text{by default}),\;\; \text{else 0}\] \[e(\text{xₜ}) = 1 / (1 + \exp(- \text{xₜ'beta})),\;\; \text{a logit of Dₜ on the covariate tickers (plus a constant)}\] \[\text{Matching on}\; \text{ℓₜ} = \ln(e / (1 - e)) \text{:} \;\text{each treated period},\;\; \text{in order},\;\; \text{takes the nearest unmatched control period} (\text{one-to-one},\; \text{without replacement}) \text{if} | \text{ℓ\_treated} - \text{ℓ\_control} | \leq \text{caliper},\;\; \text{which defaults to}\; 0.2 \cdot \text{the standard deviation} (\text{ddof} = 1) \text{of}\; \text{ℓ}\] \[\text{ATT} = (1 / m) \cdot \operatorname{Σ}(y_{\text{treated}} - y_{\text{control}}) \text{over the}\; m \;\text{matched pairs}\] \[\text{SE} = \operatorname{std}(y_{\text{treated}} - y_{\text{control}},\; \text{ddof} = 1) / \sqrt{m},\;\; \text{with a Student-t distribution with m - 1 degrees of freedom}\]

Propensity Score Matching (PSM) in Python or via the MCP

Install the Finance Toolkit with:

pip install financetoolkit -U

Then call get_propensity_score_matching as shown below.

from financetoolkit import Toolkit

toolkit = Toolkit(
    ["XOM", "USO"],
    api_key="FINANCIAL_MODELING_PREP_KEY",
    start_date="2021-01-01",
    end_date="2025-12-31",
)

# XOM's return on days USO rises more than 2%, matched on the Benchmark's return
toolkit.econometrics.get_propensity_score_matching(
    dependent_ticker="XOM",
    treatment_ticker="USO",
    covariate_tickers="Benchmark",
    treatment_threshold=0.02,
)

Or, after setting up the Finance Toolkit MCP, ask:

On days the United States Oil Fund (USO) rose more than 2%, how much more did ExxonMobil return than on comparable days with the same S&P 500 move, from 2021 to 2025?

Which returns:

  Value
ATT 0.0163
Std. Error 0.0016
t-Statistic 9.9366
P-Value 0
Matched Pairs 181
N Treated 185
N Control 1069

Using Your Own Data

get_propensity_score_matching runs on any data, not only prices: ratios, economic indicators, yield curves or your own dataset. Pass a DataFrame, a Series or a dictionary of them to Econometrics as data, with the dates in the rows or the columns, and set changes to how its changes are calculated: "percentage", "log", "difference" for rates and ratios, or None when the data are changes already. For example, for the monthly unemployment rates of the United States and Germany, where each country is a column:

from financetoolkit import Economics, Econometrics

economics = Economics(start_date="2000-01-01")

unemployment = economics.get_unemployment_rate(
    countries=["United States", "Germany"], period="monthly"
)

econometrics = Econometrics(data=unemployment, changes="difference")

Then call econometrics.get_propensity_score_matching with dependent_ticker, treatment_ticker and covariate_tickers, using the column names of the data as tickers.

Parameters

get_propensity_score_matching accepts the following parameters.

ParameterTypeDescriptionExample
dependent_tickerstrThe outcome asset."AAPL"
treatment_tickerstrThe asset whose return, once it exceeds treatment_threshold, defines the treatment indicator."MSFT"
covariate_tickersstr | list[str]The asset(s) used as covariates to estimate the propensity score – should include asset(s) believed to drive selection into “treatment”."Benchmark"
treatment_thresholdfloatThe return threshold defining treatment. Defaults to 0.0.0.01
periodstrThe data frequency (daily, weekly, monthly, quarterly, or yearly). Defaults to “daily”."weekly"
columnstrThe historical data column to use. Defaults to “Return”."Return"
caliperfloat | NoneThe maximum allowed logit-propensity-score matching distance. Defaults to None, which uses Austin’s (2011) rule of thumb – see causal_inference_model.get_propensity_score_matching.0.05
add_constantboolWhether to include an intercept in the propensity score model. Defaults to True.False
roundingint | NoneThe number of decimals to round the results to. Defaults to None.2
Share