Engee documentation
Notebook

Extraction and primary analysis of stock quotes

Introduction

This paper presents the extraction and primary analysis of stock market quotations using the YFinance library, which provides programmatic access to data from the Yahoo Finance financial portal.

Yahoo Finance* is one of the largest public financial portals that accumulates exchange information on stocks, funds, currencies and commodity assets from many global platforms (NASDAQ, NYSE, AMEX, ARCA, BATS). Library YFinance It allows you to access this data directly from the code, which makes it a convenient tool for educational and analytical tasks.

In this example, let's look at the stock quotes of Apple Inc. (AAPL) for one year in increments of one trading day. Based on them, we will calculate and visualize a number of key characteristics.:

  • Price dynamics – a chart of the closing price changes over time, allowing you to see the overall trend of the instrument, periods of growth and fall, as well as the amplitude of fluctuations.
  • Trading volume – the number of shares bought and sold during the interval. The indicator reflects the activity of market participants and the liquidity of the instrument.
  • Profitability is the relative price change over one interval; it shows how much of the invested amount the investor has gained or lost in one step.
  • Volatility is a measure of the variability of returns, usually measured as a standard deviation. The higher the volatility, the more the price fluctuates and the higher the risk of the instrument.
  • Accumulated profitability is the final result of holding a stock from the beginning of the period to the current moment; it allows you to compare several instruments on the same chart.
  • Drawdown – shows how much the current price is below its historical maximum, and characterizes the depth of the fall and the maximum loss for the investor.

Additionally, we will compare the accumulated profitability of several large technology companies (AAPL, MSFT, NVDA, AMZN), which will allow us to assess the relative effectiveness of the chosen tool.

The purpose of the work is to demonstrate the full cycle of quantitative analysis of a stock: from obtaining quotes from an external source to calculating and visualizing metrics characterizing its profitability, risk and investment attractiveness.

Joining libraries

For this work, we will need the following libraries:

  • YFinance – getting stock data from Yahoo Finance;
  • DataFrames – working with tabular data;
  • Statistics – working with tabular data;
  • Dates – working with date and time;
  • Colors – working with colors in visualization.
In [ ]:
for lib in ["YFinance", "DataFrames", "Statistics", "Dates", "Colors"]
    try
        eval(Meta.parse("using $lib"))
    catch ex
        import Pkg
        Pkg.add(lib)
        eval(Meta.parse("using $lib"))
    end
end

Selecting an exchange and displaying a list of stocks

Library YFinance allows you to extract stock quotation data from the following exchanges:

  • AMEX – American Stock Exchange
  • ARCA – Archipelago Exchange
  • BATS – Better Alternative Trading System Exchange
  • NASDAQ – National Association of Securities Dealers Automated Quotations
  • NYSE – New York Stock Exchange

For our research, let's select the NASDAQ stock exchange and display a short list of stock names traded on this exchange. You can display the full list using the command print(акции).

In [ ]:
акции = get_all_symbols("NASDAQ")
display(акции)
# print(promotions)
5622-element Vector{String}:
 "AAAP"
 "AACG"
 "AACI"
 "AACIU"
 "AACIW"
 "AACO"
 "AACOU"
 "AACOW"
 "AACP"
 "AACPR"
 "AACPU"
 "AACPW"
 "AADR"
 ⋮
 "ZSQR"
 "ZSTK"
 "ZTEN"
 "ZTG"
 "ZTOP"
 "ZTRE"
 "ZTWO"
 "ZUMZ"
 "ZURA"
 "ZVRA"
 "ZYBT"
 "ZYME"

Selection of stocks, period and interval

Let's choose a period, an interval, and a company whose stock prices we will investigate.

Period range – this is the period of time counted from the present moment (the moment of publication of this example or the moment of script launch) to the past, during which the available exchange data is loaded. The period is set as an integer and a time unit designation. The end point of the period is determined by the latest available data, usually corresponding to the last completed trading interval. For example: the last 7 days, 3 months, or 2 years.

Interval interval – this is the step with which the data is presented within the selected period: for example, 1 minute, 1 hour or 1 day.

The share price is fixed at the time of closing of each selected interval. For example, with an interval of 1 day, this is the closing price of the trading day, and with an interval of 5 minutes, it is the closing price of each five–minute interval.

The period is set as follows:

  • m – minutes: 30m – 30 minutes;

  • d – days: 7d – 7 days;

  • mo – months: 3mo – 3 months;

  • y – years: 2y – 2 years old;

  • ytd – since the beginning of this year;

  • max – the maximum available period.

The interval can only be selected from a fixed list.: 1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h, 1d, 5d, 1wk, 1mo, 3mo. However, the period is more flexible: for example, the period 7m it means 7 minutes, because for range you can specify any number of minutes (days, months, years).

Let's choose shares of Apple Inc. (AAPL) as the object of research.

In [ ]:
акция = "AAPL"
период = "1y"
интервал = "1d"
println("Stock: ", акция, "\nПериод: ", период, "\nИнтервал: ", интервал)
Акция: AAPL
Период: 1y
Интервал: 1d

Extracting the source data

We will extract the values of stock quotes in accordance with the selected stock, period and interval. The date and time data are presented in accordance with the UTC+0 time zone.

In [ ]:
данные = get_prices(акция, range = период, interval = интервал)
println("Данные извлечены\nКоличество наблюдений: ", length(данные["close"]))
Данные извлечены
Количество наблюдений: 251

Converting data to a DataFrame

Converting the received data to a DataFrame and converting the date and time data to UTC+3. The date and time will now correspond to the Moscow time zone.

In [ ]:
df = DataFrame(данные)
df.timestamp = df.timestamp .+ Hour(3)
display(df)
251×8 DataFrame
226 rows omitted
Rowtickertimestampopenhighlowcloseadjclosevol
StringDateTimeFloat64Float64Float64Float64Float64Float64
1AAPL2025-10-09T16:30:00256.861257.05252.208254.04253.1053.8181e7
2AAPL2025-10-10T16:30:00254.002255.436243.102245.27244.3676.17709e7
3AAPL2025-10-13T16:30:00248.462248.771244.656247.66246.7493.80025e7
4AAPL2025-10-14T16:30:00245.692247.934243.799247.77246.8583.53474e7
5AAPL2025-10-15T16:30:00248.572250.893246.559249.34248.4223.37689e7
6AAPL2025-10-16T16:30:00247.336248.123244.228247.45246.5393.96306e7
7AAPL2025-10-17T16:30:00247.107252.447246.36252.29251.3614.89661e7
8AAPL2025-10-20T16:30:00254.948263.407254.689262.24261.2759.015e7
9AAPL2025-10-21T16:30:00260.916264.314260.866262.77261.8034.6524e7
10AAPL2025-10-22T16:30:00261.683261.883254.49258.45257.4994.48496e7
11AAPL2025-10-23T16:30:00258.983259.661257.06259.58258.6253.26343e7
12AAPL2025-10-24T16:30:00260.229263.158258.226262.82261.8533.81129e7
13AAPL2025-10-27T16:30:00263.905268.13263.676268.81267.8214.4723e7
⋮⋮⋮⋮⋮⋮⋮⋮⋮
240AAPL2026-09-23T16:30:00341.08341.8335.5337.02337.023.16588e7
241AAPL2026-09-24T16:30:00336.72338.91334.3335.92335.922.47331e7
242AAPL2026-09-25T16:30:00336.04341.67334.53341.07341.073.00025e7
243AAPL2026-09-28T16:30:00340.37342.99338.04338.4338.43.28208e7
244AAPL2026-09-29T16:30:00336.97337.09328.7329.4329.43.8478e7
245AAPL2026-09-30T16:30:00330.8339.5330.14333.02333.024.99886e7
246AAPL2026-10-01T16:30:00330.0332.48325.81330.32330.323.63063e7
247AAPL2026-10-02T16:30:00333.26334.54330.61333.69333.693.32786e7
248AAPL2026-10-05T16:30:00332.82336.21331.65332.89332.893.44009e7
249AAPL2026-10-06T16:30:00332.28334.38330.62333.63333.633.0449e7
250AAPL2026-10-07T16:30:00336.96338.67332.78336.67336.673.41479e7
251AAPL2026-10-08T16:30:00336.82341.57335.9340.42340.423.52799e7

The main stock characteristics

Let's display the main stock characteristics of the shares of the company under study.

In [ ]:
println("Stock: ", акция)
println("Beginning of the period: ", first(df.timestamp), " UTC+3")
println("End of the period:  ", last(df.timestamp), " UTC+3")
println("Number of intervals: ", nrow(df))
println("\nНачальная цена: ", round(first(df.close), digits=2), " \$")
println("Final price:  ", round(last(df.close), digits=2), " \$")
println("Minimum:         ", round(minimum(df.close), digits=2), " \$")
println("Maximum:        ", round(maximum(df.close), digits=2), " \$")
Акция: AAPL
Начало периода: 2025-10-09T16:30:00 UTC+3
Конец периода:  2026-10-08T16:30:00 UTC+3
Количество интервалов: 251

Начальная цена: 254.04 $
Конечная цена:  340.42 $
Минимум:         245.27 $
Максимум:        341.07 $

Price dynamics

Let's plot the price dynamics of stocks for the selected period.

In [ ]:
plot(
    df.timestamp,
    df.close,
    xlabel = "Date and time",
    ylabel = "Цена, \$",
    title  = "$акция – динамика цены",
    label  = "Price",
    linewidth = 2.2,
    legend = false,
    hover = ["Дата и время: $(Dates.format(df.timestamp[i], "dd.mm.yyyy HH:MM")) UTC+3 | Цена: $(round(df.close[i], digits=2)) \$" 
    for i in 1:length(df.close)],
    linecolor = RGB(0/255, 198/255, 201/255))
Out[0]:

Trading volume

Let's plot the trading volume chart according to the selected period and interval.

In [ ]:
plot(
    df.timestamp,
    df.vol,
    xlabel = "Date and time",
    ylabel = "Number of shares",
    title = "$акция – объём торгов",
    label = false,
    linewidth = 2.2,
    legend = false,
    hover = ["Дата и время: $(Dates.format(df.timestamp[i], "dd.mm.yyyy HH:MM")) UTC+3 | " * "$(round(Int, df.vol[i])) акций"
    for i in eachindex(df.vol)], linecolor = RGB(1, 117/255, 56/255))
Out[0]:

Profitability

The interval yield is calculated using the formula:

where:

  • – serial number of the observation;
  • – the price relative to which the increase is calculated;
  • – the price is the closing price in the next observation.

Calculate the interval return and put the calculated values in a new column.

In [ ]:
df.Доходность = [missing; diff(df.close) ./ df.close[1:end-1]]
display(df)
251×9 DataFrame
226 rows omitted
RowtickertimestampopenhighlowcloseadjclosevolДоходность
StringDateTimeFloat64Float64Float64Float64Float64Float64Float64?
1AAPL2025-10-09T16:30:00256.861257.05252.208254.04253.1053.8181e7missing
2AAPL2025-10-10T16:30:00254.002255.436243.102245.27244.3676.17709e7-0.0345221
3AAPL2025-10-13T16:30:00248.462248.771244.656247.66246.7493.80025e70.00974436
4AAPL2025-10-14T16:30:00245.692247.934243.799247.77246.8583.53474e70.00044416
5AAPL2025-10-15T16:30:00248.572250.893246.559249.34248.4223.37689e70.00633649
6AAPL2025-10-16T16:30:00247.336248.123244.228247.45246.5393.96306e7-0.00758001
7AAPL2025-10-17T16:30:00247.107252.447246.36252.29251.3614.89661e70.0195595
8AAPL2025-10-20T16:30:00254.948263.407254.689262.24261.2759.015e70.0394387
9AAPL2025-10-21T16:30:00260.916264.314260.866262.77261.8034.6524e70.00202104
10AAPL2025-10-22T16:30:00261.683261.883254.49258.45257.4994.48496e7-0.0164401
11AAPL2025-10-23T16:30:00258.983259.661257.06259.58258.6253.26343e70.00437212
12AAPL2025-10-24T16:30:00260.229263.158258.226262.82261.8533.81129e70.0124818
13AAPL2025-10-27T16:30:00263.905268.13263.676268.81267.8214.4723e70.0227912
⋮⋮⋮⋮⋮⋮⋮⋮⋮⋮
240AAPL2026-09-23T16:30:00341.08341.8335.5337.02337.023.16588e7-0.00803535
241AAPL2026-09-24T16:30:00336.72338.91334.3335.92335.922.47331e7-0.00326383
242AAPL2026-09-25T16:30:00336.04341.67334.53341.07341.073.00025e70.015331
243AAPL2026-09-28T16:30:00340.37342.99338.04338.4338.43.28208e7-0.00782834
244AAPL2026-09-29T16:30:00336.97337.09328.7329.4329.43.8478e7-0.0265957
245AAPL2026-09-30T16:30:00330.8339.5330.14333.02333.024.99886e70.0109897
246AAPL2026-10-01T16:30:00330.0332.48325.81330.32330.323.63063e7-0.00810757
247AAPL2026-10-02T16:30:00333.26334.54330.61333.69333.693.32786e70.0102022
248AAPL2026-10-05T16:30:00332.82336.21331.65332.89332.893.44009e7-0.0023974
249AAPL2026-10-06T16:30:00332.28334.38330.62333.63333.633.0449e70.00222293
250AAPL2026-10-07T16:30:00336.96338.67332.78336.67336.673.41479e70.00911192
251AAPL2026-10-08T16:30:00336.82341.57335.9340.42340.423.52799e70.0111385

Calculate the average return over the standard deviation of return interval.

In [ ]:
доходности = skipmissing(df.Доходность)
println("Average interval profitability: ", round(mean(доходности) * 100, digits=3), "%")
println("Standard deviation: ", round(std(доходности) * 100, digits=3), "%")
Средняя интервальная доходность: 0.129%
Стандартное отклонение: 1.557%

Let's build a yield graph.

In [ ]:
plot(
    df.timestamp,
    df.Доходность .* 100,
    xlabel = "Date and time",
    ylabel = "Profitability, %",
    title = "$акция – интервальные доходности",
    label = false,
    linewidth = 2,
    legend = false,
    hover = ["Дата и время: $(Dates.format(df.timestamp[i], "dd.mm.yyyy HH:MM")) UTC+3 | Доходность: $(100*round(df.Доходность[i], digits=5))%" 
    for i in 1:length(df.Доходность)],
    linecolor = RGB(221/255, 128/255, 201/204) )
hline!([0])
Out[0]:

Let's build a histogram of the distribution of yields for the selected intervals.

In [ ]:
histogram(
    collect(доходности) .* 100,
    bins = 40,
    xlabel = "Profitability, %",
    ylabel = "Quantity",
    title = "$акция – распределение интервальных доходностей",
    label = false,
    fillcolor = RGB(0/255, 198/255, 201/255) )
Out[0]:

Volatility

Let's define the period of volatility by specifying the number of intervals.

In [ ]:
период_волатильности = 20
Out[0]:
20

Rolling volatility is calculated as the standard deviation of returns in a fixed-length sliding window. Let's plot the moving volatility chart for the selected period.

In [ ]:
function скользящая_волатильность(x, окно)
    [std(x[i-окно+1:i])
        for i in окно:length(x)]
end

волатильность = скользящая_волатильность(collect(доходности), период_волатильности)

plot(
    df.timestamp[21:end],
    волатильность .* 100,
    xlabel = "Date and time",
    ylabel = "Volatility, %",
    title = "$акция – волатильность за период",
    label = false,
    linewidth = 2,
    legend = false,
    hover = ["Дата и время: $(Dates.format(df.timestamp[i+период_волатильности], "dd.mm.yyyy HH:MM")) UTC+3 | Волатильность: $(round(100*волатильность[i], digits=4))%" 
    for i in 1:length(волатильность)],
    linecolor = RGB(148/255, 0/255, 211/255) )
Out[0]:

Accumulated profitability

The accumulated return is calculated as the product of the growth multipliers for all intervals:

where:

  • — the sequential number of the interval for which the accumulated profitability is calculated.

  • — interval profitability on - Mmm.

    Let's build a graph of accumulated profitability.

In [ ]:
накопленная_доходность = cumprod(1 .+ collect(доходности))

plot(
    df.timestamp[2:end],
    (накопленная_доходность .- 1) .* 100,
    xlabel = "Date and time",
    ylabel = "Profitability, %",
    title = "$акция – накопленная доходность",
    label = false,
    linewidth = 2,
    legend = false,
    hover = ["Дата и время: $(Dates.format(df.timestamp[i], "dd.mm.yyyy HH:MM")) UTC+3 | Доходность: $(round(100*(накопленная_доходность[i] - 1), digits=3))%" 
    for i in 1:length(накопленная_доходность)],
    linecolor = RGB(245/255, 64/255, 33/255) )


hline!([0], label = "Beginning")
Out[0]:

Drawdown

The drawdown shows how much the current price is below its historical maximum reached so far.:

where:

  • — serial number of the observation;

  • — closing price at time t;

  • — the historical maximum of the price for the period from the beginning of the period to the moment t inclusive: , where — an index that iterates over all values from before .

In [ ]:
цены = df.close
просадка = цены ./ accumulate(max, цены) .- 1
println("Maximum drawdown: ", round(minimum(просадка) * 100, digits=2), "%")
Максимальная просадка: -13.82%

Let's build a drawdown chart for the selected period.

In [ ]:
plot(
    df.timestamp,
    просадка .* 100,
    xlabel = "Date and time",
    ylabel = "Drawdown, %",
    title = "$акция – просадка за период",
    label = false,
    linewidth = 2,
    legend = false,
    hover = ["Дата и время: $(Dates.format(df.timestamp[i], "dd.mm.yyyy HH:MM")) UTC+3 | Просадка: $(100*round(просадка[i], digits=4))%" 
    for i in 1:length(просадка)],
    linecolor = RGB(48/255, 98/255, 107/255) )
hline!([0], label = "")
Out[0]:

Summary characteristics of the shares

Let's display the final characteristics of the shares of the selected company.

In [ ]:
итог = DataFrame(
    Показатель = [
        "Начальная цена",
        "Конечная цена",
        "Минимальная цена",
        "Максимальная цена",
        "Доходность за период",
        "Средняя интервальная доходность",
        "Волатильность доходности",
        "Максимальная просадка"],
    Значение = [
        first(df.close),
        last(df.close),
        minimum(df.close),
        maximum(df.close),
        (last(df.close) / first(df.close) - 1) * 100,
        mean(доходности) * 100,
        std(доходности) * 100,
        minimum(просадка) * 100])

display(итог)
8×2 DataFrame
RowПоказательЗначение
StringFloat64
1Начальная цена254.04
2Конечная цена340.42
3Минимальная цена245.27
4Максимальная цена341.07
5Доходность за период34.0025
6Средняя интервальная доходность0.129267
7Волатильность доходности1.55665
8Максимальная просадка-13.823

Comparison with other stocks

Let's extract stock quotes from AAPL, MSFT, NVDA, AMZN; and plot a comparison graph of their accumulated returns.

In [ ]:
акции = ["AAPL", "MSFT", "NVDA", "AMZN"]
данные_акций = Dict()

for акция in акции
    данные_акций[акция] = get_prices(акция, range = период, interval = интервал)
end

график = plot(xlabel = "Date and time", ylabel = "Accumulated profitability, %",
title = "Stock Comparison", legend = :topleft)

for акция in акции
    данные_текущие = данные_акций[акция]
    даты = данные_текущие["timestamp"]
    цены = данные_текущие["close"]
    доходность = (цены ./ first(цены) .- 1) .* 100
    plot!(график, даты, доходность, label = акция, linewidth = 2, 
    hover = ["Дата и время: $(Dates.format(даты[i], "dd.mm.yyyy HH:MM")) UTC+3 | Доходность: $(round(доходность[i], digits=2))%"
    for i in 1:length(доходность)],)
end

hline!( график, [0], label = "", linestyle = :dash )
display(график)

Conclusion

In this example, a full cycle of quantitative stock analysis is performed based on Yahoo Finance data obtained from the library. YFinance. Graphs of price dynamics, trading volume, interval profitability, its distribution, rolling volatility, accumulated profitability and drawdown are constructed. The final characteristics are calculated, including profitability for the period, average interval profitability, volatility and maximum drawdown. Additionally, a comparison of the accumulated returns between shares of different companies was performed.

The results of the work have practical value in various fields, for example:

  • in investment analysis – to assess the profitability and risk of an instrument and compare it with its analogues;
  • in portfolio management – for risk control and decision-making on bringing the structure of the investment portfolio to a given target asset ratio;
  • In research, the approach is easily transferred to other stocks, periods, and intervals.

The resulting stock quotes can be the source data for a deeper financial analysis. You can select other promotions, periods, and intervals for further research. Thus, the set of methods implemented in this example allows us to draw meaningful conclusions about the behavior of a financial instrument and serves as a convenient starting point for more serious research.