Appearance
mpt
How do you turn a window of returns into portfolio weights inside a script? Collect each day's returns into a var array, then pass a window of it to mpt.weights with a method such as "min_variance" or "sortino". mpt.read and mpt.score measure names and portfolios with the same words, so changing a portfolio's objective means changing one word. The functions read nothing but their arguments, so they can't see a later bar.
ameno
indicator("Least-variance weights of three coins")
var array<float> R = array.new_float(0)
var array<float> basis = array.new_float(0)
// One return per name per day, in the same order every day.
R.push(nz(close / close[1] - 1))
R.push(nz(request.security("ETH-USD", "1D", close / close[1] - 1)))
R.push(nz(request.security("SOL-USD", "1D", close / close[1] - 1)))
int days = math.floor(R.size() / 3)
int lo = math.max(0, days - 90)
array<bool> on = array.from(true, true, true)
array<float> w = mpt.weights("min_variance", R, 3, lo, days, on, basis,
upper = 0.6)
plot(w.get(0), "Weight of the chart's coin")
plot(mpt.score("vol", R, 3, lo, days, w), "Daily volatility")Every call reads the same window. returns holds n numbers a day, one per name, day after day. The call reads days lo to hi - 1, up to 1,000 names, and on says which names count. A measure is per day and isn't annualised. Volatility and covariance divide by the days read, not by one fewer. A ratio with nothing under it, a name that's off and a name with no reading all read na. An option a word can't honour stops the script and names the option. See mpt.
mpt.read
AmenoScript only
ameno
mpt.read(measure, returns, n, lo, hi, on, threshold, alpha, benchmark, order, compound) → array<float>One number per name: the measure of each name's returns over the window.
| Measure | Reads | Takes |
|---|---|---|
mean, vol | The mean, the standard deviation | |
downside | The root mean square of the losses below threshold | threshold |
sharpe, sortino | The mean over threshold, divided by vol or by downside | threshold |
omega, gain_to_pain | Gains over losses around threshold, or the sum over the losses | threshold |
kappa | The mean over threshold, divided by the root of the lower partial moment of order | threshold, order |
var, cvar | The loss at the alpha quantile, and the mean loss past it | alpha |
starr, rachev | The mean over threshold divided by cvar, and the gain tail's mean over the loss tail's | threshold, alpha |
max_drawdown, cdar, ulcer | The deepest drawdown, the mean of the worst 1 - alpha of them, and their root mean square | compound, and alpha for cdar |
calmar, martin | The mean over threshold, divided by the deepest drawdown or by ulcer | threshold, compound |
beta, alpha, treynor | Against benchmark, each on the excess over threshold | benchmark, threshold |
tracking_error, information | The active return's volatility, and its mean over that | benchmark |
| Parameter | Default | Description |
|---|---|---|
measureseries string | A word from the table above. | |
returnsarray<float> | The window's returns, n a day. | |
nseries int | Names per day. | |
loseries int | The first day to read. | |
hiseries int | One past the last day to read. | |
onarray<bool> | One bool per name. A name that's off reads na. | |
thresholdseries float | 0 | The return a ratio's excess is measured from, per day. |
alphaseries float | 0.95 | The confidence of var, cvar, cdar, starr and rachev. |
benchmarkarray<float> | One benchmark return per day, at least hi of them. | |
orderseries float | 2 | The moment kappa reads. |
compoundseries bool | false | true reads drawdowns on the compounded curve a chart draws, rather than on summed returns. |
mpt.score
AmenoScript only
ameno
mpt.score(measure, returns, n, lo, hi, weights, threshold, alpha, benchmark, order, compound) → series floatOne number for a portfolio: the measure of the returns weights would have earned over the window.
| Parameter | Default | Description |
|---|---|---|
measureseries string | A word from mpt.read's table. | |
returnsarray<float> | The window's returns, n a day. | |
nseries int | Names per day. | |
loseries int | The first day to read. | |
hiseries int | One past the last day to read. | |
weightsarray<float> | One weight per name. | |
thresholdseries float | 0 | As for mpt.read. |
alphaseries float | 0.95 | As for mpt.read. |
benchmarkarray<float> | As for mpt.read. | |
orderseries float | 2 | As for mpt.read. |
compoundseries bool | false | As for mpt.read. |
mpt.weights
AmenoScript only
ameno
mpt.weights(method, returns, n, lo, hi, on, basis, lower, upper, gross, groups, group_lower, group_upper, target_return, target_risk, turnover, max_turnover, held, threshold, alpha, benchmark, order, risk_aversion, estimator, mean, cov, budgets, compound) → array<float>Weights that sum to 1, chosen by method over the window, long only unless lower says otherwise.
basis is a var array<float> that starts empty and that the call owns: it keeps the last answer, so the next day's solve starts from it. Give each method its own, and leave it alone, since a basis the function didn't write stops the script. When no ordinary answer exists, the call falls back and says so through mpt.status: no name counts gives all zeros, one name takes everything, no variance holds equal weights, and limits that can't be met give all zeros. Everything is deterministic, with no random draw. See weights and limits.
| Method | Finds | How |
|---|---|---|
equal, inverse_vol | Equal shares, or shares by inverse volatility | Closed form |
risk_parity | Equal risk contributions, or budgets | Cyclical coordinate descent |
hrp | Hierarchical risk parity | Closed form, single linkage |
min_variance (or vol) | The least variance | Exact quadratic program |
utility | The mean less half risk_aversion times the variance | Exact quadratic program |
sharpe, information, diversification | The highest ratio | Exact, after Charnes and Cooper |
mean, alpha | The highest linear score | Exact linear program |
beta, treynor | The lowest absolute beta, or the highest Treynor ratio | Exact linear program |
omega, gain_to_pain | The highest ratio | An exact vertex walk with no limits, a linear program with them |
cvar, cdar, max_drawdown | The least of it | Exact linear program |
starr, calmar | The highest ratio | Exact linear program |
downside, sortino | The least downside, or the highest ratio | Exact, by finite Newton |
kappa | The highest ratio | Newton, or Omega's answer at order = 1 |
ulcer, martin | The least ulcer index, or the highest ratio | A bundle method to a gap of 1e-8 |
rachev | The highest ratio | A local search, since it isn't convex |
With compound = true, the drawdown words start from the exact answer on summed returns and then move shares between pairs of names while the compounded measure improves, so that answer is local.
| Parameter | Default | Description |
|---|---|---|
methodseries string | A word from the table above. | |
returnsarray<float> | The window's returns, n a day. | |
nseries int | Names per day. | |
loseries int | The first day to read. | |
hiseries int | One past the last day to read. | |
onarray<bool> | One bool per name. A name that's off gets 0. | |
basisarray<float> | A var array<float>, empty at the start, that this call owns. | |
lowerseries float/array<float> | 0 | The least weight per name: one number for every name, or an array with one per name. A negative value allows shorts. |
upperseries float/array<float> | The most weight per name, one number or an array with one per name. Left out, each name's is 1 less the other names' least weights, which limits nothing. | |
grossseries float | A cap on the sum of absolute weights. | |
groupsarray<int> | One group number per name, for group_lower and group_upper. | |
group_lowerarray<float> | The least total weight per group. | |
group_upperarray<float> | The most total weight per group. | |
target_returnseries float | The mean a risk word must reach. | |
target_riskseries float | The volatility mean may take. | |
turnoverseries float | A charge per unit of change from held or from the last weights, on min_variance, utility, cvar, cdar, max_drawdown and ulcer. | |
max_turnoverseries float | A cap on the sum of absolute changes from held or from the last weights. A ratio word takes this instead of turnover. | |
heldarray<float> | The weights held now. Left out, the last weights in basis. | |
thresholdseries float | 0 | As for mpt.read. |
alphaseries float | 0.95 | As for mpt.read. |
benchmarkarray<float> | As for mpt.read. | |
orderseries float | 2 | As for mpt.read. |
risk_aversionseries float | utility's weight on the variance, above 0. utility needs it. | |
estimatorseries string | "sample" | The covariance, "sample" or "ledoit_wolf". |
meanarray<float> | Your own mean per name, from any estimator, mpt.black_litterman included. | |
covmatrix<float> | Your own covariance matrix. A method that solves on the covariance stops the script on one that isn't positive semi-definite: mpt.psd repairs it. | |
budgetsarray<float> | risk_parity's share of risk per name. | |
compoundseries bool | false | As for mpt.read. |
mpt.omega_weights
AmenoScript only
ameno
mpt.omega_weights(returns, n, lo, hi, on, basis) → array<float>The weights with the highest Omega over the window, found exactly by a simplex over Omega's kinks.
A name that's off counts as nothing. Where no mix has a positive mean, the name with the highest Omega of its own takes everything, and names with gains and no losses share equally. Returns within 1e-10 of each other compare as equal, as the language's own compares do.
| Parameter | Description |
|---|---|
returnsarray<float> | The window's returns, n a day. |
nseries int | Names per day. |
loseries int | The first day to read. |
hiseries int | One past the last day to read. |
onarray<bool> | One bool per name. |
basisarray<float> | A var array<float>, empty at the start, where the call keeps yesterday's vertex. |
mpt.inverse_vol
AmenoScript only
ameno
mpt.inverse_vol(returns, n, lo, hi, on) → array<float>Each name's inverse volatility over the window, as shares that sum to 1.
Here na is no reading rather than a zero. A name with no volatility is left out, and if none has any, the names that are on share equally.
| Parameter | Description |
|---|---|
returnsarray<float> | The window's returns, n a day. |
nseries int | Names per day. |
loseries int | The first day to read. |
hiseries int | One past the last day to read. |
onarray<bool> | One bool per name. |
mpt.mean
AmenoScript only
ameno
mpt.mean(returns, n, lo, hi, on, estimator) → array<float>Each name's mean return over the window.
| Parameter | Default | Description |
|---|---|---|
returnsarray<float> | The window's returns, n a day. | |
nseries int | Names per day. | |
loseries int | The first day to read. | |
hiseries int | One past the last day to read. | |
onarray<bool> | One bool per name. | |
estimatorseries string | "sample" | "sample", or "bayes_stein" to shrink each mean toward the common one. |
mpt.vol
AmenoScript only
ameno
mpt.vol(returns, n, lo, hi, on) → array<float>Each name's volatility over the window, dividing by the days read.
| Parameter | Description |
|---|---|
returnsarray<float> | The window's returns, n a day. |
nseries int | Names per day. |
loseries int | The first day to read. |
hiseries int | One past the last day to read. |
onarray<bool> | One bool per name. |
mpt.cov
AmenoScript only
ameno
mpt.cov(returns, n, lo, hi, on, estimator) → matrix<float>The names' covariance matrix over the window.
| Parameter | Default | Description |
|---|---|---|
returnsarray<float> | The window's returns, n a day. | |
nseries int | Names per day. | |
loseries int | The first day to read. | |
hiseries int | One past the last day to read. | |
onarray<bool> | One bool per name. | |
estimatorseries string | "sample" | "sample" or "ledoit_wolf". |
mpt.corr
AmenoScript only
ameno
mpt.corr(returns, n, lo, hi, on, estimator) → matrix<float>The names' correlation matrix over the window.
| Parameter | Default | Description |
|---|---|---|
returnsarray<float> | The window's returns, n a day. | |
nseries int | Names per day. | |
loseries int | The first day to read. | |
hiseries int | One past the last day to read. | |
onarray<bool> | One bool per name. | |
estimatorseries string | "sample" | "sample" or "ledoit_wolf". |
mpt.psd
AmenoScript only
ameno
mpt.psd(cov, on) → matrix<float>A covariance matrix that mpt.weights and mpt.frontier can solve on.
A covariance you build pair by pair, each pair over the days both names have, may not be positive semi-definite: no set of returns could have made it. Given one as cov, a method that solves on the covariance stops the script. mpt.psd returns a matrix that already is one unchanged. Otherwise it sets its correlation's negative eigenvalues to 0 and its diagonal back to 1, as Rebonato and Jaeckel (1999) do, and gives each name back its own variance.
Only names that are on and have a variance above 0 count. Every other name keeps its row and column as given. A matrix that isn't square, one of more than 1,000 names, or a na between two names that count stops the script.
| Parameter | Description |
|---|---|
covmatrix<float> | The covariance, n rows by n columns. |
onarray<bool> | One bool per name. Left out, every name counts. |
ameno
indicator("A covariance repaired")
// Names 0 and 1 each move with name 2, yet against each other.
// No set of returns can do that, so the matrix isn't positive semi-definite.
matrix<float> C = matrix.new<float>(3, 3, 1.0)
matrix.set(C, 0, 1, -0.9)
matrix.set(C, 1, 0, -0.9)
matrix.set(C, 0, 2, 0.9)
matrix.set(C, 2, 0, 0.9)
matrix.set(C, 1, 2, 0.9)
matrix.set(C, 2, 1, 0.9)
matrix<float> Q = mpt.psd(C)
plot(matrix.get(Q, 0, 1), "Names 0 and 1, repaired") // about -0.5
plot(matrix.get(Q, 0, 2), "Names 0 and 2, repaired") // about 0.5
plot(matrix.get(Q, 0, 0), "Name 0's variance, kept") // 1mpt.bets
AmenoScript only
ameno
mpt.bets(returns, n, lo, hi, on) → array<float>How many names' worth of bet each name carries: the sum of its positive correlations with the names that are on, itself included. A name that moves with no other reads 1, and one that moves with every other reads close to the count of names on.
| Parameter | Description |
|---|---|
returnsarray<float> | The window's returns, n a day. |
nseries int | Names per day. |
loseries int | The first day to read. |
hiseries int | One past the last day to read. |
onarray<bool> | One bool per name. |
mpt.returns_of
AmenoScript only
ameno
mpt.returns_of(returns, n, lo, hi, weights) → array<float>The portfolio's own return on each day of the window, for weights held throughout.
| Parameter | Description |
|---|---|
returnsarray<float> | The window's returns, n a day. |
nseries int | Names per day. |
loseries int | The first day to read. |
hiseries int | One past the last day to read. |
weightsarray<float> | One weight per name. |
mpt.contributions
AmenoScript only
ameno
mpt.contributions(kind, returns, n, lo, hi, weights) → array<float>Each name's share of the portfolio's volatility or mean. The shares sum to the whole.
| Parameter | Description |
|---|---|
kindseries string | "risk" for volatility or "return" for the mean. |
returnsarray<float> | The window's returns, n a day. |
nseries int | Names per day. |
loseries int | The first day to read. |
hiseries int | One past the last day to read. |
weightsarray<float> | One weight per name. |
mpt.running_mean
AmenoScript only
ameno
mpt.running_mean(target, state) → array<float>Moves weights toward target by a step of their own mean one-way turnover so far, so no length has to be chosen.
It stays at zero until the target holds a weight. A target with a na stops the script.
| Parameter | Description |
|---|---|
targetarray<float> | The weights to move toward. |
statearray<float> | A var array<float>, empty at the start, that the call owns. |
mpt.frontier
AmenoScript only
ameno
mpt.frontier(returns, n, lo, hi, on, points, lower, upper, gross, groups, group_lower, group_upper, estimator, mean, cov) → matrix<float>The efficient frontier as a matrix, one row per point: the weights, then the volatility, then the mean, from the least risk to the highest mean, spaced evenly by mean.
| Parameter | Default | Description |
|---|---|---|
returnsarray<float> | The window's returns, n a day. | |
nseries int | Names per day. | |
loseries int | The first day to read. | |
hiseries int | One past the last day to read. | |
onarray<bool> | One bool per name. | |
pointsseries int | How many points, a whole number from 2 to 1,000. | |
lowerseries float/array<float> | 0 | As for mpt.weights. |
upperseries float/array<float> | As for mpt.weights. | |
grossseries float | As for mpt.weights. | |
groupsarray<int> | As for mpt.weights. | |
group_lowerarray<float> | As for mpt.weights. | |
group_upperarray<float> | As for mpt.weights. | |
estimatorseries string | "sample" | As for mpt.cov. |
meanarray<float> | Your own mean per name. | |
covmatrix<float> | Your own covariance matrix. One that isn't positive semi-definite stops the script: mpt.psd repairs it. |
mpt.black_litterman
AmenoScript only
ameno
mpt.black_litterman(cov, prior, views, targets, tau, risk_aversion, view_variance) → array<float>The Black-Litterman posterior mean per name, to pass to mpt.weights as mean.
| Parameter | Description |
|---|---|
covmatrix<float> | The names' covariance matrix. |
priorarray<float> | The market weights the prior mean is implied from. |
viewsmatrix<float> | One row per view, one column per name. |
targetsarray<float> | The return each view expects. |
tauseries float | The uncertainty of the prior, above 0. |
risk_aversionseries float | The market's risk aversion, above 0. |
view_variancearray<float> | One variance per view. Left out, He and Litterman's diagonal. |
mpt.capped
AmenoScript only
ameno
mpt.capped(basis) → series intHow many times a solve on basis stopped short of the optimum at one of its limits on steps or precision, summed over every call that used it. One call can count more than once. A correct run reads 0.
| Parameter | Description |
|---|---|
basisarray<float> | The basis an mpt.weights or mpt.omega_weights call owns. |
mpt.status
AmenoScript only
ameno
mpt.status(basis) → series stringWhat the last call on basis found, as one of: "solved", "stopped short", "limits can't be met", "no mix has a positive excess mean", "no risk to reduce", "target out of reach" or "singular: ties went to the most equal weights".
| Parameter | Description |
|---|---|
basisarray<float> | The basis an mpt.weights call owns. |