\[ % MathJax has no bm package; redefine \bm in terms of \boldsymbol, which it supports natively \newcommand{\bm}[1]{\boldsymbol{#1}} % --- Operators ----------------------------------------------------------- % Expectation operator \DeclareMathOperator{\E}{E} % Variance operator \DeclareMathOperator{\Var}{Var} % Covariance operator \DeclareMathOperator{\Cov}{Cov} % Correlation operator \DeclareMathOperator{\Corr}{Corr} % Rank operator (Spearman rank correlation) \DeclareMathOperator{\Rank}{Rank} % Skewness operator \DeclareMathOperator{\Skewness}{Skewness} % Kurtosis operator \DeclareMathOperator{\Kurtosis}{Kurtosis} % Difference/differential operator (upright d per ISO 80000-2) \newcommand{\Diff}{\mathrm{d}} % --- Risk Measures ------------------------------------------------------- % Value at Risk \DeclareMathOperator{\VaR}{VaR} % Expected Shortfall \DeclareMathOperator{\ES}{ES} % Marginal Value at Risk \DeclareMathOperator{\MVaR}{MVaR} % Component Value at Risk \DeclareMathOperator{\CompVaR}{CVaR} % Incremental Value at Risk \DeclareMathOperator{\IVaR}{IVaR} % Component Expected Shortfall \DeclareMathOperator{\CompES}{CES} % Weighted sensitivity for risk factor in FRTB SBM bucket \DeclareMathOperator{\WS}{WS} % Hedge-benefit ratio for FRTB DRC bucket \DeclareMathOperator{\HBR}{HBR} % Stress scenario risk measure for FRTB NMRF capital add-on \DeclareMathOperator{\SES}{SES} % Jump-to-default exposure for obligor (FRTB DRC) \DeclareMathOperator{\JTD}{JTD} % Loss given default for obligor (FRTB DRC) \DeclareMathOperator{\LGD}{LGD} % Default risk weight for obligor (FRTB DRC) \DeclareMathOperator{\RW}{RW} % ES ratio function \newcommand{\ESratio}{\lambda} % Arbitrary risk measure function (coherence axioms) \newcommand{\RiskMeasure}{\varphi} % Asset position (coherence axioms) \newcommand{\Asset}{A} % First named asset in examples \newcommand{\AssetA}{A} % Second named asset in examples \newcommand{\AssetB}{B} % Third named asset in examples \newcommand{\AssetC}{C} % Constant (risk measure axioms) \newcommand{\Constant}{c} % Bucket-level aggregate sensitivity in FRTB SBM \newcommand{\BucketAgg}{S} % Cross-bucket correlation in FRTB SBM \newcommand{\CrossBucketCorr}{\gamma} % Notional amount for an instrument (FRTB DRC and RRAO) \newcommand{\Notional}{\text{Notional}} % --- Distributions ------------------------------------------------------- % Binomial distribution \DeclareMathOperator{\Binomial}{Binomial} % Uniform distribution \DeclareMathOperator{\Uniform}{Uniform} % Normal distribution \newcommand{\NormalDist}{\mathcal{N}} % Student-t CDF \newcommand{\StudentCDF}{t} % Student-t PDF \newcommand{\StudentPDF}{f} % --- Returns ------------------------------------------------------------- % Price \newcommand{\Price}{P} % Simple (arithmetic) return \newcommand{\SimpleReturns}{R} % Log (compound) return \newcommand{\CompoundReturns}{y} % Matrix of compound returns \newcommand{\ReturnMatrix}{\bm{y}} % Dividend payment \newcommand{\dividend}{d} % --- Volatility ---------------------------------------------------------- % Realised variance \DeclareMathOperator{\RealisedVar}{RV} % Realised volatility (square root of realised variance) \DeclareMathOperator{\RealisedVol}{RVol} % Volatility \newcommand{\Vol}{\sigma} % GARCH intercept parameter \newcommand{\GARCHconst}{\omega} % ARCH coefficient (weight on past squared returns) \newcommand{\ARCHcoeff}{\alpha} % GARCH coefficient (weight on past variance) \newcommand{\GARCHcoeff}{\beta} % EWMA decay factor (typically 0.94) \newcommand{\EWMAdecay}{\lambda} % Standardized residual / error term \newcommand{\StdNormal}{\epsilon} % Leverage parameter in apARCH \newcommand{\APARCHleverage}{\zeta} % Power parameter in apARCH \newcommand{\APARCHpower}{\delta} % Degrees of freedom (Student-t) \newcommand{\DOF}{\nu} % GJR-GARCH leverage parameter \newcommand{\GJRleverage}{\gamma} % GARCH-X external regressor coefficient \newcommand{\RegressorCoeff}{\xi} % Mean (first moment) \newcommand{\Mean}{\mu} % Lag order in volatility models \newcommand{\Lag}{L} % Dummy variable (indicator in regressions) \newcommand{\DummyVar}{D} % --- Portfolio ----------------------------------------------------------- % Portfolio weight (scalar) \newcommand{\weight}{w} % Portfolio weight vector \newcommand{\weights}{\bm{w}} % Covariance matrix \newcommand{\CovMatrix}{\bm{\Sigma}} % Correlation matrix \newcommand{\CorrMatrix}{\bm{C}} % Correlation coefficient \newcommand{\correlation}{\rho} % Number of assets in portfolio \newcommand{\NumberAssets}{K} % Portfolio value \newcommand{\PortfolioValue}{\vartheta} % Asset beta (CAPM) \newcommand{\AssetBeta}{\beta} % Ledoit-Wolf shrinkage intensity \newcommand{\ShrinkageIntensity}{\delta} % Sample covariance matrix \newcommand{\SampleCov}{\bm{S}} % --- Time Series --------------------------------------------------------- % Sample size \newcommand{\SampleSize}{T} % Generic count (number of lags, payments, etc.) \newcommand{\Count}{N} % Estimation window length \newcommand{\EstWindow}{W_E} % Testing window length \newcommand{\TestWindow}{W_T} % Stress window length \newcommand{\StressWindow}{W_S} % AR coefficient \newcommand{\ARcoeff}{\phi} % MA coefficient \newcommand{\MAcoeff}{\psi} % --- Probability --------------------------------------------------------- % Probability level (e.g. 0.01 for 1% VaR) \newcommand{\probability}{p} % Cumulative sorted scenario weight \newcommand{\CumScenarioWeight}{\Omega} % p-quantile of the P and L distribution \newcommand{\Quantile}{q} % Quantile function \newcommand{\QuantileFunction}{Q} % Probability density function \newcommand{\PDF}{f} % Cumulative distribution function \newcommand{\CDF}{F} % Standard normal CDF \newcommand{\NormalCDF}{\Phi} % Inverse standard normal (quantile function) \newcommand{\NormalQuantile}{\Phi^{-1}} % Standard normal density function \newcommand{\NormalPDF}{\phi} % Likelihood function \newcommand{\lik}{\mathcal{L}} % Log-likelihood \newcommand{\LogLikelihood}{\ell} % Parameter vector (MLE estimation) \newcommand{\ParamSet}{\theta} % Parameter space \newcommand{\ParamSpace}{\Theta} % Fisher information matrix \newcommand{\FisherInfo}{\mathcal{I}} % Statistical power (Type II error rate) \newcommand{\StatPower}{\beta} % Significance level (CI coverage complement) \newcommand{\SignifLevel}{\gamma} % --- Options ------------------------------------------------------------- % Call option label \newcommand{\CallOption}{\text{call}} % Put option label \newcommand{\PutOption}{\text{put}} % Strike price \newcommand{\Strike}{X} % Risk-free interest rate \newcommand{\RiskFree}{r_f} % Option maturity (time to expiration) \newcommand{\OptionMaturity}{\tau} % Option delta \newcommand{\OptionDelta}{\Delta} % Option gamma \newcommand{\OptionGamma}{\Gamma} % Option vega \newcommand{\Vega}{\mathcal{V}} % Holding period \newcommand{\HoldingPeriod}{H} % Simulation profit/loss (signed; negative is a loss) \newcommand{\ProfitLoss}{\Pi} % Futures price \newcommand{\Futures}{F} % Hedge ratio \newcommand{\HedgeRatio}{h} % Units of basic asset held \newcommand{\StockHolding}{x^b} % Units of options held \newcommand{\OptionHolding}{x^o} % --- Interest Rates ------------------------------------------------------ % Bond convexity \newcommand{\Convexity}{C} % Interest rate / yield \newcommand{\Yields}{r} % Key rate change, basis points (scalar tenor component); used as \KeyRateChange_k or \KeyRateChange_{t,k}. EXCEPTION to the no-macro-to-macro-indirection rule, decided 2026-08-09: composed from \Yields on purpose, so a future change to the rate glyph propagates here without a follow-up edit. \Yields is defined earlier in generated output (same interest_rates category, TOML order), so expansion order is safe; verify this still holds if either entry is reordered. \newcommand{\KeyRateChange}{\Delta \Yields^{\mathrm{bp}}} % Key rate changes, basis points (vector); used bare or as \KeyRateChanges_t. EXCEPTION to the no-macro-to-macro-indirection rule, decided 2026-08-09: composed from \Yields on purpose, so a future change to the rate glyph propagates here without a follow-up edit. \bm{\Yields} depends on \bm/\boldsymbol correctly expanding a macro argument rather than a bare letter --- confirm this renders correctly the first time either consumer compiles it. \newcommand{\KeyRateChanges}{\Delta \bm{\Yields}^{\mathrm{bp}}} % Dollar value of a basis point \newcommand{\DV}{\text{DV01}} % Mean reversion speed (interest rate models) \newcommand{\MeanReversion}{\varkappa} % Modified duration \newcommand{\ModDur}{D} % Macaulay duration \newcommand{\MacDur}{D_{\text{Mac}}} % Effective duration \newcommand{\EffDur}{D_{\text{eff}}} % DV01 vector \newcommand{\DVvec}{\bm{d}} % Long-run mean rate (Vasicek/CIR/Hull-White) \newcommand{\LongRunRate}{\theta} % Hull-White time-varying drift \newcommand{\HullWhiteDrift}{\varphi} % Cash flow (coupon/principal payment) \newcommand{\CashFlow}{c} % --- Extreme Value ------------------------------------------------------- % Tail index (EVT, Pareto-type tails) \newcommand{\TailIndex}{\iota} % Shape parameter (xi = 1/iota) \newcommand{\ShapeParam}{\varsigma} % Threshold value (EVT) \newcommand{\Threshold}{u} % Extremal index (Leadbetter 1983) \newcommand{\ExtremalIndex}{\theta} % Multivariate extremal coefficient, theta in [1,d]. theta=1 complete tail dependence; theta=d asymptotic independence. Glyph collides with ExtremalIndex and ParamSet; disambiguated at source by macro name. \newcommand{\ExtremalCoef}{\theta} % GEV distribution function \newcommand{\GEV}{\mathcal{H}} % GPD distribution function \newcommand{\GPD}{\mathcal{G}} % GPD scale parameter \newcommand{\GPDscale}{\beta} % GPD scale parameter at a threshold u, distinct from the generic GPD scale \newcommand{\GPDscaleAtThreshold}{\beta_u} % Number of threshold exceedances (EVT) \newcommand{\TailCount}{C} % Sample maximum (block maxima, EVT) \newcommand{\SampleMax}{M} % Kurtosis value (realized, e.g. sample kurtosis) \newcommand{\KurtosisVal}{\kappa} % Pareto scaling constant (EVT) \newcommand{\ParetoConst}{\mathcal{A}} % Generic constant (EVT asymptotics) \newcommand{\RemainderConst}{\mathcal{C}} % Little-o asymptotic notation \newcommand{\LittleO}{o} % Moment order (EVT) \newcommand{\MomentOrder}{\mathscr{m}} % Doubled sample size (EVT block maxima) \newcommand{\DoubleSample}{D} % GEV normalizing location constant \newcommand{\GEVloc}{a} % GEV normalizing scale constant \newcommand{\GEVscale}{b} % Negative return (loss, EVT context) \newcommand{\NegativeReturn}{L} % --- Copulas ------------------------------------------------------------- % Copula function \newcommand{\Copula}{\mathcal{C}} % Copula density \newcommand{\CopulaDensity}{c} % Generator function (Archimedean copulas) \newcommand{\generator}{\varphi} % Copula dependence parameter \newcommand{\CopulaParam}{\theta} % Copula marginal parameter set \newcommand{\MarginalParamSet}{\eta} % Lower tail dependence coefficient \newcommand{\LowerTailDep}{\lambda_L} % Upper tail dependence coefficient \newcommand{\UpperTailDep}{\lambda_U} % Marginal CDF (second variable) \newcommand{\MarginalCDF}{G} % Joint density function \newcommand{\JointDensity}{h} % Marginal density function \newcommand{\MarginalDensity}{g} % Joint distribution function \newcommand{\JointCDF}{H} % Gaussian copula correlation parameter \newcommand{\CopulaCorr}{\rho} % --- Multivariate -------------------------------------------------------- % Diagonal matrix of conditional volatilities \newcommand{\DiagVolD}{\bm{D}} % Idiosyncratic variance matrix (diagonal, factor models) \newcommand{\IdioVarMatrix}{\bm{\Psi}} % Covariance matrix entry (two indices) \newcommand{\CovElement}{\sigma} % Auxiliary matrix in DCC dynamics \newcommand{\DCCauxQ}{\bm{Q}} % BEKK constant matrix \newcommand{\BEKKconst}{\bm{\Omega}} % BEKK ARCH parameter matrix \newcommand{\BEKKarch}{\bm{A}} % BEKK GARCH parameter matrix \newcommand{\BEKKgarch}{\bm{B}} % DCC rescaling diagonal matrix \newcommand{\DCCrescaleZ}{\bm{Z}} % DCC auxiliary matrix element \newcommand{\DCCelement}{q} % Factor loadings matrix (loadings/eigenvectors of the covariance matrix) \newcommand{\FactorLoadings}{\bm{\Lambda}} % Factor subscript label \newcommand{\factor}{\text{factor}} % PCA eigenvalue \newcommand{\Eigenvalue}{\lambda} % Number of factors in factor model \newcommand{\NumberFactors}{m} % Factor return vector \newcommand{\FactorReturn}{\bm{g}} % PCA factor score (scalar; not an eigenvector) \newcommand{\PCAFactor}{F} % BEKK cross-covariance coefficient \newcommand{\BEKKcross}{\delta} % Factor loading element \newcommand{\FactorLoading}{\Lambda} % DCC correlation persistence parameter \newcommand{\DCCxi}{\xi} % DCC news coefficient (weight on recent shocks) \newcommand{\DCCzeta}{\zeta} % --- Simulation ---------------------------------------------------------- % Number of simulation paths \newcommand{\NumberSims}{B} % Cholesky factor of covariance matrix \newcommand{\Cholesky}{\bm{L}} % Uniform random number \newcommand{\UniformDraw}{\tilde{u}} % --- Backtesting --------------------------------------------------------- % Violation ratio (observed/expected violations) \DeclareMathOperator{\ViolRatio}{VR} % Stressed Value at Risk \DeclareMathOperator{\StressedVaR}{SVaR} % QLIKE loss function for variance forecasts \DeclareMathOperator{\QLIKE}{QLIKE} % Mean squared error (forecast loss function) \DeclareMathOperator{\MSE}{MSE} % Mean absolute error (forecast loss function) \DeclareMathOperator{\MAE}{MAE} % Markov transition probability \newcommand{\TransProb}{\pi} % Exception indicator: 1 if y_t < -VaR_t \newcommand{\ExceptionInd}{\eta} % Exception count \newcommand{\ExceptionCount}{\upsilon} % Probability Integral Transform \newcommand{\ProbIntegralTransform}{\hat{u}} % Quantile score (pinball loss function) \newcommand{\QuantileScore}{\mathcal{S}} % Kolmogorov-Smirnov test statistic \newcommand{\KSstat}{D} % Test statistic (generic base letter) \newcommand{\TestStat}{J} % --- General ------------------------------------------------------------- % Regression intercept \newcommand{\RegressionIntercept}{a} % Regression slope \newcommand{\RegressionSlope}{\beta} % Risk factor \newcommand{\RiskFactor}{x} % Forecast horizon \newcommand{\ForecastHorizon}{h} % Block length (bootstrap) \newcommand{\BlockLength}{\ell} % Capital tau variant \newcommand{\DeliveryTime}{\Upsilon} % Today's calendar time in years (option pricing input; distinct from trading-date index t) \newcommand{\CalendarTime}{t^*} % Probability measure \newcommand{\ProbMeasure}{\mathbb{P}} % Risk-neutral measure \newcommand{\RiskNeutral}{\mathbb{Q}} % Pricing function (Black-Scholes, bond pricing, etc.) \newcommand{\PricingFn}{V} % Indicator function \newcommand{\Indicator}{\bm{1}} % Vector of ones \newcommand{\OnesVector}{\bm{1}} % Risk factor sensitivity (first-order P&L) \newcommand{\Sensitivity}{\delta} % Second-order sensitivity (convexity/gamma) \newcommand{\ConvexitySens}{\gamma} % Output floor percentage (Basel) \newcommand{\OutputFloor}{\alpha} % --- Subscript Labels ---------------------------------------------------- % Subscript label: annualized \newcommand{\Annual}{a} % Subscript label: implied \newcommand{\Implied}{I} % Subscript label: portfolio \newcommand{\Portfolio}{\pi} % --- Text Abbreviations -------------------------------------------- % S&P 500 index \newcommand{\SP}{\text{S\&P-500}} % Student-t distribution (text) \newcommand{\St}{\text{Student-t}} % Geopolitical Risk index (Caldara and Iacoviello) \newcommand{\GPR}{\text{GPR}} \]

9  Variables in Julia

Risk models depend on how we store and manipulate data. This chapter introduces the Julia structures used throughout these notes.

9.1 Assignment

In Julia, we use the equal sign, =, to assign a value to a variable. The variable’s name is on the left, and the value to be stored is on the right.

x = 3
y = 4
x == y  # Two equal signs test for equality

Note that two equal signs, ==, are used to test for equality, not assignment.

9.2 Julia data structures

Risk work starts with structure. We use vectors for returns, matrices for portfolios and dictionaries for parameters.

9.2.1 Vectors

Julia vectors are one-dimensional arrays. Row vectors in Julia are represented as 1×N matrices.

v = zeros(4)
println(v)

v = [1, 2, 3, 4, 5]
println(v)

v = collect(1:5)
println(v)

v = collect(-1:0.5:2)
println(v)

v = [1, 3, 7, 3, 0.4] .* 3
println(v)
[0.0, 0.0, 0.0, 0.0]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0]
[3.0, 9.0, 21.0, 9.0, 1.2000000000000002]

Note the dot in .* for element-wise operations (broadcasting).

Vectors can contain mixed types using Any:

x = Any[1, 4, 0.9, "ss"]
println(x)
Any[1, 4, 0.9, "ss"]

For numeric-only vectors:

x = [1, 4, 0.9]
println(x)
[1.0, 4.0, 0.9]

9.2.2 Matrices

Julia can create two-dimensional matrices. We usually work with two-dimensional matrices, but we will encounter three-dimensional arrays in the multivariate volatility models.

m = zeros(3, 1)
println(m)

m = zeros(3, 2)
println(m)

m = fill(3, 3, 2)
println(m)
[0.0; 0.0; 0.0;;]
[0.0 0.0; 0.0 0.0; 0.0 0.0]
[3 3; 3 3; 3 3]

We can combine arrays with hcat (horizontal concatenation) and vcat (vertical concatenation), similar to R’s cbind and rbind:

v = [1, 3, 7, 3, 0.4] .* 3
m = hcat(v, v)
println(m)

m = vcat(v', v')
println(m)
[3.0 3.0; 9.0 9.0; 21.0 21.0; 9.0 9.0; 1.2000000000000002 1.2000000000000002]
[3.0 9.0 21.0 9.0 1.2000000000000002; 3.0 9.0 21.0 9.0 1.2000000000000002]

We can access individual elements of matrices and vectors:

v = [1, 3, 7, 3, 0.4] .* 3
m = hcat(v, v)
println(m[1, 2])
println(m[:, 2])
println(m[2, :])
println(m[3:5, 1])
println(v[2:3])
3.0
[3.0, 9.0, 21.0, 9.0, 1.2000000000000002]
[9.0, 9.0]
[21.0, 9.0, 1.2000000000000002]
[9.0, 21.0]

Note that Julia uses one-based indexing, like R.

9.2.3 Dictionaries

Julia dictionaries are key-value pairs. They allow us to group multiple variables together.

d = Dict()
d["a"] = 2
d["b"] = "Julia is great."
println(d)

d = Dict("l" => [2, 3], "b" => "Risk")
println(d)

w = Dict()
w["q"] = "my dict"
w["d"] = d
println(w)
Dict{Any, Any}("b" => "Julia is great.", "a" => 2)
Dict{String, Any}("b" => "Risk", "l" => [2, 3])
Dict{Any, Any}("q" => "my dict", "d" => Dict{String, Any}("b" => "Risk", "l" => [2, 3]))

We can find out what is in a dictionary:

println(keys(w))
Any["q", "d"]

Access individual elements:

println(w["d"])
Dict{String, Any}("b" => "Risk", "l" => [2, 3])

We make extensive use of dictionaries in these notes.

9.2.4 Named tuples

Julia also has named tuples, which are immutable and can be more efficient than dictionaries for fixed structures:

portfolio = (weights = [0.3, 0.7], assets = ["AAPL", "MSFT"])
println(portfolio.weights)
println(portfolio.assets)
[0.3, 0.7]
["AAPL", "MSFT"]

9.3 DataFrames

For tabular data, the DataFrames.jl package provides functionality similar to R data frames and pandas DataFrames. It is an external package, so we load it with using DataFrames.

using DataFrames

df = DataFrame(
    col1 = [1, 2, 3],
    col2 = ["A", "B", "C"],
    col3 = [true, true, false],
    col4 = [1.0, 2.2, 3.3]
)
println(df)
3×4 DataFrame
 Row │ col1   col2    col3   col4
     │ Int64  String  Bool   Float64
─────┼───────────────────────────────
   1 │     1  A        true      1.0
   2 │     2  B        true      2.2
   3 │     3  C       false      3.3

You specify each column name and the vector of values it contains. We can check the structure:

println(eltype.(eachcol(df)))
println(size(df))
println(names(df))
DataType[Int64, String, Bool, Float64]
(3, 4)
["col1", "col2", "col3", "col4"]

9.3.1 Accessing data from columns

We can access data from columns by name using dot notation or bracket notation:

println(df.col1)
println(df[!, :col1])
[1, 2, 3]
[1, 2, 3]

9.3.2 Transforming between arrays and DataFrames

You can convert between matrices and DataFrames:

my_matrix = permutedims(reshape(1:10, 2, 5))
println(my_matrix)
println(typeof(my_matrix))

df = DataFrame(my_matrix, [:Odd, :Even])
println(df)
println(typeof(df))
[1 2; 3 4; 5 6; 7 8; 9 10]
Matrix{Int64}
5×2 DataFrame
 Row │ Odd    Even
     │ Int64  Int64
─────┼──────────────
   1 │     1      2
   2 │     3      4
   3 │     5      6
   4 │     7      8
   5 │     9     10
DataFrame

Convert back:

mat = Matrix(df)
println(typeof(mat))
Matrix{Int64}

9.4 Julia-specific concepts

Julia has a few habits that matter in practice. Ignore missing, types or mutability and the code will behave in ways that surprise you.

9.4.1 Nothing, missing and NaN

Julia distinguishes three ways a value can be absent. nothing is what a function returns when it has nothing to report, for example a failed findfirst search:

x = findfirst(==(9), [1, 2, 3])
println(x)
println(isnothing(x))
nothing
true

missing marks a missing data point, most often a DataFrame entry, and propagates through arithmetic rather than raising an error. NaN is a floating-point value for an undefined numerical result, such as 0.0 / 0.0. In DataFrames, missing values are represented as missing:

using DataFrames
df = DataFrame(a = [1, missing, 3], b = [NaN, 2.0, 3.0])
println(df)
println(ismissing.(df.a))
println(isnan.(df.b))
3×2 DataFrame
 Row │ a        b
     │ Int64?   Float64
─────┼──────────────────
   1 │       1    NaN
   2 │ missing      2.0
   3 │       3      3.0
Bool[0, 1, 0]
Bool[1, 0, 0]

9.4.2 Type system

Every value has a type, which typeof() reports. A plain variable can be rebound to a value of any type. Annotating it, as below, fixes the binding’s type:

int_val::Int64 = 5
float_val::Float64 = 3.14
str_val::String = "hello"
println(typeof(int_val), " ", typeof(float_val), " ", typeof(str_val))
Int64 Float64 String

9.4.3 Mutability

Julia distinguishes between mutable and immutable types:

  • Immutable: numbers, strings, tuples
  • Mutable: arrays, dictionaries, mutable structs
# Arrays are mutable
a = [1, 2, 3]
b = a
b[1] = 99
println("a = $a, b = $b")
a = [99, 2, 3], b = [99, 2, 3]

To create an independent copy:

a = [1, 2, 3]
b = copy(a)
b[1] = 99
println("a = $a, b = $b")
a = [1, 2, 3], b = [99, 2, 3]

copy() is shallow, so nested mutable objects still share their inner structures with the original. For nested structures, use deepcopy().

9.4.4 Scope and global variables

Variables in Julia have scope. Variables defined at the top level are global, while variables defined inside functions are local.

global_var = 100

function my_function()
    local_var = 50
    println("Inside function: global_var = $global_var")
    println("Inside function: local_var = $local_var")
end

my_function();
Inside function: global_var = 100
Inside function: local_var = 50

To modify a global variable inside a function, use the global keyword:

counter = 0

function increment()
    global counter
    counter += 1
end

increment()
println(counter)
1

Reading a non-const global inside a function also carries a performance cost. Because the global’s type can change at any time, the compiler cannot specialise the function for it, and every access falls back to a slow, generic code path. Declare a global const when its value never changes, or pass the value into the function as an argument instead:

const MAX_LEVERAGE = 3.0

function within_limit(leverage)
    leverage <= MAX_LEVERAGE
end

println(within_limit(2.5))
true

9.5 Julia programming essentials

9.5.1 String interpolation

Julia uses $ for string interpolation:

x = 10
y = 1.2
w = "risk"

println("Important number for $w is x = $x")
println("The return is $(round(100 * y, digits=1))%")
Important number for risk is x = 10
The return is 120.0%

9.5.2 Printing

Julia uses println() for output with a newline and print() without:

x = 10
y = 1.234

println(x)
println("This is the answer. x = $x, and y = $y.")
10
This is the answer. x = 10, and y = 1.234.

9.5.3 Some useful functions

A small set of Julia functions does most of the work in these notes:

  • length(): return the length of an object
  • size(): return dimensions of an array
  • typeof(): return the type of an object
  • println(), print(): print values
  • zeros(), ones(), fill(): create arrays
  • push!(), append!(): add elements to arrays
  • hcat(), vcat(): concatenate arrays

9.6 Packages/libraries

Julia manages packages through Pkg. The main packages for data science and financial analysis include:

  • DataFrames.jl: data manipulation
  • CSV.jl: reading/writing CSV files
  • TidierPlots.jl with CairoMakie.jl: visualisation (used throughout these notes)
  • ARCHModels.jl: GARCH models
  • Distributions.jl: statistical distributions
  • Statistics: basic statistics (in standard library)
  • LinearAlgebra: matrix operations (in standard library)

Install packages using the package manager:

using Pkg
Pkg.add("DataFrames")

Import packages with using:

using Statistics
using LinearAlgebra

9.7 Matrix algebra

Julia supports linear algebra natively. Use .* for element-by-element multiplication and * for matrix multiplication:

weight = [0.3, 0.7]
prices = rand(5, 2) .* 100

println("Weights:")
println(weight)
println("\nPrices:")
println(prices)
Weights:
[0.3, 0.7]

Prices:
[36.07851335873995 2.7039367444012252; 64.59903377441113 60.85645418276637; 12.394266252342568 13.235562025276227; 2.153707048503839 43.61375994096882; 14.074419426197304 17.97047334370676]
# Element-by-element multiplication (broadcasting)
println(weight' .* prices)
[10.823554007621984 1.8927557210808574; 19.379710132323336 42.59951792793645; 3.71827987570277 9.264893417693358; 0.6461121145511517 30.52963195867817; 4.222325827859191 12.579331340594733]
# Matrix multiplication
println(prices * weight)
[12.716309728702843, 61.97922806025979, 12.98317329339613, 31.175744073229325, 16.80165716845392]
# Transpose and multiply
println(weight' * prices')
adjoint([12.716309728702843, 61.97922806025979, 12.98317329339613, 31.175744073229325, 16.80165716845392])

9.8 Source files — include

Julia code can be organised into separate files and included:

# In functions.jl
function my_function()
    return "Hello"
end

# In main script
include("functions.jl")

In these notes we keep shared functions in common/functions.jl and include them where needed.

That is the Julia material the rest of the book relies on. The book continues at Chapter 10.